import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GameConfigPanel } from '../../components/GameConfigPanel';
import { DEFAULT_GAME_CONFIG } from '../../types';
import type { GameConfig } from '../../types';

describe('GameConfigPanel', () => {
  const mockOnChange = vi.fn();
  
  const defaultProps = {
    config: { ...DEFAULT_GAME_CONFIG },
    onChange: mockOnChange,
    questionCount: 10,
  };

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

  describe('rendering', () => {
    it('renders all config toggles', () => {
      render(<GameConfigPanel {...defaultProps} />);
      
      expect(screen.getByText('Shuffle Questions')).toBeInTheDocument();
      expect(screen.getByText('Shuffle Answers')).toBeInTheDocument();
      expect(screen.getByText('Host Participates')).toBeInTheDocument();
      expect(screen.getByText('Streak Bonus')).toBeInTheDocument();
      expect(screen.getByText('Comeback Bonus')).toBeInTheDocument();
      expect(screen.getByText('Wrong Answer Penalty')).toBeInTheDocument();
      expect(screen.getByText('First Correct Bonus')).toBeInTheDocument();
    });

    it('renders descriptions for each toggle', () => {
      render(<GameConfigPanel {...defaultProps} />);
      
      expect(screen.getByText('Randomize question order when starting')).toBeInTheDocument();
      expect(screen.getByText('Randomize answer positions for each question')).toBeInTheDocument();
      expect(screen.getByText('Join as a player and answer questions')).toBeInTheDocument();
    });

    it('renders compact collapsed state when compact=true', () => {
      render(<GameConfigPanel {...defaultProps} compact />);
      
      expect(screen.getByText('Game Settings')).toBeInTheDocument();
      expect(screen.queryByText('Shuffle Questions')).not.toBeInTheDocument();
    });

    it('expands when clicking collapsed compact view', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} compact />);
      
      await user.click(screen.getByText('Game Settings'));
      
      expect(screen.getByText('Shuffle Questions')).toBeInTheDocument();
    });

    it('collapses when clicking expanded compact view header', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} compact />);
      
      await user.click(screen.getByText('Game Settings'));
      expect(screen.getByText('Shuffle Questions')).toBeInTheDocument();
      
      await user.click(screen.getByText('Game Settings'));
      expect(screen.queryByText('Shuffle Questions')).not.toBeInTheDocument();
    });
  });

  describe('toggle interactions - happy path', () => {
    const getCheckboxForLabel = (labelText: string) => {
      const label = screen.getByText(labelText);
      const row = label.closest('[class*="bg-white rounded-xl"]')!;
      return row.querySelector('input[type="checkbox"]')!;
    };

    it('calls onChange when toggling shuffleQuestions', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      await user.click(screen.getByText('Shuffle Questions'));
      
      expect(mockOnChange).toHaveBeenCalledWith({
        ...DEFAULT_GAME_CONFIG,
        shuffleQuestions: true,
      });
    });

    it('calls onChange when toggling shuffleAnswers', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      await user.click(screen.getByText('Shuffle Answers'));
      
      expect(mockOnChange).toHaveBeenCalledWith({
        ...DEFAULT_GAME_CONFIG,
        shuffleAnswers: true,
      });
    });

    it('calls onChange when toggling hostParticipates off', async () => {
      const user = userEvent.setup();
      const config = { ...DEFAULT_GAME_CONFIG, hostParticipates: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      await user.click(screen.getByText('Host Participates'));
      
      expect(mockOnChange).toHaveBeenCalledWith({
        ...config,
        hostParticipates: false,
      });
    });

    it('calls onChange when enabling streakBonus', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      await user.click(screen.getByText('Streak Bonus'));
      
      expect(mockOnChange).toHaveBeenCalledWith({
        ...DEFAULT_GAME_CONFIG,
        streakBonusEnabled: true,
      });
    });

    it('calls onChange when enabling comebackBonus', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      await user.click(screen.getByText('Comeback Bonus'));
      
      expect(mockOnChange).toHaveBeenCalledWith({
        ...DEFAULT_GAME_CONFIG,
        comebackBonusEnabled: true,
      });
    });

    it('calls onChange when enabling penalty', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      await user.click(screen.getByText('Wrong Answer Penalty'));
      
      expect(mockOnChange).toHaveBeenCalledWith({
        ...DEFAULT_GAME_CONFIG,
        penaltyForWrongAnswer: true,
      });
    });

    it('calls onChange when enabling firstCorrectBonus', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      await user.click(screen.getByText('First Correct Bonus'));
      
      expect(mockOnChange).toHaveBeenCalledWith({
        ...DEFAULT_GAME_CONFIG,
        firstCorrectBonusEnabled: true,
      });
    });
  });

  describe('nested number inputs - happy path', () => {
    it('shows streak settings when streakBonusEnabled', () => {
      const config = { ...DEFAULT_GAME_CONFIG, streakBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      expect(screen.getByText('Streak threshold')).toBeInTheDocument();
      expect(screen.getByText('Multiplier')).toBeInTheDocument();
    });

    it('shows comeback settings when comebackBonusEnabled', () => {
      const config = { ...DEFAULT_GAME_CONFIG, comebackBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      expect(screen.getByText('Bonus points')).toBeInTheDocument();
    });

    it('shows penalty settings when penaltyForWrongAnswer enabled', () => {
      const config = { ...DEFAULT_GAME_CONFIG, penaltyForWrongAnswer: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      expect(screen.getByText('Penalty')).toBeInTheDocument();
    });

    it('updates streakThreshold value', async () => {
      const config = { ...DEFAULT_GAME_CONFIG, streakBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      const thresholdInput = screen.getByDisplayValue('3') as HTMLInputElement;
      fireEvent.change(thresholdInput, { target: { value: '5' } });
      
      expect(mockOnChange).toHaveBeenLastCalledWith(
        expect.objectContaining({ streakThreshold: 5 })
      );
    });

    it('updates streakMultiplier value', async () => {
      const config = { ...DEFAULT_GAME_CONFIG, streakBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      const multiplierInput = screen.getByDisplayValue('1.1') as HTMLInputElement;
      fireEvent.change(multiplierInput, { target: { value: '1.5' } });
      
      expect(mockOnChange).toHaveBeenLastCalledWith(
        expect.objectContaining({ streakMultiplier: 1.5 })
      );
    });

    it('updates comebackBonusPoints value', async () => {
      const config = { ...DEFAULT_GAME_CONFIG, comebackBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      const bonusInput = screen.getByDisplayValue('50') as HTMLInputElement;
      fireEvent.change(bonusInput, { target: { value: '100' } });
      
      expect(mockOnChange).toHaveBeenLastCalledWith(
        expect.objectContaining({ comebackBonusPoints: 100 })
      );
    });

    it('updates penaltyPercent value', async () => {
      const config = { ...DEFAULT_GAME_CONFIG, penaltyForWrongAnswer: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      const penaltyInput = screen.getByDisplayValue('25') as HTMLInputElement;
      fireEvent.change(penaltyInput, { target: { value: '50' } });
      
      expect(mockOnChange).toHaveBeenLastCalledWith(
        expect.objectContaining({ penaltyPercent: 50 })
      );
    });

    it('updates firstCorrectBonusPoints value', async () => {
      const config = { ...DEFAULT_GAME_CONFIG, firstCorrectBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      const bonusInput = screen.getByDisplayValue('50') as HTMLInputElement;
      fireEvent.change(bonusInput, { target: { value: '75' } });
      
      expect(mockOnChange).toHaveBeenLastCalledWith(
        expect.objectContaining({ firstCorrectBonusPoints: 75 })
      );
    });
  });

  describe('suggested values', () => {
    it('displays suggested comeback bonus based on question count', () => {
      const config = { ...DEFAULT_GAME_CONFIG, comebackBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} questionCount={10} />);
      
      expect(screen.getByText(/Suggested for 10 questions: 100 pts/)).toBeInTheDocument();
    });

    it('displays suggested first correct bonus based on question count', () => {
      const config = { ...DEFAULT_GAME_CONFIG, firstCorrectBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} questionCount={10} />);
      
      expect(screen.getByText(/Suggested for 10 questions: 50 pts/)).toBeInTheDocument();
    });

    it('updates suggested values when question count changes', () => {
      const config = { ...DEFAULT_GAME_CONFIG, comebackBonusEnabled: true };
      const { rerender } = render(
        <GameConfigPanel {...defaultProps} config={config} questionCount={10} />
      );
      
      expect(screen.getByText(/Suggested for 10 questions: 100 pts/)).toBeInTheDocument();
      
      rerender(<GameConfigPanel {...defaultProps} config={config} questionCount={20} />);
      
      expect(screen.getByText(/Suggested for 20 questions: 150 pts/)).toBeInTheDocument();
    });
  });

  describe('interactions - unhappy path / edge cases', () => {
    it('handles rapid toggle clicks', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      const label = screen.getByText('Shuffle Questions');
      
      await user.click(label);
      await user.click(label);
      await user.click(label);
      
      expect(mockOnChange).toHaveBeenCalledTimes(3);
    });

    it('handles number input with invalid value (converts to 0)', async () => {
      const config = { ...DEFAULT_GAME_CONFIG, streakBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      const thresholdInput = screen.getByDisplayValue('3') as HTMLInputElement;
      fireEvent.change(thresholdInput, { target: { value: 'abc' } });
      
      expect(mockOnChange).toHaveBeenCalledWith(
        expect.objectContaining({ streakThreshold: 0 })
      );
    });

    it('handles number input with negative value', async () => {
      const config = { ...DEFAULT_GAME_CONFIG, comebackBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      const bonusInput = screen.getByDisplayValue('50') as HTMLInputElement;
      fireEvent.change(bonusInput, { target: { value: '-10' } });
      
      expect(mockOnChange).toHaveBeenCalledWith(
        expect.objectContaining({ comebackBonusPoints: -10 })
      );
    });

    it('handles empty number input (converts to 0)', async () => {
      const config = { ...DEFAULT_GAME_CONFIG, streakBonusEnabled: true };
      render(<GameConfigPanel {...defaultProps} config={config} />);
      
      const thresholdInput = screen.getByDisplayValue('3') as HTMLInputElement;
      fireEvent.change(thresholdInput, { target: { value: '' } });
      
      expect(mockOnChange).toHaveBeenCalledWith(
        expect.objectContaining({ streakThreshold: 0 })
      );
    });

    it('does not show nested settings when toggle is off', () => {
      render(<GameConfigPanel {...defaultProps} />);
      
      expect(screen.queryByText('Streak threshold')).not.toBeInTheDocument();
      expect(screen.queryByText('Multiplier')).not.toBeInTheDocument();
    });

    it('hides nested settings when toggle is turned off', async () => {
      const user = userEvent.setup();
      const config = { ...DEFAULT_GAME_CONFIG, streakBonusEnabled: true };
      const { rerender } = render(<GameConfigPanel {...defaultProps} config={config} />);
      
      expect(screen.getByText('Streak threshold')).toBeInTheDocument();
      
      rerender(<GameConfigPanel {...defaultProps} config={{ ...config, streakBonusEnabled: false }} />);
      
      expect(screen.queryByText('Streak threshold')).not.toBeInTheDocument();
    });
  });

  describe('tooltip interactions', () => {
    it('shows tooltip on hover', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      const infoButtons = screen.getAllByRole('button').filter(
        btn => btn.querySelector('svg')
      );
      
      if (infoButtons.length > 0) {
        await user.hover(infoButtons[0]);
      }
    });

    it('shows tooltip on click', async () => {
      const user = userEvent.setup();
      render(<GameConfigPanel {...defaultProps} />);
      
      const infoButtons = screen.getAllByRole('button').filter(
        btn => btn.querySelector('svg')
      );
      
      if (infoButtons.length > 0) {
        await user.click(infoButtons[0]);
      }
    });
  });

  describe('state management', () => {
    const getCheckboxForRow = (labelText: string) => {
      const label = screen.getByText(labelText);
      const row = label.closest('[class*="bg-white rounded-xl"]')!;
      return row.querySelector('input[type="checkbox"]') as HTMLInputElement;
    };

    it('reflects updated config values correctly', () => {
      const customConfig: GameConfig = {
        ...DEFAULT_GAME_CONFIG,
        shuffleQuestions: true,
        shuffleAnswers: true,
        hostParticipates: false,
        streakBonusEnabled: true,
        streakThreshold: 5,
        streakMultiplier: 1.5,
      };
      
      render(<GameConfigPanel {...defaultProps} config={customConfig} />);
      
      expect(getCheckboxForRow('Shuffle Questions').checked).toBe(true);
      expect(getCheckboxForRow('Shuffle Answers').checked).toBe(true);
      expect(getCheckboxForRow('Host Participates').checked).toBe(false);
      
      expect(screen.getByDisplayValue('5')).toBeInTheDocument();
      expect(screen.getByDisplayValue('1.5')).toBeInTheDocument();
    });

    it('handles config with all options enabled', () => {
      const allEnabledConfig: GameConfig = {
        shuffleQuestions: true,
        shuffleAnswers: true,
        hostParticipates: true,
        randomNamesEnabled: false,
        streakBonusEnabled: true,
        streakThreshold: 3,
        streakMultiplier: 1.2,
        comebackBonusEnabled: true,
        comebackBonusPoints: 100,
        penaltyForWrongAnswer: true,
        penaltyPercent: 30,
        firstCorrectBonusEnabled: true,
        firstCorrectBonusPoints: 75,
        timerEnabled: true,
        maxPlayers: 10,
      };
      
      render(<GameConfigPanel {...defaultProps} config={allEnabledConfig} />);
      
      expect(screen.getByText('Streak threshold')).toBeInTheDocument();
      expect(screen.getAllByText('Bonus points').length).toBe(2);
      expect(screen.getByText('Penalty')).toBeInTheDocument();
    });

    it('handles config with all options disabled', () => {
      const allDisabledConfig: GameConfig = {
        shuffleQuestions: false,
        shuffleAnswers: false,
        hostParticipates: false,
        randomNamesEnabled: false,
        streakBonusEnabled: false,
        streakThreshold: 3,
        streakMultiplier: 1.1,
        comebackBonusEnabled: false,
        comebackBonusPoints: 50,
        penaltyForWrongAnswer: false,
        penaltyPercent: 25,
        firstCorrectBonusEnabled: false,
        firstCorrectBonusPoints: 50,
        timerEnabled: true,
        maxPlayers: 10,
      };
      
      render(<GameConfigPanel {...defaultProps} config={allDisabledConfig} />);
      
      expect(screen.queryByText('Streak threshold')).not.toBeInTheDocument();
      expect(screen.queryByText('Bonus points')).not.toBeInTheDocument();
      expect(screen.queryByText('Penalty')).not.toBeInTheDocument();
    });
  });
});
