/**
 * Component Tests - AnswerInput
 * 
 * Tests for:
 * - Input value change
 * - Math symbol insertion
 * - Keyboard shortcuts (Enter to submit)
 * - Preview rendering
 * - XSS prevention
 * - Validation states
 */

import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';
import { AnswerInput } from '@/components/exercises/AnswerInput';

// Mock MathFormula
vi.mock('@/components/math/MathFormula', () => ({
  MathFormula: ({ formula }: { formula: string }) => <div data-testid="math-preview">{formula}</div>,
  validateFormula: vi.fn((formula: string) => ({ 
    isValid: !formula.includes('href') && !formula.includes('script'), 
    error: formula.includes('href') || formula.includes('script') ? 'Invalid formula' : undefined 
  })),
}));

describe('AnswerInput', () => {
  const mockOnChange = vi.fn();
  const mockOnSubmit = vi.fn();

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

  // ============================================
  // BASIC RENDERING TESTS
  // ============================================

  describe('Basic Rendering', () => {
    it('should render input with placeholder', () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          placeholder="Test placeholder"
        />
      );
      
      const input = screen.getByPlaceholderText(/Test placeholder/i);
      expect(input).toBeInTheDocument();
    });

    it('should render with label', () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          label="Your Answer"
        />
      );
      
      expect(screen.getByText(/Your Answer/)).toBeInTheDocument();
    });

    it('should display current value', () => {
      render(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      const input = screen.getByDisplayValue('42');
      expect(input).toBeInTheDocument();
    });
  });

  // ============================================
  // INPUT CHANGE TESTS
  // ============================================

  describe('Input Changes', () => {
    it('should call onChange when typing', async () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      const input = screen.getByPlaceholderText(/Enter your answer/i);
      await userEvent.type(input, '4');

      expect(mockOnChange).toHaveBeenCalledWith('4');
    });

    it('should update value on change', async () => {
      const { rerender } = render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      const input = screen.getByPlaceholderText(/Enter your answer/i);
      
      // Type character by character to simulate real typing
      await userEvent.type(input, '4');
      expect(mockOnChange).toHaveBeenCalledWith('4');
      
      // Simulate parent updating value after first character
      rerender(
        <AnswerInput
          value="4"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      // Clear mock and type second character
      mockOnChange.mockClear();
      await userEvent.type(input, '2');
      expect(mockOnChange).toHaveBeenCalledWith('42');

      // Simulate parent updating value
      rerender(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );

      expect(screen.getByDisplayValue('42')).toBeInTheDocument();
    });
  });

  // ============================================
  // SUBMISSION TESTS
  // ============================================

  describe('Answer Submission', () => {
    it('should call onSubmit when Enter is pressed', async () => {
      render(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      const input = screen.getByDisplayValue('42');
      fireEvent.keyDown(input, { key: 'Enter' });

      expect(mockOnSubmit).toHaveBeenCalled();
    });

    it('should not submit when input is empty', async () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      const input = screen.getByPlaceholderText(/Enter your answer/i);
      fireEvent.keyDown(input, { key: 'Enter' });

      expect(mockOnSubmit).not.toHaveBeenCalled();
    });

    it('should submit when clicking submit button', async () => {
      render(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      const submitButton = screen.getByRole('button', { name: /submit answer/i });
      fireEvent.click(submitButton);

      expect(mockOnSubmit).toHaveBeenCalled();
    });
  });

  // ============================================
  // MATH SYMBOLS TESTS
  // ============================================

  describe('Math Symbols Toolbar', () => {
    it('should render math symbols', () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showSymbols={true}
        />
      );
      
      expect(screen.getByText(/Quick insert/i)).toBeInTheDocument();
      
      // Check for some symbols
      expect(screen.getByRole('button', { name: /Insert \+/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /Insert π/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /Insert √/i })).toBeInTheDocument();
    });

    it('should insert symbol at cursor position', async () => {
      render(
        <AnswerInput
          value="2+"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showSymbols={true}
        />
      );
      
      // First focus input and set cursor at the end
      const input = screen.getByDisplayValue('2+');
      await userEvent.click(input);
      
      const piButton = screen.getByRole('button', { name: /Insert π/i });
      fireEvent.click(piButton);

      expect(mockOnChange).toHaveBeenCalledWith('2+\\pi');
    });

    it('should not show symbols when disabled', () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showSymbols={false}
        />
      );
      
      expect(screen.queryByText(/Quick insert/i)).not.toBeInTheDocument();
    });

    it('should not show symbols when input is disabled', () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showSymbols={true}
          disabled={true}
        />
      );
      
      expect(screen.queryByText(/Quick insert/i)).not.toBeInTheDocument();
    });
  });

  // ============================================
  // PREVIEW TESTS
  // ============================================

  describe('LaTeX Preview', () => {
    it('should show preview when showPreview is true and value exists', () => {
      render(
        <AnswerInput
          value="x^2 + y^2"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showPreview={true}
        />
      );
      
      expect(screen.getByText(/Preview/i)).toBeInTheDocument();
      expect(screen.getByTestId('math-preview')).toHaveTextContent('x^2 + y^2');
    });

    it('should not show preview when value is empty', () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showPreview={true}
        />
      );
      
      expect(screen.queryByText(/Preview/i)).not.toBeInTheDocument();
    });

    it('should not show preview when showPreview is false', () => {
      render(
        <AnswerInput
          value="x^2 + y^2"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showPreview={false}
        />
      );
      
      expect(screen.queryByText(/Preview/i)).not.toBeInTheDocument();
    });

    it('should show error when formula is invalid', async () => {
      const { validateFormula } = await import('@/components/math/MathFormula');
      (validateFormula as any).mockReturnValueOnce({ 
        isValid: false, 
        error: 'Invalid LaTeX' 
      });

      render(
        <AnswerInput
          value="\\invalid{command}"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showPreview={true}
        />
      );
      
      expect(screen.getByText(/Invalid LaTeX/)).toBeInTheDocument();
    });
  });

  // ============================================
  // SECURITY TESTS
  // ============================================

  describe('Security - XSS Prevention', () => {
    it('should detect XSS in LaTeX commands', async () => {
      const { validateFormula } = await import('@/components/math/MathFormula');
      
      // Use single backslash as user would type it (gets escaped to double in display)
      const xssInput = '\\href{javascript:alert(1)}{x}';
      
      render(
        <AnswerInput
          value={xssInput}
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showPreview={true}
        />
      );

      expect(validateFormula).toHaveBeenCalledWith(xssInput);
    });

    it('should show security error for dangerous formulas', async () => {
      const { validateFormula } = await import('@/components/math/MathFormula');
      (validateFormula as any).mockReturnValue({ 
        isValid: false, 
        error: 'Comandos no permitidos detectados' 
      });

      const { rerender } = render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showPreview={true}
        />
      );

      rerender(
        <AnswerInput
          value="\\href{javascript:alert(1)}{x}"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          showPreview={true}
        />
      );

      // Wait for validation to complete
      await new Promise(resolve => setTimeout(resolve, 0));

      expect(screen.getByText(/Comandos no permitidos/i)).toBeInTheDocument();
    });
  });

  // ============================================
  // VALIDATION STATE TESTS
  // ============================================

  describe('Validation States', () => {
    it('should show correct state styling', () => {
      render(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          isCorrect={true}
          showValidation={true}
        />
      );
      
      const input = screen.getByDisplayValue('42');
      expect(input).toHaveClass('border-green-500');
      
      // Check for check icon
      expect(screen.getByRole('button', { name: /submit answer/i }).parentElement?.querySelector('.bg-green-500')).toBeInTheDocument();
    });

    it('should show incorrect state styling', () => {
      render(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          isIncorrect={true}
          showValidation={true}
        />
      );
      
      const input = screen.getByDisplayValue('42');
      expect(input).toHaveClass('border-red-500');
      
      // Check for X icon
      expect(screen.getByRole('button', { name: /submit answer/i }).parentElement?.querySelector('.bg-red-500')).toBeInTheDocument();
    });

    it('should show error message', () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          error="Please enter an answer"
        />
      );
      
      expect(screen.getByRole('alert')).toHaveTextContent('Please enter an answer');
      
      const input = screen.getByPlaceholderText(/Enter your answer/i);
      expect(input).toHaveClass('border-destructive');
    });
  });

  // ============================================
  // DISABLED STATE TESTS
  // ============================================

  describe('Disabled State', () => {
    it('should disable input when disabled prop is true', () => {
      render(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          disabled={true}
        />
      );
      
      const input = screen.getByDisplayValue('42');
      expect(input).toBeDisabled();
    });

    it('should not show submit button when disabled', () => {
      render(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
          disabled={true}
        />
      );
      
      // Submit button should not be present when disabled
      const submitButtons = screen.queryAllByRole('button');
      const submitButton = submitButtons.find(btn => 
        btn.getAttribute('aria-label') === 'Submit answer'
      );
      expect(submitButton).toBeUndefined();
    });
  });

  // ============================================
  // CLEAR FUNCTIONALITY TESTS
  // ============================================

  describe('Clear Functionality', () => {
    it('should clear input when clear button is clicked', () => {
      render(
        <AnswerInput
          value="42"
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      const clearButton = screen.getByRole('button', { name: /Clear input/i });
      fireEvent.click(clearButton);

      expect(mockOnChange).toHaveBeenCalledWith('');
    });

    it('should not show clear button when input is empty', () => {
      render(
        <AnswerInput
          value=""
          onChange={mockOnChange}
          onSubmit={mockOnSubmit}
        />
      );
      
      const clearButton = screen.queryByRole('button', { name: /Clear input/i });
      expect(clearButton).not.toBeInTheDocument();
    });
  });
});
