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 { verifyEmail, resendVerificationEmail } from '../services/authService';

// Mock dependencies
vi.mock('../services/authService', () => ({
  verifyEmail: vi.fn(),
  resendVerificationEmail: 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(verifyEmail).mockResolvedValue(undefined);

    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(() => {
      expect(screen.getByText("Vérification de l'email")).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(verifyEmail).mockResolvedValue(undefined);

    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(verifyEmail).toHaveBeenCalledWith('abc123');
      },
      { timeout: 3000 },
    );
  });

  it('should display success message after successful verification', async () => {
    vi.mocked(verifyEmail).mockResolvedValue(undefined);

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

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

    // Wait for verifyEmail to be called
    await waitFor(
      () => {
        expect(verifyEmail).toHaveBeenCalled();
      },
      { timeout: 2000 },
    );

    // 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(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.useFakeTimers();
    vi.mocked(verifyEmail).mockResolvedValue(undefined);

    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();
    });

    // Fast-forward the timer (3 seconds)
    await act(async () => {
      vi.advanceTimersByTime(3000);
    });

    await waitFor(() => {
      expect(mockNavigate).toHaveBeenCalledWith('/login', { replace: true });
    });

    vi.useRealTimers();
  });

  it('should display retry button when verification fails', async () => {
    const error = { message: 'Verification failed' };
    vi.mocked(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(verifyEmail)
      .mockRejectedValueOnce(error)
      .mockResolvedValueOnce(undefined);

    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(verifyEmail).toHaveBeenCalledTimes(2);
    });
  });

  it('should display resend email button', async () => {
    const error = { message: 'Verification failed' };
    vi.mocked(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 resendVerificationEmail when resend button is clicked', async () => {
    const user = userEvent.setup();
    const error = { message: 'Verification failed' };
    vi.mocked(verifyEmail).mockRejectedValue(error);
    vi.mocked(resendVerificationEmail).mockResolvedValue(undefined);

    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 resendVerificationEmail
    await waitFor(
      () => {
        expect(resendVerificationEmail).toHaveBeenCalledWith(
          'test@example.com',
        );
      },
      { timeout: 3000 },
    );
  });

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

    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 resendVerificationEmail was called
    await waitFor(
      () => {
        expect(resendVerificationEmail).toHaveBeenCalledWith(
          '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(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);
    });

    // The component should handle the missing email gracefully
    // resendVerificationEmail should NOT be called since email is missing
    // Wait a bit for async operations
    await new Promise((resolve) => setTimeout(resolve, 200));
    expect(resendVerificationEmail).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');
  });
});
