// @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';
import { tableNameProfiles, tableNameUsers } from '../lib/const';

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

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

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

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();
        fromMock.mockReset();
        usersSingleMock.mockReset();
        profilesLimitMock.mockReset();
        profilesInsertMock.mockReset();
        unsubscribeMock.mockReset();
        tradingAuthState.user = { id: 'user-1', email: 'sarah@example.com', role: 'admin', name: 'Sarah Algo' };
        tradingAuthState.isLoading = false;
        tradingAuthState.logout.mockReset();

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

        fromMock.mockImplementation((table: string) => {
            if (table === tableNameUsers) {
                const usersBuilder: any = {
                    select: vi.fn(() => usersBuilder),
                    eq: vi.fn(() => usersBuilder),
                    single: usersSingleMock
                };
                return usersBuilder;
            }
            if (table === tableNameProfiles) {
                const profilesBuilder: any = {
                    select: vi.fn(() => profilesBuilder),
                    eq: vi.fn(() => profilesBuilder),
                    limit: profilesLimitMock,
                    insert: profilesInsertMock
                };
                return profilesBuilder;
            }
            return {
                select: vi.fn(),
                eq: vi.fn(),
                single: vi.fn(),
                limit: vi.fn(),
                insert: vi.fn()
            };
        });
    });

    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(profilesInsertMock).toHaveBeenCalledTimes(1);
        expect(profilesInsertMock.mock.calls[0][0]).toEqual([
            expect.objectContaining({ user_id: 'user-1', name: 'My First Strategy' })
        ]);
        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(usersSingleMock).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' } } } });
        usersSingleMock.mockResolvedValue({ data: null, error: { message: 'Profile Not Found' } });

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

        await waitFor(() => {
            expect(consoleSpy).toHaveBeenCalledWith('Error fetching user 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' } } } });
        usersSingleMock.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' } } } });
        profilesLimitMock.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();
    });
});
