import { render, act, waitFor, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { BrowserRouter, MemoryRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ProtectedRoute } from './ProtectedRoute';

// Mock useAuth hook
const mockIsAuthenticated = vi.fn(() => false);
const mockUser = vi.fn(() => null);

vi.mock('@/features/auth/hooks/useAuth', () => ({
  useAuth: () => ({
    isAuthenticated: mockIsAuthenticated(),
    user: mockUser(),
  }),
}));

vi.mock('@/features/auth/store/authStore', () => ({
  useAuthStore: () => ({
    isAuthenticated: mockIsAuthenticated(),
    isLoading: false,
  }),
}));

vi.mock('@/services/tokenStorage', () => ({
  TokenStorage: {
    getAccessToken: () => mockIsAuthenticated() ? 'mock-token' : null,
  },
}));

const createTestQueryClient = () =>
  new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

const TestWrapper = ({ children }: { children: React.ReactNode }) => {
  const queryClient = createTestQueryClient();
  return (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>{children}</BrowserRouter>
    </QueryClientProvider>
  );
};

describe('ProtectedRoute Component', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('renders children when user is authenticated', async () => {
    mockIsAuthenticated.mockReturnValue(true);
    mockUser.mockReturnValue({ id: '1', name: 'Test' });

    const { getByText } = render(
      <TestWrapper>
        <ProtectedRoute>
          <div>Protected Content</div>
        </ProtectedRoute>
      </TestWrapper>,
    );

    // Wait for the checking timer (200ms)
    await act(async () => {
      vi.advanceTimersByTime(300);
    });

    expect(getByText('Protected Content')).toBeInTheDocument();
  });

  it('redirects to login when user is not authenticated', () => {
    mockIsAuthenticated.mockReturnValue(false);
    mockUser.mockReturnValue(null);

    render(
      <MemoryRouter initialEntries={['/dashboard']}>
        <QueryClientProvider client={createTestQueryClient()}>
          <ProtectedRoute>
            <div>Protected Content</div>
          </ProtectedRoute>
        </QueryClientProvider>
      </MemoryRouter>,
    );

    // Wait for the checking timer
    vi.advanceTimersByTime(300);

    // Le composant Navigate devrait rediriger vers /login
    // On vérifie que le contenu protégé n'est pas visible
    const protectedContent = document.querySelector('div');
    expect(protectedContent?.textContent).not.toBe('Protected Content');
  });

  it('uses replace navigation when redirecting', () => {
    mockIsAuthenticated.mockReturnValue(false);
    mockUser.mockReturnValue(null);

    const { container } = render(
      <MemoryRouter initialEntries={['/dashboard']}>
        <QueryClientProvider client={createTestQueryClient()}>
          <ProtectedRoute>
            <div>Protected Content</div>
          </ProtectedRoute>
        </QueryClientProvider>
      </MemoryRouter>,
    );

    vi.advanceTimersByTime(300);

    // Le composant Navigate devrait être présent avec replace
    expect(container).toBeInTheDocument();
  });

  it('renders multiple children correctly when authenticated', async () => {
    mockIsAuthenticated.mockReturnValue(true);
    mockUser.mockReturnValue({ id: '1', name: 'Test' });

    render(
      <TestWrapper>
        <ProtectedRoute>
          <div>Child 1</div>
          <div>Child 2</div>
        </ProtectedRoute>
      </TestWrapper>,
    );

    await act(async () => {
      vi.advanceTimersByTime(300);
    });

    // waitFor needs real timers to poll; switch to real timers then assert
    vi.useRealTimers();
    await screen.findByText('Child 1', { timeout: 500 });
    expect(screen.getByText('Child 2')).toBeInTheDocument();
    vi.useFakeTimers();
  });

  it('handles empty children gracefully', () => {
    mockIsAuthenticated.mockReturnValue(true);
    mockUser.mockReturnValue({ id: '1', name: 'Test' });

    const { container } = render(
      <TestWrapper>
        <ProtectedRoute>{null}</ProtectedRoute>
      </TestWrapper>,
    );

    vi.advanceTimersByTime(300);

    expect(container).toBeInTheDocument();
  });
});
