import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import CurrencyInput from '../CurrencyInput';

describe('CurrencyInput', () => {
  const defaultProps = {
    value: 0,
    onChange: vi.fn(),
  };

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

  describe('Rendering', () => {
    it('renders an input element', () => {
      render(<CurrencyInput {...defaultProps} />);
      const input = screen.getByRole('textbox');
      expect(input).toBeInTheDocument();
    });

    it('renders with default placeholder', () => {
      render(<CurrencyInput {...defaultProps} />);
      const input = screen.getByPlaceholderText('$0.00');
      expect(input).toBeInTheDocument();
    });

    it('renders with custom placeholder', () => {
      render(<CurrencyInput {...defaultProps} placeholder="Enter amount" />);
      const input = screen.getByPlaceholderText('Enter amount');
      expect(input).toBeInTheDocument();
    });

    it('applies custom className', () => {
      render(<CurrencyInput {...defaultProps} className="custom-class" />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveClass('custom-class');
    });

    it('displays formatted value for non-zero cents', () => {
      render(<CurrencyInput {...defaultProps} value={1234} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveValue('$12.34');
    });

    it('displays empty for zero value', () => {
      render(<CurrencyInput {...defaultProps} value={0} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveValue('');
    });

    it('can be disabled', () => {
      render(<CurrencyInput {...defaultProps} disabled />);
      const input = screen.getByRole('textbox');
      expect(input).toBeDisabled();
    });

    it('can be required', () => {
      render(<CurrencyInput {...defaultProps} required />);
      const input = screen.getByRole('textbox');
      expect(input).toBeRequired();
    });
  });

  describe('Value Formatting', () => {
    it('formats 5 cents as $0.05', () => {
      render(<CurrencyInput {...defaultProps} value={5} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveValue('$0.05');
    });

    it('formats 50 cents as $0.50', () => {
      render(<CurrencyInput {...defaultProps} value={50} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveValue('$0.50');
    });

    it('formats 500 cents as $5.00', () => {
      render(<CurrencyInput {...defaultProps} value={500} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveValue('$5.00');
    });

    it('formats 1234 cents as $12.34', () => {
      render(<CurrencyInput {...defaultProps} value={1234} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveValue('$12.34');
    });

    it('formats large amounts correctly', () => {
      render(<CurrencyInput {...defaultProps} value={999999} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveValue('$9999.99');
    });
  });

  describe('User Input', () => {
    it('calls onChange with cents value when digits entered', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} />);
      const input = screen.getByRole('textbox');

      fireEvent.change(input, { target: { value: '1234' } });
      expect(onChange).toHaveBeenCalledWith(1234);
    });

    it('extracts only digits from input', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} />);
      const input = screen.getByRole('textbox');

      fireEvent.change(input, { target: { value: '$12.34' } });
      expect(onChange).toHaveBeenCalledWith(1234);
    });

    it('handles empty input', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} value={100} />);
      const input = screen.getByRole('textbox');

      fireEvent.change(input, { target: { value: '' } });
      expect(onChange).toHaveBeenCalledWith(0);
    });

    it('ignores non-digit characters', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} />);
      const input = screen.getByRole('textbox');

      fireEvent.change(input, { target: { value: 'abc123xyz456' } });
      expect(onChange).toHaveBeenCalledWith(123456);
    });
  });

  describe('Min/Max Constraints', () => {
    it('enforces max value on input', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} max={1000} />);
      const input = screen.getByRole('textbox');

      fireEvent.change(input, { target: { value: '5000' } });
      expect(onChange).toHaveBeenCalledWith(1000);
    });

    it('enforces min value on blur', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} min={100} value={50} />);
      const input = screen.getByRole('textbox');

      fireEvent.blur(input);
      expect(onChange).toHaveBeenCalledWith(100);
    });

    it('does not enforce min on blur when value is zero', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} min={100} value={0} />);
      const input = screen.getByRole('textbox');

      fireEvent.blur(input);
      expect(onChange).not.toHaveBeenCalled();
    });
  });

  describe('Focus Behavior', () => {
    it('selects all text on focus', async () => {
      vi.useFakeTimers();
      render(<CurrencyInput {...defaultProps} value={1234} />);
      const input = screen.getByRole('textbox') as HTMLInputElement;

      const selectSpy = vi.spyOn(input, 'select');
      fireEvent.focus(input);

      vi.runAllTimers();
      expect(selectSpy).toHaveBeenCalled();

      vi.useRealTimers();
    });
  });

  describe('Paste Handling', () => {
    it('handles paste with digits', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} />);
      const input = screen.getByRole('textbox');

      const pasteEvent = {
        preventDefault: vi.fn(),
        clipboardData: {
          getData: () => '1234',
        },
      };

      fireEvent.paste(input, pasteEvent);
      expect(onChange).toHaveBeenCalledWith(1234);
    });

    it('extracts digits from pasted text', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} />);
      const input = screen.getByRole('textbox');

      const pasteEvent = {
        preventDefault: vi.fn(),
        clipboardData: {
          getData: () => '$12.34',
        },
      };

      fireEvent.paste(input, pasteEvent);
      expect(onChange).toHaveBeenCalledWith(1234);
    });

    it('enforces max on paste', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} max={500} />);
      const input = screen.getByRole('textbox');

      const pasteEvent = {
        preventDefault: vi.fn(),
        clipboardData: {
          getData: () => '1000',
        },
      };

      fireEvent.paste(input, pasteEvent);
      expect(onChange).toHaveBeenCalledWith(500);
    });

    it('ignores empty paste', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} />);
      const input = screen.getByRole('textbox');

      const pasteEvent = {
        preventDefault: vi.fn(),
        clipboardData: {
          getData: () => '',
        },
      };

      fireEvent.paste(input, pasteEvent);
      expect(onChange).not.toHaveBeenCalled();
    });
  });

  describe('Keyboard Handling', () => {
    it('allows digit keys (can type digits)', () => {
      const onChange = vi.fn();
      render(<CurrencyInput {...defaultProps} onChange={onChange} />);
      const input = screen.getByRole('textbox');

      // Type a digit
      fireEvent.change(input, { target: { value: '5' } });
      expect(onChange).toHaveBeenCalledWith(5);
    });

    it('handles navigation keys (component accepts them)', () => {
      render(<CurrencyInput {...defaultProps} />);
      const input = screen.getByRole('textbox');

      const navigationKeys = ['Backspace', 'Delete', 'Tab', 'ArrowLeft', 'ArrowRight'];

      navigationKeys.forEach((key) => {
        // These should not throw or cause issues
        fireEvent.keyDown(input, { key });
      });

      // If we got here without errors, navigation keys are handled
      expect(input).toBeInTheDocument();
    });

    it('allows Ctrl+A for select all', () => {
      render(<CurrencyInput {...defaultProps} value={1234} />);
      const input = screen.getByRole('textbox');

      // Ctrl+A should not cause issues
      fireEvent.keyDown(input, { key: 'a', ctrlKey: true });
      expect(input).toBeInTheDocument();
    });

    it('allows Cmd+C for copy', () => {
      render(<CurrencyInput {...defaultProps} value={1234} />);
      const input = screen.getByRole('textbox');

      // Cmd+C should not cause issues
      fireEvent.keyDown(input, { key: 'c', metaKey: true });
      expect(input).toBeInTheDocument();
    });
  });

  describe('Input Attributes', () => {
    it('has numeric input mode', () => {
      render(<CurrencyInput {...defaultProps} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveAttribute('inputMode', 'numeric');
    });

    it('disables autocomplete', () => {
      render(<CurrencyInput {...defaultProps} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveAttribute('autoComplete', 'off');
    });

    it('disables spell check', () => {
      render(<CurrencyInput {...defaultProps} />);
      const input = screen.getByRole('textbox');
      expect(input).toHaveAttribute('spellCheck', 'false');
    });
  });
});
