// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
// import userEvent from '@testing-library/user-event';
import { AuthProvider, useAuth } from './AuthContext';

const {
    getSessionMock,
    signOutMock,
    tradingAuthState,
    fetchCurrentUserProfileMock,
    fetchTradeProfilesMock
} = vi.hoisted(() => ({
    getSessionMock: vi.fn(),
    signOutMock: vi.fn(),
    tradingAuthState: {
        user: { id: 'user-1', email: 'sarah@example.com', role: 'admin', name: 'Sarah Algo' } as any,
        isLoading: false,
        logout: vi.fn()
    },
    fetchCurrentUserProfileMock: vi.fn(),
    fetchTradeProfilesMock: vi.fn()
}));

vi.mock('../lib/tradingAuth', () => ({
    TradingAuthProvider: ({ children }: { children: React.ReactNode }) => children,
    useTradingAuth: () => tradingAuthState
}));

vi.mock('../lib/supabaseClient', () => ({
    supabase: {
        auth: {
            getSession: getSessionMock,
            signOut: signOutMock
        }
    }
}));

vi.mock('../lib/profileApi', () => ({
    fetchCurrentUserProfile: fetchCurrentUserProfileMock,
    fetchTradeProfiles: fetchTradeProfilesMock
}));

const Probe = () => {
    const auth = useAuth();
    return (
        <div>
            <div data-testid="loading">{String(auth.loading)}</div>
            <div data-testid="user">{auth.user?.id || 'none'}</div>
            <div data-testid="role">{auth.profile?.role || 'none'}</div>
            <button onClick={() => void auth.refreshProfile()}>Refresh</button>
            <button onClick={() => void auth.signOut()}>Sign Out</button>
        </div>
    );
};

describe('AuthContext DOM behavior', () => {
    beforeEach(() => {
        getSessionMock.mockReset();
        signOutMock.mockReset();
        tradingAuthState.user = { id: 'user-1', email: 'sarah@example.com', role: 'admin', name: 'Sarah Algo' };
        tradingAuthState.isLoading = false;
        tradingAuthState.logout.mockReset();
        fetchCurrentUserProfileMock.mockReset();
        fetchTradeProfilesMock.mockReset();

        signOutMock.mockResolvedValue({ error: null });
        fetchCurrentUserProfileMock.mockResolvedValue({
            user_id: 'user-1',
            first_name: 'Sarah',
            last_name: 'Algo',
            email: 'sarah@example.com',
            role: 'admin',
            trade_enable: true
        });
        fetchTradeProfilesMock.mockResolvedValue([{ id: 'p1' }]);
    });

    it('loads session/profile, ensures default profile, and cleans up subscription', async () => {
        getSessionMock.mockResolvedValue({
            data: { session: { user: { id: 'user-1' } } }
        });
        const dispatchSpy = vi.spyOn(window, 'dispatchEvent');

        render(
            <AuthProvider>
                <Probe />
            </AuthProvider>
        );

        await waitFor(() => {
            expect(screen.getByTestId('loading')).toHaveTextContent('false');
            expect(screen.getByTestId('user')).toHaveTextContent('user-1');
            expect(screen.getByTestId('role')).toHaveTextContent('admin');
        });

        expect(fetchTradeProfilesMock).toHaveBeenCalledWith({ ensureDefault: true });
        expect(dispatchSpy).toHaveBeenCalled();
        expect((dispatchSpy.mock.calls[0][0] as Event).type).toBe('profiles-updated');
    }, 20000);

    it('handles no initial session gracefully', async () => {
        tradingAuthState.user = null;
        getSessionMock.mockResolvedValue({ data: { session: null } });
        render(<AuthProvider><Probe /></AuthProvider>);

        await waitFor(() => {
            expect(screen.getByTestId('loading')).toHaveTextContent('false');
            expect(screen.getByTestId('user')).toHaveTextContent('none');
        });
        expect(fetchCurrentUserProfileMock).not.toHaveBeenCalled();
    });

    it('handles auth state changes with no session', async () => {
        getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } });
        const { rerender } = render(<AuthProvider><Probe /></AuthProvider>);

        await waitFor(() => {
            expect(screen.getByTestId('user')).toHaveTextContent('u1');
        });

        tradingAuthState.user = null;
        rerender(<AuthProvider><Probe /></AuthProvider>);

        await waitFor(() => {
            expect(screen.getByTestId('user')).toHaveTextContent('none');
            expect(screen.getByTestId('role')).toHaveTextContent('none');
        });
    });

    it('logs error when profile fetch fails', async () => {
        const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
        tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' };
        getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } });
        fetchCurrentUserProfileMock.mockRejectedValue({ message: 'Profile Not Found' });

        render(<AuthProvider><Probe /></AuthProvider>);

        await waitFor(() => {
            expect(consoleSpy).toHaveBeenCalledWith('Unexpected error fetching profile:', expect.objectContaining({ message: 'Profile Not Found' }));
        });
        consoleSpy.mockRestore();
    });

    it('handles unexpected errors in fetchProfile', async () => {
        const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
        tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' };
        getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } });
        fetchCurrentUserProfileMock.mockImplementation(() => { throw new Error('Crashed'); });

        render(<AuthProvider><Probe /></AuthProvider>);

        await waitFor(() => {
            expect(consoleSpy).toHaveBeenCalledWith('Unexpected error fetching profile:', expect.any(Error));
        });
        consoleSpy.mockRestore();
    });

    it('handles unexpected errors in ensureDefaultProfile', async () => {
        const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
        tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' };
        getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } });
        fetchTradeProfilesMock.mockImplementation(() => { throw new Error('Limit Error'); });

        render(<AuthProvider><Probe /></AuthProvider>);

        await waitFor(() => {
            expect(consoleSpy).toHaveBeenCalledWith('[Auth] Error ensuring default profile:', expect.any(Error));
        });
        consoleSpy.mockRestore();
    });
});
