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

describe('DefaultConfigModal', () => {
  const mockOnClose = vi.fn();
  const mockOnChange = vi.fn();
  const mockOnSave = vi.fn();

  const defaultProps = {
    isOpen: true,
    onClose: mockOnClose,
    config: { ...DEFAULT_GAME_CONFIG },
    onChange: mockOnChange,
    onSave: mockOnSave,
    saving: false,
  };

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

  describe('rendering', () => {
    it('renders nothing when isOpen is false', () => {
      render(<DefaultConfigModal {...defaultProps} isOpen={false} />);
      expect(screen.queryByText('Default Game Settings')).not.toBeInTheDocument();
    });

    it('renders modal when isOpen is true', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      expect(screen.getByText('Default Game Settings')).toBeInTheDocument();
    });

    it('displays subtitle explaining the settings', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      expect(screen.getByText('Applied to all new quizzes')).toBeInTheDocument();
    });

    it('renders GameConfigPanel with config', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      expect(screen.getByText('Shuffle Questions')).toBeInTheDocument();
      expect(screen.getByText('Host Participates')).toBeInTheDocument();
    });

    it('renders Cancel button', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      expect(screen.getByText('Cancel')).toBeInTheDocument();
    });

    it('renders Save Defaults button', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      expect(screen.getByText('Save Defaults')).toBeInTheDocument();
    });

    it('renders close X button', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      const closeButtons = screen.getAllByRole('button');
      const xButton = closeButtons.find(btn => btn.querySelector('svg.lucide-x'));
      expect(xButton).toBeInTheDocument();
    });
  });

  describe('interactions - happy path', () => {
    it('calls onClose when Cancel is clicked', async () => {
      const user = userEvent.setup();
      render(<DefaultConfigModal {...defaultProps} />);
      
      await user.click(screen.getByText('Cancel'));
      
      expect(mockOnClose).toHaveBeenCalledTimes(1);
    });

    it('calls onClose when X button is clicked', async () => {
      const user = userEvent.setup();
      render(<DefaultConfigModal {...defaultProps} />);
      
      const closeButtons = screen.getAllByRole('button');
      const xButton = closeButtons.find(btn => btn.querySelector('svg.lucide-x'));
      await user.click(xButton!);
      
      expect(mockOnClose).toHaveBeenCalledTimes(1);
    });

    it('calls onClose when backdrop is clicked', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      
      const backdrop = document.querySelector('.fixed.inset-0');
      fireEvent.click(backdrop!);
      
      expect(mockOnClose).toHaveBeenCalledTimes(1);
    });

    it('does not close when modal content is clicked', async () => {
      const user = userEvent.setup();
      render(<DefaultConfigModal {...defaultProps} />);
      
      await user.click(screen.getByText('Default Game Settings'));
      
      expect(mockOnClose).not.toHaveBeenCalled();
    });

    it('calls onSave when Save Defaults is clicked', async () => {
      const user = userEvent.setup();
      render(<DefaultConfigModal {...defaultProps} />);
      
      await user.click(screen.getByText('Save Defaults'));
      
      expect(mockOnSave).toHaveBeenCalledTimes(1);
    });

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

  describe('interactions - unhappy path / edge cases', () => {
    it('disables Save button when saving is true', () => {
      render(<DefaultConfigModal {...defaultProps} saving={true} />);
      
      const saveButton = screen.getByText('Saving...').closest('button');
      expect(saveButton).toBeDisabled();
    });

    it('shows Saving... text when saving', () => {
      render(<DefaultConfigModal {...defaultProps} saving={true} />);
      
      expect(screen.getByText('Saving...')).toBeInTheDocument();
      expect(screen.queryByText('Save Defaults')).not.toBeInTheDocument();
    });

    it('does not call onSave when button is disabled and clicked', async () => {
      const user = userEvent.setup();
      render(<DefaultConfigModal {...defaultProps} saving={true} />);
      
      const saveButton = screen.getByText('Saving...').closest('button')!;
      await user.click(saveButton);
      
      expect(mockOnSave).not.toHaveBeenCalled();
    });

    it('Cancel button is still clickable when saving', async () => {
      const user = userEvent.setup();
      render(<DefaultConfigModal {...defaultProps} saving={true} />);
      
      await user.click(screen.getByText('Cancel'));
      
      expect(mockOnClose).toHaveBeenCalledTimes(1);
    });

    it('handles rapid clicks on Save button', async () => {
      const user = userEvent.setup();
      render(<DefaultConfigModal {...defaultProps} />);
      
      const saveButton = screen.getByText('Save Defaults');
      await user.click(saveButton);
      await user.click(saveButton);
      await user.click(saveButton);
      
      expect(mockOnSave).toHaveBeenCalledTimes(3);
    });

    it('remains functional after re-opening', async () => {
      const user = userEvent.setup();
      const { rerender } = render(<DefaultConfigModal {...defaultProps} />);
      
      await user.click(screen.getByText('Save Defaults'));
      expect(mockOnSave).toHaveBeenCalledTimes(1);
      
      rerender(<DefaultConfigModal {...defaultProps} isOpen={false} />);
      expect(screen.queryByText('Default Game Settings')).not.toBeInTheDocument();
      
      rerender(<DefaultConfigModal {...defaultProps} isOpen={true} />);
      await user.click(screen.getByText('Save Defaults'));
      expect(mockOnSave).toHaveBeenCalledTimes(2);
    });
  });

  describe('config propagation', () => {
    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('displays provided config values', () => {
      const customConfig: GameConfig = {
        ...DEFAULT_GAME_CONFIG,
        shuffleQuestions: true,
        streakBonusEnabled: true,
        streakThreshold: 5,
      };
      
      render(<DefaultConfigModal {...defaultProps} config={customConfig} />);
      
      expect(getCheckboxForRow('Shuffle Questions').checked).toBe(true);
      expect(screen.getByDisplayValue('5')).toBeInTheDocument();
    });

    it('updates display when config prop changes', () => {
      const { rerender } = render(<DefaultConfigModal {...defaultProps} />);
      
      expect(getCheckboxForRow('Shuffle Questions').checked).toBe(false);
      
      const newConfig = { ...DEFAULT_GAME_CONFIG, shuffleQuestions: true };
      rerender(<DefaultConfigModal {...defaultProps} config={newConfig} />);
      
      expect(getCheckboxForRow('Shuffle Questions').checked).toBe(true);
    });
  });

  describe('state transitions', () => {
    it('transitions from not saving to saving correctly', () => {
      const { rerender } = render(<DefaultConfigModal {...defaultProps} saving={false} />);
      
      expect(screen.getByText('Save Defaults')).toBeInTheDocument();
      
      rerender(<DefaultConfigModal {...defaultProps} saving={true} />);
      
      expect(screen.getByText('Saving...')).toBeInTheDocument();
    });

    it('transitions from saving back to not saving', () => {
      const { rerender } = render(<DefaultConfigModal {...defaultProps} saving={true} />);
      
      expect(screen.getByText('Saving...')).toBeInTheDocument();
      
      rerender(<DefaultConfigModal {...defaultProps} saving={false} />);
      
      expect(screen.getByText('Save Defaults')).toBeInTheDocument();
    });

    it('transitions from open to closed preserves state for re-open', () => {
      const customConfig = { ...DEFAULT_GAME_CONFIG, shuffleQuestions: true };
      const { rerender } = render(<DefaultConfigModal {...defaultProps} config={customConfig} />);
      
      const getCheckbox = () => {
        const label = screen.getByText('Shuffle Questions');
        const row = label.closest('[class*="bg-white rounded-xl"]')!;
        return row.querySelector('input[type="checkbox"]') as HTMLInputElement;
      };
      
      expect(getCheckbox().checked).toBe(true);
      
      rerender(<DefaultConfigModal {...defaultProps} config={customConfig} isOpen={false} />);
      rerender(<DefaultConfigModal {...defaultProps} config={customConfig} isOpen={true} />);
      
      expect(getCheckbox().checked).toBe(true);
    });
  });

  describe('accessibility', () => {
    it('all interactive elements are focusable', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      
      const buttons = screen.getAllByRole('button');
      buttons.forEach(button => {
        expect(button).not.toHaveAttribute('tabindex', '-1');
      });
    });

    it('modal traps focus within when open', () => {
      render(<DefaultConfigModal {...defaultProps} />);
      
      const modal = document.querySelector('[class*="bg-white rounded-2xl"]');
      expect(modal).toBeInTheDocument();
    });
  });

  describe('with all config options enabled', () => {
    it('renders all nested settings when all options are enabled', () => {
      const allEnabledConfig: GameConfig = {
        shuffleQuestions: true,
        shuffleAnswers: true,
        hostParticipates: true,
        streakBonusEnabled: true,
        streakThreshold: 3,
        streakMultiplier: 1.2,
        comebackBonusEnabled: true,
        comebackBonusPoints: 100,
        penaltyForWrongAnswer: true,
        penaltyPercent: 30,
        firstCorrectBonusEnabled: true,
        firstCorrectBonusPoints: 75,
      };
      
      render(<DefaultConfigModal {...defaultProps} config={allEnabledConfig} />);
      
      expect(screen.getByText('Streak threshold')).toBeInTheDocument();
      expect(screen.getByText('Multiplier')).toBeInTheDocument();
      expect(screen.getAllByText('Bonus points').length).toBe(2);
      expect(screen.getByText('Penalty')).toBeInTheDocument();
    });
  });

  describe('async save behavior', () => {
    it('handles async onSave that resolves', async () => {
      const user = userEvent.setup();
      const asyncOnSave = vi.fn().mockResolvedValue(undefined);
      
      render(<DefaultConfigModal {...defaultProps} onSave={asyncOnSave} />);
      
      await user.click(screen.getByText('Save Defaults'));
      
      expect(asyncOnSave).toHaveBeenCalledTimes(1);
    });

    it('handles async onSave that rejects', async () => {
      const user = userEvent.setup();
      const asyncOnSave = vi.fn().mockRejectedValue(new Error('Save failed'));
      
      render(<DefaultConfigModal {...defaultProps} onSave={asyncOnSave} />);
      
      await user.click(screen.getByText('Save Defaults'));
      
      expect(asyncOnSave).toHaveBeenCalledTimes(1);
    });
  });
});
