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 { QuizEditor } from '../../components/QuizEditor';
import { DEFAULT_GAME_CONFIG } from '../../types';
import type { Quiz, GameConfig } from '../../types';

vi.mock('uuid', () => ({
  v4: () => 'mock-uuid-' + Math.random().toString(36).substr(2, 9),
}));

const createMockQuiz = (overrides?: Partial<Quiz>): Quiz => ({
  title: 'Test Quiz',
  questions: [
    {
      id: 'q1',
      text: 'What is 2+2?',
      timeLimit: 20,
      options: [
        { text: '3', isCorrect: false, shape: 'triangle', color: 'red' },
        { text: '4', isCorrect: true, shape: 'diamond', color: 'blue' },
        { text: '5', isCorrect: false, shape: 'circle', color: 'yellow' },
        { text: '6', isCorrect: false, shape: 'square', color: 'green' },
      ],
    },
  ],
  ...overrides,
});

describe('QuizEditor - Game Config Integration', () => {
  const mockOnSave = vi.fn();
  const mockOnStartGame = vi.fn();
  const mockOnConfigChange = vi.fn();
  const mockOnBack = vi.fn();

  const defaultProps = {
    quiz: createMockQuiz(),
    onSave: mockOnSave,
    onStartGame: mockOnStartGame,
    onConfigChange: mockOnConfigChange,
    onBack: mockOnBack,
    showSaveButton: true,
    isSaving: false,
  };

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

  describe('config initialization', () => {
    it('uses DEFAULT_GAME_CONFIG when quiz has no config', () => {
      render(<QuizEditor {...defaultProps} />);
      
      expect(screen.getByText('Game Settings')).toBeInTheDocument();
    });

    it('uses quiz config when provided', async () => {
      const user = userEvent.setup();
      const quizWithConfig = createMockQuiz({
        config: {
          ...DEFAULT_GAME_CONFIG,
          shuffleQuestions: true,
          streakBonusEnabled: true,
        },
      });
      
      render(<QuizEditor {...defaultProps} quiz={quizWithConfig} />);
      
      await user.click(screen.getByText('Game Settings'));
      
      const getCheckbox = (labelText: string) => {
        const label = screen.getByText(labelText);
        const row = label.closest('[class*="bg-white rounded-xl"]')!;
        return row.querySelector('input[type="checkbox"]') as HTMLInputElement;
      };
      
      expect(getCheckbox('Shuffle Questions').checked).toBe(true);
    });

    it('uses defaultConfig prop when quiz has no config', async () => {
      const user = userEvent.setup();
      const customDefault: GameConfig = {
        ...DEFAULT_GAME_CONFIG,
        hostParticipates: false,
        penaltyForWrongAnswer: true,
      };
      
      render(<QuizEditor {...defaultProps} defaultConfig={customDefault} />);
      
      await user.click(screen.getByText('Game Settings'));
      
      const getCheckbox = (labelText: string) => {
        const label = screen.getByText(labelText);
        const row = label.closest('[class*="bg-white rounded-xl"]')!;
        return row.querySelector('input[type="checkbox"]') as HTMLInputElement;
      };
      
      expect(getCheckbox('Host Participates').checked).toBe(false);
      expect(getCheckbox('Wrong Answer Penalty').checked).toBe(true);
    });

    it('prioritizes quiz config over defaultConfig prop', async () => {
      const user = userEvent.setup();
      const quizWithConfig = createMockQuiz({
        config: {
          ...DEFAULT_GAME_CONFIG,
          shuffleQuestions: true,
        },
      });
      const customDefault: GameConfig = {
        ...DEFAULT_GAME_CONFIG,
        shuffleQuestions: false,
        shuffleAnswers: true,
      };
      
      render(<QuizEditor {...defaultProps} quiz={quizWithConfig} defaultConfig={customDefault} />);
      
      await user.click(screen.getByText('Game Settings'));
      
      const getCheckbox = (labelText: string) => {
        const label = screen.getByText(labelText);
        const row = label.closest('[class*="bg-white rounded-xl"]')!;
        return row.querySelector('input[type="checkbox"]') as HTMLInputElement;
      };
      
      expect(getCheckbox('Shuffle Questions').checked).toBe(true);
      expect(getCheckbox('Shuffle Answers').checked).toBe(false);
    });
  });

  describe('config panel interactions', () => {
    it('renders collapsed Game Settings by default (compact mode)', () => {
      render(<QuizEditor {...defaultProps} />);
      
      expect(screen.getByText('Game Settings')).toBeInTheDocument();
      expect(screen.queryByText('Shuffle Questions')).not.toBeInTheDocument();
    });

    it('expands config panel when Game Settings is clicked', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      await user.click(screen.getByText('Game Settings'));
      
      expect(screen.getByText('Shuffle Questions')).toBeInTheDocument();
      expect(screen.getByText('Host Participates')).toBeInTheDocument();
    });

    it('collapses config panel when Game Settings is clicked again', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      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();
    });

    it('calls onConfigChange when config is modified', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      await user.click(screen.getByText('Game Settings'));
      await user.click(screen.getByText('Shuffle Questions'));
      
      expect(mockOnConfigChange).toHaveBeenCalledWith({
        ...DEFAULT_GAME_CONFIG,
        shuffleQuestions: true,
      });
    });

    it('calls onConfigChange multiple times for multiple changes', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      await user.click(screen.getByText('Game Settings'));
      await user.click(screen.getByText('Shuffle Questions'));
      await user.click(screen.getByText('Shuffle Answers'));
      
      expect(mockOnConfigChange).toHaveBeenCalledTimes(2);
    });
  });

  describe('start game with config', () => {
    it('passes config to onStartGame', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      await user.click(screen.getByText(/Start Game/));
      
      expect(mockOnStartGame).toHaveBeenCalledWith(
        expect.objectContaining({
          title: 'Test Quiz',
          config: DEFAULT_GAME_CONFIG,
        }),
        DEFAULT_GAME_CONFIG
      );
    });

    it('passes modified config to onStartGame', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      await user.click(screen.getByText('Game Settings'));
      await user.click(screen.getByText('Shuffle Questions'));
      
      await user.click(screen.getByText(/Start Game/));
      
      expect(mockOnStartGame).toHaveBeenCalledWith(
        expect.objectContaining({
          config: expect.objectContaining({
            shuffleQuestions: true,
          }),
        }),
        expect.objectContaining({
          shuffleQuestions: true,
        })
      );
    });

    it('shuffles questions when shuffleQuestions is enabled', async () => {
      const user = userEvent.setup();
      const multiQuestionQuiz = createMockQuiz({
        questions: [
          { id: 'q1', text: 'Q1', timeLimit: 20, options: [{ text: 'A', isCorrect: true, shape: 'triangle', color: 'red' }, { text: 'B', isCorrect: false, shape: 'diamond', color: 'blue' }] },
          { id: 'q2', text: 'Q2', timeLimit: 20, options: [{ text: 'C', isCorrect: true, shape: 'circle', color: 'yellow' }, { text: 'D', isCorrect: false, shape: 'square', color: 'green' }] },
          { id: 'q3', text: 'Q3', timeLimit: 20, options: [{ text: 'E', isCorrect: true, shape: 'triangle', color: 'red' }, { text: 'F', isCorrect: false, shape: 'diamond', color: 'blue' }] },
        ],
        config: { ...DEFAULT_GAME_CONFIG, shuffleQuestions: true },
      });
      
      render(<QuizEditor {...defaultProps} quiz={multiQuestionQuiz} />);
      
      await user.click(screen.getByText(/Start Game/));
      
      expect(mockOnStartGame).toHaveBeenCalledWith(
        expect.objectContaining({
          questions: expect.any(Array),
        }),
        expect.objectContaining({
          shuffleQuestions: true,
        })
      );
      
      const calledQuiz = mockOnStartGame.mock.calls[0][0];
      expect(calledQuiz.questions).toHaveLength(3);
    });

    it('shuffles answers when shuffleAnswers is enabled', async () => {
      const user = userEvent.setup();
      const quizWithConfig = createMockQuiz({
        config: { ...DEFAULT_GAME_CONFIG, shuffleAnswers: true },
      });
      
      render(<QuizEditor {...defaultProps} quiz={quizWithConfig} />);
      
      await user.click(screen.getByText(/Start Game/));
      
      expect(mockOnStartGame).toHaveBeenCalledWith(
        expect.objectContaining({
          questions: expect.arrayContaining([
            expect.objectContaining({
              options: expect.any(Array),
            }),
          ]),
        }),
        expect.objectContaining({
          shuffleAnswers: true,
        })
      );
    });
  });

  describe('config persistence', () => {
    it('maintains config state across quiz title edits', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      await user.click(screen.getByText('Game Settings'));
      await user.click(screen.getByText('Shuffle Questions'));
      
      await user.click(screen.getByText('Test Quiz'));
      const titleInput = screen.getByDisplayValue('Test Quiz');
      await user.clear(titleInput);
      await user.type(titleInput, 'New Title');
      fireEvent.blur(titleInput);
      
      await user.click(screen.getByText(/Start Game/));
      
      expect(mockOnStartGame).toHaveBeenCalledWith(
        expect.objectContaining({
          config: expect.objectContaining({
            shuffleQuestions: true,
          }),
        }),
        expect.any(Object)
      );
    });

    it('maintains config state across question additions', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      await user.click(screen.getByText('Game Settings'));
      await user.click(screen.getByText('Host Participates'));
      
      await user.click(screen.getByText('Add Question'));
      
      expect(mockOnConfigChange).toHaveBeenCalledWith(
        expect.objectContaining({
          hostParticipates: false,
        })
      );
    });
  });

  describe('edge cases', () => {
    it('handles quiz with all config options enabled', async () => {
      const user = userEvent.setup();
      const fullConfigQuiz = createMockQuiz({
        config: {
          shuffleQuestions: true,
          shuffleAnswers: true,
          hostParticipates: true,
          streakBonusEnabled: true,
          streakThreshold: 5,
          streakMultiplier: 1.5,
          comebackBonusEnabled: true,
          comebackBonusPoints: 100,
          penaltyForWrongAnswer: true,
          penaltyPercent: 30,
          firstCorrectBonusEnabled: true,
          firstCorrectBonusPoints: 75,
        },
      });
      
      render(<QuizEditor {...defaultProps} quiz={fullConfigQuiz} />);
      
      await user.click(screen.getByText(/Start Game/));
      
      expect(mockOnStartGame).toHaveBeenCalledWith(
        expect.objectContaining({
          config: expect.objectContaining({
            shuffleQuestions: true,
            streakBonusEnabled: true,
            comebackBonusEnabled: true,
            penaltyForWrongAnswer: true,
            firstCorrectBonusEnabled: true,
          }),
        }),
        expect.any(Object)
      );
    });

    it('handles config without onConfigChange callback', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} onConfigChange={undefined} />);
      
      await user.click(screen.getByText('Game Settings'));
      await user.click(screen.getByText('Shuffle Questions'));
      
      await user.click(screen.getByText(/Start Game/));
      
      expect(mockOnStartGame).toHaveBeenCalledWith(
        expect.objectContaining({
          config: expect.objectContaining({
            shuffleQuestions: true,
          }),
        }),
        expect.any(Object)
      );
    });

    it('handles empty questions array', () => {
      const emptyQuiz = createMockQuiz({ questions: [] });
      
      render(<QuizEditor {...defaultProps} quiz={emptyQuiz} />);
      
      const startButton = screen.getByText(/Start Game/).closest('button');
      expect(startButton).toBeDisabled();
    });

    it('handles quiz without title', () => {
      const noTitleQuiz = createMockQuiz({ title: '' });
      
      render(<QuizEditor {...defaultProps} quiz={noTitleQuiz} />);
      
      expect(screen.getByText('Untitled Quiz')).toBeInTheDocument();
    });
  });

  describe('onConfigChange callback timing', () => {
    it('calls onConfigChange immediately when toggle changes', async () => {
      const user = userEvent.setup();
      render(<QuizEditor {...defaultProps} />);
      
      await user.click(screen.getByText('Game Settings'));
      await user.click(screen.getByText('Shuffle Questions'));
      
      expect(mockOnConfigChange).toHaveBeenCalledTimes(1);
    });

    it('calls onConfigChange for nested number input changes', async () => {
      const user = userEvent.setup();
      const quizWithStreak = createMockQuiz({
        config: { ...DEFAULT_GAME_CONFIG, streakBonusEnabled: true },
      });
      
      render(<QuizEditor {...defaultProps} quiz={quizWithStreak} />);
      
      await user.click(screen.getByText('Game Settings'));
      
      const thresholdInput = screen.getByDisplayValue('3');
      await user.clear(thresholdInput);
      await user.type(thresholdInput, '5');
      
      expect(mockOnConfigChange).toHaveBeenCalled();
    });
  });
});
