/**
 * Comprehensive Unit Tests for ForgotPassword Component
 *
 * Test Coverage:
 * - Component rendering (form fields, buttons, links)
 * - Form validation (email required, email format)
 * - Form submission and password reset flow
 * - Error handling and display
 * - Success state display
 * - Loading states
 * - Accessibility
 * - Internationalization
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import ForgotPassword from '../ForgotPassword';
import { useForgotPassword } from '../../hooks/useAuth';

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

vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    Link: ({ children, to, ...props }: any) => <a href={to} {...props}>{children}</a>,
  };
});

vi.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: (key: string) => {
      const translations: Record<string, string> = {
        'auth.emailRequired': 'Email is required',
        'auth.invalidEmail': 'Please enter a valid email address',
        'auth.email': 'Email',
        'auth.enterEmail': 'Enter your email',
        'auth.forgotPasswordTitle': 'Reset Your Password',
        'auth.forgotPasswordDescription': 'Enter your email and we\'ll send you a link to reset your password.',
        'auth.forgotPasswordHeading': 'Forgot your password?',
        'auth.forgotPasswordSubheading': 'No worries, we\'ll send you reset instructions.',
        'auth.sendResetLink': 'Send reset link',
        'auth.sending': 'Sending...',
        'auth.backToLogin': 'Back to login',
        'auth.validationError': 'Validation Error',
        'auth.forgotPasswordError': 'Failed to send reset email',
        'auth.forgotPasswordSuccessTitle': 'Email sent!',
        'auth.forgotPasswordSuccessMessage': 'Check your email for a link to reset your password.',
        'marketing.copyright': 'All rights reserved',
      };
      return translations[key] || key;
    },
  }),
}));

vi.mock('../../components/SmoothScheduleLogo', () => ({
  default: ({ className }: { className?: string }) => (
    <div className={className} data-testid="logo">Logo</div>
  ),
}));

vi.mock('../../components/LanguageSelector', () => ({
  default: () => <div data-testid="language-selector">Language Selector</div>,
}));

// Test wrapper with Router and QueryClient
const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  });

  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>{children}</BrowserRouter>
    </QueryClientProvider>
  );
};

describe('ForgotPassword', () => {
  let mockForgotPasswordMutate: ReturnType<typeof vi.fn>;

  beforeEach(() => {
    vi.clearAllMocks();

    // Setup mocks
    mockForgotPasswordMutate = vi.fn();
    vi.mocked(useForgotPassword).mockReturnValue({
      mutate: mockForgotPasswordMutate,
      mutateAsync: vi.fn(),
      isPending: false,
    } as any);
  });

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

  describe('Rendering', () => {
    it('should render forgot password form', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByRole('heading', { name: /forgot your password/i })).toBeInTheDocument();
      expect(screen.getByText('No worries, we\'ll send you reset instructions.')).toBeInTheDocument();
    });

    it('should render email input field', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      expect(emailInput).toBeInTheDocument();
      expect(emailInput).toHaveAttribute('type', 'email');
      expect(emailInput).toHaveAttribute('name', 'email');
      expect(emailInput).toHaveAttribute('required');
      expect(emailInput).toHaveAttribute('placeholder', 'Enter your email');
    });

    it('should render submit button', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const submitButton = screen.getByTestId('submit-button');
      expect(submitButton).toBeInTheDocument();
      expect(submitButton).toHaveAttribute('type', 'submit');
      expect(submitButton).toHaveTextContent('Send reset link');
    });

    it('should render back to login link', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const backLink = screen.getByTestId('back-to-login');
      expect(backLink).toBeInTheDocument();
      expect(backLink).toHaveAttribute('href', '/login');
      expect(backLink).toHaveTextContent('Back to login');
    });

    it('should render email icon', () => {
      const { container } = render(<ForgotPassword />, { wrapper: createWrapper() });

      // lucide-react renders SVG elements
      const svgs = container.querySelectorAll('svg');

      // Should have icons for email input and other UI elements
      expect(svgs.length).toBeGreaterThanOrEqual(1);
    });

    it('should render logo', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const logos = screen.getAllByTestId('logo');
      expect(logos.length).toBeGreaterThan(0);
    });

    it('should render branding section on desktop', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
      expect(screen.getByText(/enter your email and we'll send/i)).toBeInTheDocument();
    });

    it('should render language selector', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByTestId('language-selector')).toBeInTheDocument();
    });

    it('should display copyright text', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const currentYear = new Date().getFullYear();
      expect(screen.getByText(`© ${currentYear} All rights reserved`)).toBeInTheDocument();
    });
  });

  describe('Form Input Handling', () => {
    it('should update email field on input', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input') as HTMLInputElement;
      await user.type(emailInput, 'test@example.com');

      expect(emailInput.value).toBe('test@example.com');
    });

    it('should handle empty form submission', async () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const submitButton = screen.getByTestId('submit-button');
      fireEvent.click(submitButton);

      // HTML5 validation should prevent submission
      expect(mockForgotPasswordMutate).not.toHaveBeenCalled();
    });
  });

  describe('Form Validation', () => {
    it('should validate required email', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      // Click without filling - form should not submit
      const submitButton = screen.getByTestId('submit-button');

      // Use fireEvent to bypass HTML5 validation and trigger component validation
      fireEvent.submit(submitButton.closest('form')!);

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

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

      const emailInput = screen.getByTestId('email-input');
      await user.type(emailInput, 'invalid-email');

      const submitButton = screen.getByTestId('submit-button');

      // Use fireEvent to trigger form submit event
      fireEvent.submit(submitButton.closest('form')!);

      await waitFor(() => {
        expect(screen.getByText('Please enter a valid email address')).toBeInTheDocument();
      });
    });

    it('should accept valid email format', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      await user.type(emailInput, 'valid@example.com');

      const submitButton = screen.getByTestId('submit-button');
      await user.click(submitButton);

      expect(mockForgotPasswordMutate).toHaveBeenCalledWith(
        { email: 'valid@example.com' },
        expect.any(Object)
      );
    });

    it('should clear error on new input', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      // Trigger validation error
      const submitButton = screen.getByTestId('submit-button');
      fireEvent.submit(submitButton.closest('form')!);

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

      // Type in input - should clear error on next submit
      const emailInput = screen.getByTestId('email-input');
      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      expect(screen.queryByText('Email is required')).not.toBeInTheDocument();
    });
  });

  describe('Form Submission', () => {
    it('should call forgot password mutation with email', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      expect(mockForgotPasswordMutate).toHaveBeenCalledWith(
        { email: 'test@example.com' },
        expect.any(Object)
      );
    });

    it('should disable submit button when request is pending', () => {
      vi.mocked(useForgotPassword).mockReturnValue({
        mutate: vi.fn(),
        mutateAsync: vi.fn(),
        isPending: true,
      } as any);

      render(<ForgotPassword />, { wrapper: createWrapper() });

      const submitButton = screen.getByTestId('submit-button');
      expect(submitButton).toBeDisabled();
    });

    it('should show loading state in submit button', () => {
      vi.mocked(useForgotPassword).mockReturnValue({
        mutate: vi.fn(),
        mutateAsync: vi.fn(),
        isPending: true,
      } as any);

      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByText('Sending...')).toBeInTheDocument();

      // Should have loading spinner (Loader2 icon)
      const button = screen.getByTestId('submit-button');
      const svg = button.querySelector('svg');
      expect(svg).toBeInTheDocument();
    });
  });

  describe('Success State', () => {
    it('should display success message on successful submission', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      // Trigger success callback
      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onSuccess = callArgs[1].onSuccess;

      await act(async () => {
        onSuccess();
      });

      await waitFor(() => {
        expect(screen.getByText('Email sent!')).toBeInTheDocument();
        expect(screen.getByText('Check your email for a link to reset your password.')).toBeInTheDocument();
      });
    });

    it('should hide form when success message is shown', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      // Trigger success callback
      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onSuccess = callArgs[1].onSuccess;

      await act(async () => {
        onSuccess();
      });

      await waitFor(() => {
        expect(screen.queryByTestId('email-input')).not.toBeInTheDocument();
        expect(screen.getByText('Email sent!')).toBeInTheDocument();
      });
    });

    it('should show success icon in success message', async () => {
      const user = userEvent.setup();
      const { container } = render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      // Trigger success callback
      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onSuccess = callArgs[1].onSuccess;

      await act(async () => {
        onSuccess();
      });

      await waitFor(() => {
        expect(screen.getByText('Email sent!')).toBeInTheDocument();
        // Check that SVG icons are present in the container
        const svgs = container.querySelectorAll('svg');
        expect(svgs.length).toBeGreaterThan(0);
      });
    });

    it('should still show back to login link after success', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      // Trigger success callback
      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onSuccess = callArgs[1].onSuccess;

      await act(async () => {
        onSuccess();
      });

      await waitFor(() => {
        expect(screen.getByTestId('back-to-login')).toBeInTheDocument();
      });
    });
  });

  describe('Error Handling', () => {
    it('should display error message on submission failure', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      // Trigger error callback
      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onError = callArgs[1].onError;

      await act(async () => {
        onError({ response: { data: { error: 'Email not found' } } });
      });

      await waitFor(() => {
        expect(screen.getByText('Validation Error')).toBeInTheDocument();
        expect(screen.getByText('Email not found')).toBeInTheDocument();
      });
    });

    it('should display default error message when no specific error provided', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      // Trigger error callback without specific message
      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onError = callArgs[1].onError;

      await act(async () => {
        onError({});
      });

      await waitFor(() => {
        expect(screen.getByText('Failed to send reset email')).toBeInTheDocument();
      });
    });

    it('should show error icon in error message', async () => {
      const user = userEvent.setup();
      const { container } = render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      // Trigger error callback
      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onError = callArgs[1].onError;

      await act(async () => {
        onError({ response: { data: { error: 'Email not found' } } });
      });

      await waitFor(() => {
        expect(screen.getByText('Email not found')).toBeInTheDocument();
        // Check that SVG icons are present in the container
        const svgs = container.querySelectorAll('svg');
        expect(svgs.length).toBeGreaterThan(0);
      });
    });

    it('should clear error state on new submission', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      // First submission - trigger error
      await user.type(emailInput, 'wrong@example.com');
      await user.click(submitButton);

      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onError = callArgs[1].onError;

      await act(async () => {
        onError({ response: { data: { error: 'Email not found' } } });
      });

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

      // Clear and try again
      await user.clear(emailInput);
      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      // Error should be cleared before new submission
      expect(screen.queryByText('Email not found')).not.toBeInTheDocument();
    });
  });

  describe('Accessibility', () => {
    it('should have proper form label', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
    });

    it('should have required attribute on input', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByTestId('email-input')).toHaveAttribute('required');
    });

    it('should have proper input type', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByTestId('email-input')).toHaveAttribute('type', 'email');
    });

    it('should have autocomplete attribute', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByTestId('email-input')).toHaveAttribute('autoComplete', 'email');
    });

    it('should have accessible logo link', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const links = screen.getAllByRole('link');
      const logoLinks = links.filter(link => link.getAttribute('href') === '/');

      expect(logoLinks.length).toBeGreaterThan(0);
    });
  });

  describe('Internationalization', () => {
    it('should use translations for form labels', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

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

    it('should use translations for buttons', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByRole('button', { name: /send reset link/i })).toBeInTheDocument();
    });

    it('should use translations for headings', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByRole('heading', { name: /forgot your password/i })).toBeInTheDocument();
    });

    it('should use translations for placeholders', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByPlaceholderText('Enter your email')).toBeInTheDocument();
    });

    it('should use translations for validation messages', async () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const submitButton = screen.getByTestId('submit-button');
      fireEvent.submit(submitButton.closest('form')!);

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

    it('should use translations for links', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      expect(screen.getByText('Back to login')).toBeInTheDocument();
    });
  });

  describe('Visual State', () => {
    it('should have proper styling classes on form elements', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      expect(emailInput).toHaveClass('focus:ring-brand-500', 'focus:border-brand-500');
    });

    it('should have proper button styling', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      const submitButton = screen.getByTestId('submit-button');
      expect(submitButton).toHaveClass('bg-brand-600', 'hover:bg-brand-700');
    });

    it('should have error styling when error is displayed', async () => {
      const user = userEvent.setup();
      const { container } = render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onError = callArgs[1].onError;

      await act(async () => {
        onError({ response: { data: { error: 'Email not found' } } });
      });

      await waitFor(() => {
        expect(screen.getByText('Email not found')).toBeInTheDocument();
        // Check for red error styling in the container
        const redElements = container.querySelectorAll('.bg-red-50, .dark\\:bg-red-900\\/20');
        expect(redElements.length).toBeGreaterThan(0);
      });
    });

    it('should have success styling when success is displayed', async () => {
      const user = userEvent.setup();
      const { container } = render(<ForgotPassword />, { wrapper: createWrapper() });

      const emailInput = screen.getByTestId('email-input');
      const submitButton = screen.getByTestId('submit-button');

      await user.type(emailInput, 'test@example.com');
      await user.click(submitButton);

      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onSuccess = callArgs[1].onSuccess;

      await act(async () => {
        onSuccess();
      });

      await waitFor(() => {
        expect(screen.getByText('Email sent!')).toBeInTheDocument();
        // Check for green success styling in the container
        const greenElements = container.querySelectorAll('.bg-green-50, .dark\\:bg-green-900\\/20');
        expect(greenElements.length).toBeGreaterThan(0);
      });
    });
  });

  describe('Integration', () => {
    it('should handle complete forgot password flow successfully', async () => {
      const user = userEvent.setup();
      render(<ForgotPassword />, { wrapper: createWrapper() });

      // Fill in form
      await user.type(screen.getByTestId('email-input'), 'user@example.com');

      // Submit form
      await user.click(screen.getByTestId('submit-button'));

      // Verify mutation was called
      expect(mockForgotPasswordMutate).toHaveBeenCalledWith(
        { email: 'user@example.com' },
        expect.any(Object)
      );

      // Simulate successful response
      const callArgs = mockForgotPasswordMutate.mock.calls[0];
      const onSuccess = callArgs[1].onSuccess;

      await act(async () => {
        onSuccess();
      });

      // Verify success message is displayed
      await waitFor(() => {
        expect(screen.getByText('Email sent!')).toBeInTheDocument();
        expect(screen.getByText('Check your email for a link to reset your password.')).toBeInTheDocument();
      });
    });

    it('should render all sections together', () => {
      render(<ForgotPassword />, { wrapper: createWrapper() });

      // Form elements
      expect(screen.getByTestId('email-input')).toBeInTheDocument();
      expect(screen.getByTestId('submit-button')).toBeInTheDocument();
      expect(screen.getByTestId('back-to-login')).toBeInTheDocument();

      // Additional components
      expect(screen.getByTestId('language-selector')).toBeInTheDocument();

      // Branding
      expect(screen.getAllByTestId('logo').length).toBeGreaterThan(0);
      expect(screen.getByText('Reset Your Password')).toBeInTheDocument();
    });
  });
});
