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 { ResetPasswordPage } from './ResetPasswordPage';
import { usePasswordReset } from '../hooks/usePasswordReset';

// Mock dependencies
vi.mock('../hooks/usePasswordReset', () => ({
  usePasswordReset: 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('ResetPasswordPage', () => {
  const mockHandleReset = vi.fn();
  const mockUsePasswordReset = {
    handleReset: mockHandleReset,
    loading: false,
    error: null,
    success: false,
  };

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

  it('should render reset password form when token is present', async () => {
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=test-token']}>
        {children}
      </MemoryRouter>
    );

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

    // Wait for the token to be extracted and form to render
    await waitFor(() => {
      expect(screen.getByLabelText('Nouveau mot de passe')).toBeInTheDocument();
    });

    expect(
      screen.getByText('Entrez votre nouveau mot de passe'),
    ).toBeInTheDocument();
    expect(
      screen.getByLabelText('Confirmer le mot de passe'),
    ).toBeInTheDocument();
    expect(
      screen.getByRole('button', { name: 'Réinitialiser le mot de passe' }),
    ).toBeInTheDocument();
  });

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

    expect(
      screen.getByText('Lien de réinitialisation invalide'),
    ).toBeInTheDocument();
    // Check for the subtitle which is unique
    expect(
      screen.getByText('Le lien de réinitialisation est invalide ou a expiré'),
    ).toBeInTheDocument();
  });

  it('should extract token from URL', async () => {
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=abc123']}>
        {children}
      </MemoryRouter>
    );

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

    // Wait for the token to be extracted and form to render
    // The useEffect will extract the token and update state
    await waitFor(
      () => {
        // Check for the form inputs instead of the title (which appears twice)
        expect(
          screen.getByLabelText('Nouveau mot de passe'),
        ).toBeInTheDocument();
      },
      { timeout: 3000 },
    );

    // Verify the form is rendered (not the error message)
    expect(
      screen.queryByText('Lien de réinitialisation invalide'),
    ).not.toBeInTheDocument();
    expect(
      screen.getByText('Entrez votre nouveau mot de passe'),
    ).toBeInTheDocument();
  });

  it('should validate required password field', async () => {
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=test-token']}>
        {children}
      </MemoryRouter>
    );

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

    const form = screen
      .getByRole('button', { name: 'Réinitialiser le mot de passe' })
      .closest('form');
    expect(form).toBeInTheDocument();

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

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

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

  it('should validate password minimum length', async () => {
    const user = userEvent.setup();
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=test-token']}>
        {children}
      </MemoryRouter>
    );

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

    const passwordInput = screen.getByLabelText('Nouveau mot de passe');
    await act(async () => {
      await user.type(passwordInput, 'short');
      await user.tab();
    });

    await waitFor(() => {
      expect(
        screen.getByText('Le mot de passe doit contenir au moins 8 caractères'),
      ).toBeInTheDocument();
    });
  });

  it('should validate password confirmation match', async () => {
    const user = userEvent.setup();
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=test-token']}>
        {children}
      </MemoryRouter>
    );

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

    const passwordInput = screen.getByLabelText('Nouveau mot de passe');
    const confirmPasswordInput = screen.getByLabelText(
      'Confirmer le mot de passe',
    );

    await act(async () => {
      await user.type(passwordInput, 'password123');
      await user.type(confirmPasswordInput, 'password456');
      await user.tab();
    });

    await waitFor(() => {
      expect(
        screen.getByText('Les mots de passe ne correspondent pas'),
      ).toBeInTheDocument();
    });
  });

  it('should clear error when user starts typing', async () => {
    const user = userEvent.setup();
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=test-token']}>
        {children}
      </MemoryRouter>
    );

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

    const passwordInput = screen.getByLabelText('Nouveau mot de passe');

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

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

    // Clear error by typing
    await act(async () => {
      await user.type(passwordInput, 'password123');
    });

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

  it('should call handleReset with form data on valid submission', async () => {
    const user = userEvent.setup();
    mockHandleReset.mockResolvedValue(undefined);
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=test-token-123']}>
        {children}
      </MemoryRouter>
    );

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

    await act(async () => {
      await user.type(
        screen.getByLabelText('Nouveau mot de passe'),
        'newpassword123',
      );
      await user.type(
        screen.getByLabelText('Confirmer le mot de passe'),
        'newpassword123',
      );
    });

    const form = screen
      .getByRole('button', { name: 'Réinitialiser le mot de passe' })
      .closest('form');
    await act(async () => {
      if (form) {
        await userEvent.click(
          screen.getByRole('button', { name: 'Réinitialiser le mot de passe' }),
        );
      }
    });

    await waitFor(() => {
      expect(mockHandleReset).toHaveBeenCalledWith({
        token: 'test-token-123',
        password: 'newpassword123',
        confirmPassword: 'newpassword123',
      });
    });
  });

  it('should display error message when reset fails', () => {
    const error = new Error('Token invalide ou expiré');
    vi.mocked(usePasswordReset).mockReturnValue({
      ...mockUsePasswordReset,
      error,
    });

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

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

    expect(screen.getByText('Token invalide ou expiré')).toBeInTheDocument();
  });

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

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

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

    // Le texte "Chargement..." est dans un span avec aria-hidden, donc on cherche le bouton par son aria-busy
    const submitButton = screen.getByRole('button', {
      name: 'Chargement en cours',
    });
    expect(submitButton).toBeDisabled();
    expect(submitButton).toHaveAttribute('aria-busy', 'true');
    expect(screen.getByText('Chargement...')).toBeInTheDocument();
  });

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

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

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

    expect(screen.getByText('Mot de passe réinitialisé')).toBeInTheDocument();
    expect(
      screen.getByText(/Votre mot de passe a été modifié avec succès/),
    ).toBeInTheDocument();
    expect(
      screen.getByText(/Vous allez être redirigé vers la page de connexion/),
    ).toBeInTheDocument();
  });

  it('should redirect to login after successful reset', async () => {
    vi.mocked(usePasswordReset).mockReturnValue({
      ...mockUsePasswordReset,
      success: true,
    });

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

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

    // Wait for success message to appear
    await waitFor(() => {
      expect(screen.getByText('Mot de passe réinitialisé')).toBeInTheDocument();
    });

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

  it('should display password strength indicator', () => {
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=test-token']}>
        {children}
      </MemoryRouter>
    );

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

    // PasswordStrengthIndicator should be present
    const passwordInput = screen.getByLabelText('Nouveau mot de passe');
    expect(passwordInput).toBeInTheDocument();
  });

  it('should display footer links', () => {
    const wrapperWithToken = ({ children }: { children: React.ReactNode }) => (
      <MemoryRouter initialEntries={['/reset-password?token=test-token']}>
        {children}
      </MemoryRouter>
    );

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

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