import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { AuthProvider, useAuth } from './AuthContext';
import { useRouter } from 'next/navigation';
import { useCurrentSession } from '@/hooks/auth/useCurrentSession';
import { useLogout } from '@/hooks/auth/useLogout';

// Mock Next.js navigation
vi.mock('next/navigation', () => ({
  useRouter: vi.fn(),
}));

// Mock auth hooks
vi.mock('@/hooks/auth/useCurrentSession', () => ({
  useCurrentSession: vi.fn(),
}));

vi.mock('@/hooks/auth/useLogout', () => ({
  useLogout: vi.fn(),
}));

// Test component that uses the auth context
const TestConsumer = () => {
  const auth = useAuth();
  return (
    <div data-testid="auth-consumer">
      <div data-testid="session">{auth.session ? 'has-session' : 'no-session'}</div>
      <div data-testid="loading">{auth.loading ? 'loading' : 'not-loading'}</div>
      <button onClick={() => auth.login()}>Login</button>
      <button onClick={() => auth.logout()}>Logout</button>
      <button onClick={() => auth.refreshSession()}>Refresh</button>
    </div>
  );
};

describe('AuthContext', () => {
  let mockRouter: any;
  let mockRefetch: any;
  let mockMutateAsync: any;

  beforeEach(() => {
    vi.clearAllMocks();
    
    mockRouter = {
      push: vi.fn(),
      refresh: vi.fn(),
    };
    
    mockRefetch = vi.fn();
    mockMutateAsync = vi.fn().mockResolvedValue(undefined);
    
    (useRouter as any).mockReturnValue(mockRouter);
    (useCurrentSession as any).mockReturnValue({
      data: null,
      isLoading: false,
      refetch: mockRefetch,
    });
    (useLogout as any).mockReturnValue({
      mutateAsync: mockMutateAsync,
    });
  });

  describe('AuthProvider', () => {
    it('should provide default context values', () => {
      render(
        <AuthProvider>
          <TestConsumer />
        </AuthProvider>
      );

      expect(screen.getByTestId('session')).toHaveTextContent('no-session');
      expect(screen.getByTestId('loading')).toHaveTextContent('not-loading');
    });

    it('should provide loading state', () => {
      (useCurrentSession as any).mockReturnValue({
        data: null,
        isLoading: true,
        refetch: mockRefetch,
      });

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

      expect(screen.getByTestId('loading')).toHaveTextContent('loading');
    });

    it('should provide session data', () => {
      const mockSession = { user: { id: '123', name: 'Test User' } };
      (useCurrentSession as any).mockReturnValue({
        data: mockSession,
        isLoading: false,
        refetch: mockRefetch,
      });

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

      expect(screen.getByTestId('session')).toHaveTextContent('has-session');
    });

    it('should provide initial session data', () => {
      const mockSession = { user: { id: '123', name: 'Test User' } };

      render(
        <AuthProvider initialSession={mockSession}>
          <TestConsumer />
        </AuthProvider>
      );

      expect(screen.getByTestId('session')).toHaveTextContent('has-session');
    });
  });

  describe('useAuth hook', () => {
    it('should throw error when used outside AuthProvider', () => {
      // Suppress console.error for this test
      const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});

      expect(() => {
        render(<TestConsumer />);
      }).toThrow('useAuth must be used within an AuthProvider');

      consoleSpy.mockRestore();
    });

    it('should provide login function', async () => {
      render(
        <AuthProvider>
          <TestConsumer />
        </AuthProvider>
      );

      const loginButton = screen.getByText('Login');
      loginButton.click();

      await waitFor(() => {
        expect(mockRouter.push).toHaveBeenCalledWith('/auth/login');
      });
    });

    it('should provide login function with returnTo parameter', async () => {
      const TestConsumerWithReturnTo = () => {
        const auth = useAuth();
        return (
          <button onClick={() => auth.login('/dashboard')}>
            Login with Return
          </button>
        );
      };

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

      const loginButton = screen.getByText('Login with Return');
      loginButton.click();

      await waitFor(() => {
        expect(mockRouter.push).toHaveBeenCalledWith('/auth/login?returnTo=%2Fdashboard');
      });
    });

    it('should provide logout function', async () => {
      render(
        <AuthProvider>
          <TestConsumer />
        </AuthProvider>
      );

      const logoutButton = screen.getByText('Logout');
      logoutButton.click();

      await waitFor(() => {
        expect(mockMutateAsync).toHaveBeenCalled();
        expect(mockRouter.push).toHaveBeenCalledWith('/');
        expect(mockRouter.refresh).toHaveBeenCalled();
      });
    });

    it('should handle logout failure gracefully', async () => {
      const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
      mockMutateAsync.mockRejectedValue(new Error('Logout failed'));

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

      const logoutButton = screen.getByText('Logout');
      logoutButton.click();

      await waitFor(() => {
        expect(mockMutateAsync).toHaveBeenCalled();
        expect(mockRouter.push).toHaveBeenCalledWith('/');
      });

      consoleSpy.mockRestore();
    });

    it('should provide refreshSession function', async () => {
      render(
        <AuthProvider>
          <TestConsumer />
        </AuthProvider>
      );

      const refreshButton = screen.getByText('Refresh');
      refreshButton.click();

      await waitFor(() => {
        expect(mockRefetch).toHaveBeenCalled();
      });
    });
  });

  describe('edge cases', () => {
    it('should handle null initial session', () => {
      render(
        <AuthProvider initialSession={null}>
          <TestConsumer />
        </AuthProvider>
      );

      expect(screen.getByTestId('session')).toHaveTextContent('no-session');
    });

    it('should handle undefined initial session', () => {
      render(
        <AuthProvider initialSession={undefined}>
          <TestConsumer />
        </AuthProvider>
      );

      expect(screen.getByTestId('session')).toHaveTextContent('no-session');
    });

    it('should handle multiple consumers', () => {
      render(
        <AuthProvider>
          <TestConsumer />
          <TestConsumer />
        </AuthProvider>
      );

      const consumers = screen.getAllByTestId('auth-consumer');
      expect(consumers).toHaveLength(2);
    });
  });
});
