import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import { VerifyEmailPage } from './VerifyEmailPage';
import { authApi } from '@/services/api/auth';

// Mock authApi
vi.mock('@/services/api/auth', () => ({
  authApi: {
    verifyEmail: vi.fn(),
    resendVerification: vi.fn(),
  },
}));

const mockNavigate = vi.fn();
vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    useNavigate: () => mockNavigate,
  };
});

const wrapper = ({ children }: { children: React.ReactNode }) => (
  <MemoryRouter>{children}</MemoryRouter>
);

describe('VerifyEmailPage', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockNavigate.mockClear();
    localStorage.clear();
  });

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

  it('should render verifying state when token is present', async () => {
    vi.mocked(authApi.verifyEmail).mockResolvedValue({
      message: 'Email verified',
    });

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Should show verifying state initially
    await waitFor(() => {
      const heading = document.querySelector('h1');
      expect(heading).toBeInTheDocument();
    });
  });

  it('should display error message when token is missing', () => {
    render(<VerifyEmailPage />, { wrapper });

    expect(screen.getByText("Vérification de l'email")).toBeInTheDocument();
    expect(
      screen.getByText('Lien de vérification invalide ou manquant'),
    ).toBeInTheDocument();
  });

  it('should extract token from URL and verify email', async () => {
    vi.mocked(authApi.verifyEmail).mockResolvedValue({
      message: 'Email verified',
    });

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=abc123']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for verification to complete
    await waitFor(
      () => {
        expect(authApi.verifyEmail).toHaveBeenCalledWith({ token: 'abc123' });
      },
      { timeout: 3000 },
    );
  });

  it('should display success message after successful verification', async () => {
    vi.mocked(authApi.verifyEmail).mockResolvedValue({
      message: 'Email verified',
    });

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for success message
    await waitFor(
      () => {
        expect(screen.getByText('Email vérifié')).toBeInTheDocument();
      },
      { timeout: 2000 },
    );
  });

  it('should display error message when verification fails', async () => {
    const error = { message: 'Token invalide ou expiré' };
    vi.mocked(authApi.verifyEmail).mockRejectedValue(error);

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=invalid-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for error message
    await waitFor(
      () => {
        expect(
          screen.getByText('Token invalide ou expiré'),
        ).toBeInTheDocument();
      },
      { timeout: 3000 },
    );
  });

  it('should redirect to login after successful verification', async () => {
    vi.mocked(authApi.verifyEmail).mockResolvedValue({
      message: 'Email verified',
    });

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for success
    await waitFor(() => {
      expect(screen.getByText('Email vérifié')).toBeInTheDocument();
    });

    // Wait for the redirect timer (3 seconds)
    await waitFor(
      () => {
        expect(mockNavigate).toHaveBeenCalledWith('/login', { replace: true });
      },
      { timeout: 4000 },
    );
  });

  it('should display retry button when verification fails', async () => {
    const error = { message: 'Verification failed' };
    vi.mocked(authApi.verifyEmail).mockRejectedValue(error);

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for error state
    await waitFor(
      () => {
        expect(screen.getByText('Réessayer')).toBeInTheDocument();
      },
      { timeout: 3000 },
    );
  });

  it('should call verifyEmail when retry button is clicked', async () => {
    const user = userEvent.setup();
    const error = { message: 'Verification failed' };
    vi.mocked(authApi.verifyEmail)
      .mockRejectedValueOnce(error)
      .mockResolvedValueOnce({ message: 'Email verified' });

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for error state
    await waitFor(() => {
      expect(screen.getByText('Réessayer')).toBeInTheDocument();
    });

    // Click retry button
    const retryButton = screen.getByText('Réessayer');
    await act(async () => {
      await user.click(retryButton);
    });

    // Should call verifyEmail again
    await waitFor(() => {
      expect(authApi.verifyEmail).toHaveBeenCalledTimes(2);
    });
  });

  it('should display resend email button', async () => {
    const error = { message: 'Verification failed' };
    vi.mocked(authApi.verifyEmail).mockRejectedValue(error);

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for error state
    await waitFor(
      () => {
        expect(
          screen.getByText(/Renvoyer l'email de vérification/),
        ).toBeInTheDocument();
      },
      { timeout: 3000 },
    );
  });

  it('should call resendVerification when resend button is clicked', async () => {
    const user = userEvent.setup();
    const error = { message: 'Verification failed' };
    vi.mocked(authApi.verifyEmail).mockRejectedValue(error);
    vi.mocked(authApi.resendVerification).mockResolvedValue({
      message: 'Email sent',
    });

    localStorage.setItem('pendingVerificationEmail', 'test@example.com');

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for error state
    await waitFor(
      () => {
        expect(
          screen.getByText(/Renvoyer l'email de vérification/),
        ).toBeInTheDocument();
      },
      { timeout: 3000 },
    );

    // Click resend button
    const resendButton = screen.getByText(/Renvoyer l'email de vérification/);
    await act(async () => {
      await user.click(resendButton);
    });

    // Should call resendVerification
    await waitFor(
      () => {
        expect(authApi.resendVerification).toHaveBeenCalledWith({
          email: 'test@example.com',
        });
      },
      { timeout: 3000 },
    );
  });

  it('should set cooldown after resending email', async () => {
    const user = userEvent.setup();
    const error = { message: 'Verification failed' };
    vi.mocked(authApi.verifyEmail).mockRejectedValue(error);
    vi.mocked(authApi.resendVerification).mockResolvedValue({
      message: 'Email sent',
    });

    localStorage.setItem('pendingVerificationEmail', 'test@example.com');

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for error state
    await waitFor(
      () => {
        expect(
          screen.getByText(/Renvoyer l'email de vérification/),
        ).toBeInTheDocument();
      },
      { timeout: 2000 },
    );

    // Click resend button
    const resendButton = screen.getByText(/Renvoyer l'email de vérification/);
    await act(async () => {
      await user.click(resendButton);
    });

    // Verify that resendVerification was called
    await waitFor(
      () => {
        expect(authApi.resendVerification).toHaveBeenCalledWith({
          email: 'test@example.com',
        });
      },
      { timeout: 2000 },
    );
  });

  it('should handle resend when email is not found', async () => {
    const user = userEvent.setup();
    const error = { message: 'Verification failed' };
    vi.mocked(authApi.verifyEmail).mockRejectedValue(error);

    // No email in localStorage
    localStorage.clear();

    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/verify-email?token=test-token']}>
        {children}
      </MemoryRouter>
    );

    render(<VerifyEmailPage />, { wrapper: wrapperWithToken });

    // Wait for error state
    await waitFor(
      () => {
        expect(
          screen.getByText(/Renvoyer l'email de vérification/),
        ).toBeInTheDocument();
      },
      { timeout: 2000 },
    );

    // Click resend button
    const resendButton = screen.getByText(/Renvoyer l'email de vérification/);
    await act(async () => {
      await user.click(resendButton);
    });

    // resendVerification should NOT be called since email is missing
    await new Promise((resolve) => setTimeout(resolve, 200));
    expect(authApi.resendVerification).not.toHaveBeenCalled();
  });

  it('should display footer links', () => {
    render(<VerifyEmailPage />, { wrapper });

    const loginLink = screen.getByText('Retour à la connexion');
    expect(loginLink).toBeInTheDocument();
    expect(loginLink.closest('a')).toHaveAttribute('href', '/login');
  });
});
