import { describe, it, expect, vi, beforeEach } 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 { ForgotPasswordPage } from './ForgotPasswordPage';
import { usePasswordReset } from '../hooks/usePasswordReset';

// Mock dependencies
vi.mock('../hooks/usePasswordReset', () => ({
  usePasswordReset: vi.fn(),
}));

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

describe('ForgotPasswordPage', () => {
  const mockHandleRequestReset = vi.fn();
  const mockUsePasswordReset = {
    handleRequestReset: mockHandleRequestReset,
    loading: false,
    error: null,
    success: false,
  };

  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(usePasswordReset).mockReturnValue(mockUsePasswordReset);
  });

  it('should render forgot password form', () => {
    render(<ForgotPasswordPage />, { wrapper });

    expect(screen.getByText('Mot de passe oublié')).toBeInTheDocument();
    expect(
      screen.getByText(
        'Entrez votre email pour recevoir un lien de réinitialisation',
      ),
    ).toBeInTheDocument();
    expect(screen.getByLabelText('Email')).toBeInTheDocument();
    expect(
      screen.getByRole('button', {
        name: 'Envoyer le lien de réinitialisation',
      }),
    ).toBeInTheDocument();
  });

  it('should display footer link to login', () => {
    render(<ForgotPasswordPage />, { wrapper });

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

  it('should validate required email field', async () => {
    render(<ForgotPasswordPage />, { wrapper });

    const form = screen
      .getByRole('button', { name: 'Envoyer le lien de réinitialisation' })
      .closest('form');
    expect(form).toBeInTheDocument();

    // Try to submit with empty email
    await act(async () => {
      if (form) {
        form.requestSubmit();
      }
    });

    // Wait a bit for validation to run
    await new Promise((resolve) => setTimeout(resolve, 100));

    // Check if validation error appears
    const emailInput = screen.getByLabelText('Email');
    expect(emailInput).toBeInTheDocument();

    // The validation should prevent handleRequestReset from being called
    expect(mockHandleRequestReset).not.toHaveBeenCalled();
  });

  it('should validate email format', async () => {
    const user = userEvent.setup();
    render(<ForgotPasswordPage />, { wrapper });

    const emailInput = screen.getByLabelText('Email');
    await act(async () => {
      await user.type(emailInput, 'invalid-email');
      await user.tab();
    });

    await waitFor(() => {
      expect(screen.getByText('Email invalide')).toBeInTheDocument();
    });
  });

  it('should clear error when user starts typing', async () => {
    const user = userEvent.setup();
    render(<ForgotPasswordPage />, { wrapper });

    const emailInput = screen.getByLabelText('Email');

    // Trigger validation error
    await act(async () => {
      await user.click(emailInput);
      await user.tab();
    });

    await waitFor(() => {
      expect(screen.getByText('Email requis')).toBeInTheDocument();
    });

    // Clear error by typing
    await act(async () => {
      await user.type(emailInput, 'test@example.com');
    });

    await waitFor(() => {
      expect(screen.queryByText('Email requis')).not.toBeInTheDocument();
    });
  });

  it('should call handleRequestReset with email on valid submission', async () => {
    const user = userEvent.setup();
    mockHandleRequestReset.mockResolvedValue(undefined);
    render(<ForgotPasswordPage />, { wrapper });

    await act(async () => {
      await user.type(screen.getByLabelText('Email'), 'test@example.com');
    });

    const form = screen
      .getByRole('button', { name: 'Envoyer le lien de réinitialisation' })
      .closest('form');
    await act(async () => {
      if (form) {
        await userEvent.click(
          screen.getByRole('button', {
            name: 'Envoyer le lien de réinitialisation',
          }),
        );
      }
    });

    await waitFor(() => {
      expect(mockHandleRequestReset).toHaveBeenCalledWith({
        email: 'test@example.com',
      });
    });
  });

  it('should display error message when request fails', () => {
    const error = new Error('Email not found');
    vi.mocked(usePasswordReset).mockReturnValue({
      ...mockUsePasswordReset,
      error,
    });

    render(<ForgotPasswordPage />, { wrapper });

    expect(screen.getByText('Email not found')).toBeInTheDocument();
  });

  it('should show loading state on button', () => {
    vi.mocked(usePasswordReset).mockReturnValue({
      ...mockUsePasswordReset,
      loading: true,
    });

    render(<ForgotPasswordPage />, { wrapper });

    const submitButton = screen.getByRole('button', { name: 'Chargement...' });
    expect(submitButton).toBeDisabled();
    expect(screen.getByText('Chargement...')).toBeInTheDocument();
  });

  it('should display success message after successful request', () => {
    vi.mocked(usePasswordReset).mockReturnValue({
      ...mockUsePasswordReset,
      success: true,
    });

    render(<ForgotPasswordPage />, { wrapper });

    expect(screen.getByText('Email envoyé !')).toBeInTheDocument();
    expect(
      screen.getByText(/Un lien de réinitialisation a été envoyé à/),
    ).toBeInTheDocument();
    expect(
      screen.getByText(/Veuillez vérifier votre boîte mail/),
    ).toBeInTheDocument();
  });

  it('should display link to login in success message', () => {
    vi.mocked(usePasswordReset).mockReturnValue({
      ...mockUsePasswordReset,
      success: true,
    });

    render(<ForgotPasswordPage />, { wrapper });

    // There are two "Retour à la connexion" links - one in footer, one in success message
    const loginLinks = screen.getAllByText('Retour à la connexion');
    expect(loginLinks.length).toBeGreaterThan(0);
    // Check that at least one link points to /login
    const loginLink = loginLinks.find(
      (link) => link.closest('a')?.getAttribute('href') === '/login',
    );
    expect(loginLink).toBeInTheDocument();
  });
});
