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

const createMockQuiz = (overrides?: Partial<QuizListItem>): QuizListItem => ({
  id: 'quiz-1',
  title: 'Test Quiz',
  source: 'manual',
  questionCount: 5,
  createdAt: '2024-01-15T10:00:00.000Z',
  updatedAt: '2024-01-15T10:00:00.000Z',
  ...overrides,
});

describe('QuizLibrary', () => {
  const defaultProps = {
    isOpen: true,
    onClose: vi.fn(),
    quizzes: [createMockQuiz()],
    loading: false,
    loadingQuizId: null,
    deletingQuizId: null,
    exporting: false,
    error: null,
    onLoadQuiz: vi.fn(),
    onDeleteQuiz: vi.fn(),
    onExportQuizzes: vi.fn(),
    onImportClick: vi.fn(),
    onRetry: vi.fn(),
  };

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

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

    it('renders modal when isOpen is true', () => {
      render(<QuizLibrary {...defaultProps} />);
      expect(screen.getByText('My Library')).toBeInTheDocument();
    });

    it('shows quiz list', () => {
      const quizzes = [
        createMockQuiz({ id: '1', title: 'Quiz 1' }),
        createMockQuiz({ id: '2', title: 'Quiz 2' }),
      ];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      expect(screen.getByText('Quiz 1')).toBeInTheDocument();
      expect(screen.getByText('Quiz 2')).toBeInTheDocument();
    });

    it('shows import button in header', () => {
      render(<QuizLibrary {...defaultProps} />);
      expect(screen.getByTitle('Import quizzes')).toBeInTheDocument();
    });

    it('shows selection mode toggle button', () => {
      render(<QuizLibrary {...defaultProps} />);
      expect(screen.getByTitle('Select for export')).toBeInTheDocument();
    });

    it('shows empty state when no quizzes', () => {
      render(<QuizLibrary {...defaultProps} quizzes={[]} />);
      expect(screen.getByText('No saved quizzes yet')).toBeInTheDocument();
    });

    it('shows import button in empty state', () => {
      render(<QuizLibrary {...defaultProps} quizzes={[]} />);
      expect(screen.getByText('Import Quizzes')).toBeInTheDocument();
    });

    it('shows loading state', () => {
      render(<QuizLibrary {...defaultProps} loading={true} />);
      expect(screen.getByText('Loading your quizzes...')).toBeInTheDocument();
    });

    it('shows error state with retry button', () => {
      render(<QuizLibrary {...defaultProps} error="Failed to load quizzes" />);
      expect(screen.getByText('Failed to load quizzes')).toBeInTheDocument();
      expect(screen.getByText('Try Again')).toBeInTheDocument();
    });
  });

  describe('selection mode', () => {
    it('enters selection mode on toggle click', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      expect(screen.getByTitle('Cancel selection')).toBeInTheDocument();
      expect(screen.getByText('0 of 1 selected')).toBeInTheDocument();
    });

    it('exits selection mode on toggle click', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));
      expect(screen.getByText('0 of 1 selected')).toBeInTheDocument();

      await user.click(screen.getByTitle('Cancel selection'));
      expect(screen.getByText('Select a quiz to play')).toBeInTheDocument();
    });

    it('shows checkboxes in selection mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const checkboxes = document.querySelectorAll('[class*="rounded-lg border-2"]');
      expect(checkboxes.length).toBeGreaterThan(0);
    });

    it('selects quiz on click in selection mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));
      expect(screen.getByText('0 of 1 selected')).toBeInTheDocument();

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);

      expect(screen.getByText('1 of 1 selected')).toBeInTheDocument();
    });

    it('deselects quiz on second click', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);
      expect(screen.getByText('1 of 1 selected')).toBeInTheDocument();

      await user.click(quizCard);
      expect(screen.getByText('0 of 1 selected')).toBeInTheDocument();
    });

    it('shows Select All button in selection mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      expect(screen.getByText('Select All')).toBeInTheDocument();
    });

    it('selects all quizzes on Select All click', async () => {
      const user = userEvent.setup();
      const quizzes = [
        createMockQuiz({ id: '1', title: 'Quiz 1' }),
        createMockQuiz({ id: '2', title: 'Quiz 2' }),
      ];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      await user.click(screen.getByTitle('Select for export'));
      expect(screen.getByText('0 of 2 selected')).toBeInTheDocument();

      await user.click(screen.getByText('Select All'));
      expect(screen.getByText('2 of 2 selected')).toBeInTheDocument();
    });

    it('shows Deselect All when all selected', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);

      expect(screen.getByText('Deselect All')).toBeInTheDocument();
    });

    it('deselects all on Deselect All click', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);
      expect(screen.getByText('1 of 1 selected')).toBeInTheDocument();

      await user.click(screen.getByText('Deselect All'));
      expect(screen.getByText('0 of 1 selected')).toBeInTheDocument();
    });

    it('clears selection when exiting selection mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);
      expect(screen.getByText('1 of 1 selected')).toBeInTheDocument();

      await user.click(screen.getByTitle('Cancel selection'));

      await user.click(screen.getByTitle('Select for export'));
      expect(screen.getByText('0 of 1 selected')).toBeInTheDocument();
    });

    it('hides play and delete buttons in selection mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      expect(screen.getByTitle('Delete quiz')).toBeInTheDocument();

      await user.click(screen.getByTitle('Select for export'));

      expect(screen.queryByTitle('Delete quiz')).not.toBeInTheDocument();
    });
  });

  describe('export functionality', () => {
    it('shows export footer in selection mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      expect(screen.getByText('Cancel')).toBeInTheDocument();
      expect(screen.getByText(/Export 0 Quiz/)).toBeInTheDocument();
    });

    it('export button is disabled when no selection', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const exportButton = screen.getByRole('button', { name: /Export 0 Quiz/ });
      expect(exportButton).toBeDisabled();
    });

    it('export button is enabled with selection', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);

      const exportButton = screen.getByRole('button', { name: /Export 1 Quiz/ });
      expect(exportButton).not.toBeDisabled();
    });

    it('calls onExportQuizzes with selected IDs', async () => {
      const user = userEvent.setup();
      const quizzes = [
        createMockQuiz({ id: 'id-1', title: 'Quiz 1' }),
        createMockQuiz({ id: 'id-2', title: 'Quiz 2' }),
      ];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      await user.click(screen.getByTitle('Select for export'));

      const quiz1Card = screen.getByText('Quiz 1').closest('[class*="cursor-pointer"]')!;
      await user.click(quiz1Card);

      await user.click(screen.getByRole('button', { name: /Export 1 Quiz/ }));

      expect(defaultProps.onExportQuizzes).toHaveBeenCalledWith(['id-1']);
    });

    it('exports multiple selected quizzes', async () => {
      const user = userEvent.setup();
      const quizzes = [
        createMockQuiz({ id: 'id-1', title: 'Quiz 1' }),
        createMockQuiz({ id: 'id-2', title: 'Quiz 2' }),
      ];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      await user.click(screen.getByTitle('Select for export'));
      await user.click(screen.getByText('Select All'));
      await user.click(screen.getByRole('button', { name: /Export 2 Quizzes/ }));

      expect(defaultProps.onExportQuizzes).toHaveBeenCalledWith(['id-1', 'id-2']);
    });

    it('exits selection mode after export', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);

      await user.click(screen.getByRole('button', { name: /Export 1 Quiz/ }));

      await waitFor(() => {
        expect(screen.getByText('Select a quiz to play')).toBeInTheDocument();
      });
    });

    it('cancel button exits selection mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));
      expect(screen.getByText('Cancel')).toBeInTheDocument();

      await user.click(screen.getByText('Cancel'));

      expect(screen.getByText('Select a quiz to play')).toBeInTheDocument();
    });

    it('shows exporting state', async () => {
      const user = userEvent.setup();
      const { rerender } = render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);

      rerender(<QuizLibrary {...defaultProps} exporting={true} />);

      expect(screen.getByText('Exporting...')).toBeInTheDocument();
    });

    it('disables export button while exporting', async () => {
      const user = userEvent.setup();
      const { rerender } = render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);

      rerender(<QuizLibrary {...defaultProps} exporting={true} />);

      const exportButton = screen.getByRole('button', { name: /Exporting/ });
      expect(exportButton).toBeDisabled();
    });
  });

  describe('import functionality', () => {
    it('calls onImportClick when import button clicked', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Import quizzes'));

      expect(defaultProps.onImportClick).toHaveBeenCalled();
    });

    it('calls onImportClick from empty state', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} quizzes={[]} />);

      await user.click(screen.getByText('Import Quizzes'));

      expect(defaultProps.onImportClick).toHaveBeenCalled();
    });
  });

  describe('normal mode interactions', () => {
    it('calls onLoadQuiz when quiz clicked in normal mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);

      expect(defaultProps.onLoadQuiz).toHaveBeenCalledWith('quiz-1');
    });

    it('does not call onLoadQuiz in selection mode', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Select for export'));

      const quizCard = screen.getByText('Test Quiz').closest('[class*="cursor-pointer"]')!;
      await user.click(quizCard);

      expect(defaultProps.onLoadQuiz).not.toHaveBeenCalled();
    });
  });

  describe('quiz display', () => {
    it('shows AI badge for ai_generated quizzes', () => {
      const quizzes = [createMockQuiz({ source: 'ai_generated', aiTopic: 'Science' })];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      expect(screen.getByText('AI')).toBeInTheDocument();
    });

    it('shows Manual badge for manual quizzes', () => {
      const quizzes = [createMockQuiz({ source: 'manual' })];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      expect(screen.getByText('Manual')).toBeInTheDocument();
    });

    it('shows question count', () => {
      const quizzes = [createMockQuiz({ questionCount: 10 })];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      expect(screen.getByText('10 questions')).toBeInTheDocument();
    });

    it('shows singular for 1 question', () => {
      const quizzes = [createMockQuiz({ questionCount: 1 })];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      expect(screen.getByText('1 question')).toBeInTheDocument();
    });

    it('shows aiTopic for AI quizzes', () => {
      const quizzes = [createMockQuiz({ source: 'ai_generated', aiTopic: 'Space' })];
      render(<QuizLibrary {...defaultProps} quizzes={quizzes} />);

      expect(screen.getByText(/Topic: Space/)).toBeInTheDocument();
    });
  });

  describe('delete functionality', () => {
    it('shows delete confirmation on delete click', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Delete quiz'));

      expect(screen.getByText('Confirm')).toBeInTheDocument();
      expect(screen.getByText('Cancel')).toBeInTheDocument();
    });

    it('calls onDeleteQuiz on confirm', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Delete quiz'));
      await user.click(screen.getByText('Confirm'));

      expect(defaultProps.onDeleteQuiz).toHaveBeenCalledWith('quiz-1');
    });

    it('hides confirmation on cancel', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByTitle('Delete quiz'));
      expect(screen.getByText('Confirm')).toBeInTheDocument();

      await user.click(screen.getByText('Cancel'));

      expect(screen.queryByText('Confirm')).not.toBeInTheDocument();
    });
  });

  describe('disabled states', () => {
    it('hides export/import buttons when loading', () => {
      render(<QuizLibrary {...defaultProps} loading={true} />);

      expect(screen.queryByTitle('Import quizzes')).not.toBeInTheDocument();
      expect(screen.queryByTitle('Select for export')).not.toBeInTheDocument();
    });

    it('shows quiz cards with reduced opacity when loadingQuizId is set', async () => {
      render(<QuizLibrary {...defaultProps} loadingQuizId="quiz-1" />);

      const quizCard = screen.getByText('Test Quiz').closest('[class*="rounded-2xl"]')!;
      expect(quizCard).toHaveClass('opacity-70');
    });

    it('shows quiz cards with reduced opacity when deletingQuizId is set', async () => {
      render(<QuizLibrary {...defaultProps} deletingQuizId="quiz-1" />);

      const quizCard = screen.getByText('Test Quiz').closest('[class*="rounded-2xl"]')!;
      expect(quizCard).toHaveClass('opacity-70');
    });
  });

  describe('modal interactions', () => {
    it('calls onClose when backdrop clicked', async () => {
      render(<QuizLibrary {...defaultProps} />);

      const backdrop = document.querySelector('.fixed.inset-0');
      expect(backdrop).toBeInTheDocument();
      fireEvent.click(backdrop!);

      expect(defaultProps.onClose).toHaveBeenCalled();
    });

    it('does not close when modal content clicked', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} />);

      await user.click(screen.getByText('My Library'));

      expect(defaultProps.onClose).not.toHaveBeenCalled();
    });

    it('calls onRetry when Try Again clicked', async () => {
      const user = userEvent.setup();
      render(<QuizLibrary {...defaultProps} error="Failed to load" />);

      await user.click(screen.getByText('Try Again'));

      expect(defaultProps.onRetry).toHaveBeenCalled();
    });
  });
});
