/**
 * ResetPassword Page Tests
 *
 * Tests for the password reset page that validates reset tokens
 * and allows users to set a new password.
 */

import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi, type MockedFunction } from 'vitest';
import { MemoryRouter, Routes, Route } from 'react-router';
import ResetPassword from '../../pages/ResetPassword';
import { useAuth } from '../../contexts/AuthContext';
import { authAPI } from '../../services/api';

// Mock dependencies
vi.mock('../../contexts/AuthContext');
vi.mock('../../services/api', () => ({
  authAPI: {
    verifyResetToken: vi.fn(),
    resetPassword: vi.fn(),
  },
}));

// Mock child components
vi.mock('../../components/PasswordInput', () => ({
  default: ({ id, label, value, onChange, required }: any) => (
    <div>
      <label htmlFor={id}>{label}</label>
      <input
        id={id}
        type="password"
        value={value}
        onChange={onChange}
        required={required}
        data-testid={id}
      />
    </div>
  ),
}));

vi.mock('../../components/PasswordStrengthMeter', () => ({
  default: ({ password }: { password: string }) => (
    <div data-testid="password-strength-meter">
      Strength: {password.length >= 8 ? 'Strong' : 'Weak'}
    </div>
  ),
}));

const mockNavigate = vi.fn();
const mockOpenAuthModal = vi.fn();

vi.mock('react-router', async (importOriginal) => {
  const actual = await importOriginal<typeof import('react-router')>();
  return {
    ...actual,
    useNavigate: () => mockNavigate,
  };
});

const mockedUseAuth = useAuth as MockedFunction<typeof useAuth>;
const mockedVerifyResetToken = authAPI.verifyResetToken as MockedFunction<typeof authAPI.verifyResetToken>;
const mockedResetPassword = authAPI.resetPassword as MockedFunction<typeof authAPI.resetPassword>;

// Helper to render ResetPassword with route params
const renderResetPassword = (searchParams: string = '') => {
  mockedUseAuth.mockReturnValue({
    user: null,
    loading: false,
    showAuthModal: false,
    authModalMode: 'login',
    login: vi.fn(),
    register: vi.fn(),
    googleLogin: vi.fn(),
    logout: vi.fn(),
    checkAuth: vi.fn(),
    updateUser: vi.fn(),
    openAuthModal: mockOpenAuthModal,
    closeAuthModal: vi.fn(),
  });

  return render(
    <MemoryRouter initialEntries={[`/reset-password${searchParams}`]}>
      <Routes>
        <Route path="/reset-password" element={<ResetPassword />} />
      </Routes>
    </MemoryRouter>
  );
};

describe('ResetPassword', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedVerifyResetToken.mockResolvedValue({ data: { valid: true } });
    mockedResetPassword.mockResolvedValue({ data: { success: true } });
  });

  describe('Initial Token Validation', () => {
    it('shows validating state while checking token', () => {
      // Make the token verification hang
      mockedVerifyResetToken.mockImplementation(() => new Promise(() => {}));

      renderResetPassword('?token=test-token');

      expect(screen.getByRole('status')).toBeInTheDocument();
      expect(screen.getByText('Verifying Reset Link...')).toBeInTheDocument();
    });

    it('validates token on mount', async () => {
      renderResetPassword('?token=valid-token-123');

      await waitFor(() => {
        expect(mockedVerifyResetToken).toHaveBeenCalledWith('valid-token-123');
      });
    });

    it('shows password form for valid token', async () => {
      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
        expect(screen.getByText('Enter your new password below.')).toBeInTheDocument();
      });

      expect(screen.getByTestId('newPassword')).toBeInTheDocument();
      expect(screen.getByTestId('confirmPassword')).toBeInTheDocument();
    });
  });

  describe('Missing Token', () => {
    it('displays error for missing token parameter', async () => {
      renderResetPassword();

      await waitFor(() => {
        expect(screen.getByText('Invalid Reset Link')).toBeInTheDocument();
        expect(screen.getByText('No reset token provided.')).toBeInTheDocument();
      });
    });
  });

  describe('Invalid Token Errors', () => {
    it('displays error for expired token', async () => {
      mockedVerifyResetToken.mockRejectedValue({
        response: { data: { code: 'TOKEN_EXPIRED' } },
      });

      renderResetPassword('?token=expired-token');

      await waitFor(() => {
        expect(screen.getByText('Invalid Reset Link')).toBeInTheDocument();
        expect(screen.getByText(/password reset link has expired/i)).toBeInTheDocument();
      });
    });

    it('displays error for invalid token', async () => {
      mockedVerifyResetToken.mockRejectedValue({
        response: { data: { code: 'TOKEN_INVALID' } },
      });

      renderResetPassword('?token=invalid-token');

      await waitFor(() => {
        expect(screen.getByText('Invalid Reset Link')).toBeInTheDocument();
        expect(screen.getByText(/invalid password reset link/i)).toBeInTheDocument();
      });
    });

    it('displays generic error for unknown failures', async () => {
      mockedVerifyResetToken.mockRejectedValue({
        response: { data: { error: 'Something went wrong' } },
      });

      renderResetPassword('?token=problem-token');

      await waitFor(() => {
        expect(screen.getByText('Invalid Reset Link')).toBeInTheDocument();
        expect(screen.getByText('Something went wrong')).toBeInTheDocument();
      });
    });
  });

  describe('Password Form', () => {
    it('validates password strength via PasswordStrengthMeter', async () => {
      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      expect(screen.getByTestId('password-strength-meter')).toBeInTheDocument();
    });

    it('validates password confirmation matches', async () => {
      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const newPasswordInput = screen.getByTestId('newPassword');
      const confirmPasswordInput = screen.getByTestId('confirmPassword');
      const submitButton = screen.getByRole('button', { name: /reset password/i });

      fireEvent.change(newPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'DifferentPassword' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(screen.getByText('Passwords do not match.')).toBeInTheDocument();
      });
    });

    it('disables submit while processing', async () => {
      // Make reset hang
      mockedResetPassword.mockImplementation(() => new Promise(() => {}));

      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const newPasswordInput = screen.getByTestId('newPassword');
      const confirmPasswordInput = screen.getByTestId('confirmPassword');
      const submitButton = screen.getByRole('button', { name: /reset password/i });

      fireEvent.change(newPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(screen.getByRole('button', { name: /resetting password/i })).toBeDisabled();
      });
    });

    it('disables submit when passwords are empty', async () => {
      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const submitButton = screen.getByRole('button', { name: /reset password/i });
      expect(submitButton).toBeDisabled();
    });
  });

  describe('Form Submission', () => {
    it('submits form with valid data', async () => {
      renderResetPassword('?token=valid-token-456');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const newPasswordInput = screen.getByTestId('newPassword');
      const confirmPasswordInput = screen.getByTestId('confirmPassword');
      const submitButton = screen.getByRole('button', { name: /reset password/i });

      fireEvent.change(newPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(mockedResetPassword).toHaveBeenCalledWith('valid-token-456', 'NewPassword123!');
      });
    });

    it('shows success state on successful reset', async () => {
      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const newPasswordInput = screen.getByTestId('newPassword');
      const confirmPasswordInput = screen.getByTestId('confirmPassword');
      const submitButton = screen.getByRole('button', { name: /reset password/i });

      fireEvent.change(newPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(screen.getByText('Password Reset Successfully!')).toBeInTheDocument();
        expect(screen.getByText(/can now log in with your new password/i)).toBeInTheDocument();
      });
    });

    it('shows validation errors from API', async () => {
      mockedResetPassword.mockRejectedValue({
        response: {
          data: {
            details: [
              { message: 'Password must be at least 8 characters' },
              { message: 'Password must contain a number' },
            ],
          },
        },
      });

      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const newPasswordInput = screen.getByTestId('newPassword');
      const confirmPasswordInput = screen.getByTestId('confirmPassword');
      const submitButton = screen.getByRole('button', { name: /reset password/i });

      fireEvent.change(newPasswordInput, { target: { value: 'weak' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'weak' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(screen.getByText(/password must be at least 8 characters.*password must contain a number/i)).toBeInTheDocument();
      });
    });

    it('handles TOKEN_EXPIRED on submit', async () => {
      mockedResetPassword.mockRejectedValue({
        response: { data: { code: 'TOKEN_EXPIRED' } },
      });

      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const newPasswordInput = screen.getByTestId('newPassword');
      const confirmPasswordInput = screen.getByTestId('confirmPassword');
      const submitButton = screen.getByRole('button', { name: /reset password/i });

      fireEvent.change(newPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(screen.getByText(/reset link has expired/i)).toBeInTheDocument();
      });
    });
  });

  describe('Navigation and Actions', () => {
    it('"Request New Link" button navigates home and opens auth modal', async () => {
      mockedVerifyResetToken.mockRejectedValue({
        response: { data: { code: 'TOKEN_EXPIRED' } },
      });

      renderResetPassword('?token=expired-token');

      await waitFor(() => {
        expect(screen.getByText('Invalid Reset Link')).toBeInTheDocument();
      });

      const requestNewLink = screen.getByRole('button', { name: /request new link/i });
      fireEvent.click(requestNewLink);

      expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true });
      expect(mockOpenAuthModal).toHaveBeenCalledWith('forgot-password');
    });

    it('has return to home link on invalid token', async () => {
      mockedVerifyResetToken.mockRejectedValue({
        response: { data: { code: 'TOKEN_INVALID' } },
      });

      renderResetPassword('?token=invalid-token');

      await waitFor(() => {
        expect(screen.getByText('Invalid Reset Link')).toBeInTheDocument();
      });

      const homeLink = screen.getByRole('link', { name: /return to home/i });
      expect(homeLink).toHaveAttribute('href', '/');
    });

    it('redirects to login modal on success', async () => {
      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const newPasswordInput = screen.getByTestId('newPassword');
      const confirmPasswordInput = screen.getByTestId('confirmPassword');
      const submitButton = screen.getByRole('button', { name: /reset password/i });

      fireEvent.change(newPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'NewPassword123!' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(screen.getByText('Password Reset Successfully!')).toBeInTheDocument();
      });

      const loginButton = screen.getByRole('button', { name: /log in now/i });
      fireEvent.click(loginButton);

      expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true });
      expect(mockOpenAuthModal).toHaveBeenCalledWith('login');
    });

    it('has return to home link in form view', async () => {
      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      const homeLink = screen.getByRole('link', { name: /return to home/i });
      expect(homeLink).toHaveAttribute('href', '/');
    });
  });

  describe('StrictMode Prevention', () => {
    it('prevents double token validation', async () => {
      renderResetPassword('?token=valid-token');

      await waitFor(() => {
        expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      });

      // Should only call once despite potential StrictMode double-render
      expect(mockedVerifyResetToken).toHaveBeenCalledTimes(1);
    });
  });
});
