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

vi.mock('react-hot-toast', () => ({
  default: {
    success: vi.fn(),
    error: vi.fn(),
  },
}));

vi.mock('framer-motion', () => ({
  motion: {
    div: ({ children, ...props }: React.PropsWithChildren<Record<string, unknown>>) => <div {...props}>{children}</div>,
  },
  AnimatePresence: ({ children }: React.PropsWithChildren) => <>{children}</>,
}));

vi.mock('qrcode.react', () => ({
  QRCodeSVG: ({ value, size }: { value: string; size: number }) => (
    <svg data-testid="qr-code" data-value={value} width={size} height={size} />
  ),
}));

describe('Lobby', () => {
  const defaultProps = {
    quizTitle: 'Test Quiz',
    players: [],
    gamePin: 'ABC123',
    role: 'HOST' as const,
    onStart: vi.fn(),
    onEndGame: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
    Object.defineProperty(window, 'location', {
      value: { origin: 'http://localhost:5173' },
      writable: true,
    });
  });

  describe('copy join link', () => {
    it('shows copy link button for host', () => {
      render(<Lobby {...defaultProps} />);
      
      expect(screen.getByTitle('Copy join link')).toBeInTheDocument();
    });

    it('does not show copy link button for client', () => {
      render(<Lobby {...defaultProps} role="CLIENT" />);
      
      expect(screen.queryByTitle('Copy join link')).not.toBeInTheDocument();
    });

    it('copies join URL to clipboard when clicked', async () => {
      const user = userEvent.setup();
      render(<Lobby {...defaultProps} />);
      
      const copyButton = screen.getByTitle('Copy join link');
      expect(copyButton.querySelector('.lucide-link')).toBeInTheDocument();
      
      await user.click(copyButton);
      
      await waitFor(() => {
        expect(copyButton.querySelector('.lucide-check')).toBeInTheDocument();
      });
    });

    it('shows success toast when link is copied', async () => {
      const toast = await import('react-hot-toast');
      const user = userEvent.setup();
      render(<Lobby {...defaultProps} />);
      
      await user.click(screen.getByTitle('Copy join link'));
      
      await waitFor(() => {
        expect(toast.default.success).toHaveBeenCalledWith('Join link copied!');
      });
    });

    it('shows checkmark after copying link', async () => {
      const user = userEvent.setup();
      render(<Lobby {...defaultProps} gamePin="XYZ789" />);
      
      const copyButton = screen.getByTitle('Copy join link');
      
      await user.click(copyButton);
      
      await waitFor(() => {
        expect(copyButton.querySelector('.lucide-check')).toBeInTheDocument();
      });
    });
  });

  describe('display', () => {
    it('displays game PIN', () => {
      render(<Lobby {...defaultProps} />);
      
      const pins = screen.getAllByText('ABC123');
      expect(pins.length).toBeGreaterThan(0);
    });

    it('displays quiz title on all screen sizes', () => {
      render(<Lobby {...defaultProps} />);
      
      const titles = screen.getAllByText('Test Quiz');
      expect(titles.length).toBeGreaterThan(0);
    });

    it('displays player count', () => {
      const players = [
        { id: '1', name: 'Player 1', score: 0, avatarSeed: 0.1 },
        { id: '2', name: 'Player 2', score: 0, avatarSeed: 0.2 },
      ];
      render(<Lobby {...defaultProps} players={players} />);
      
      const counts = screen.getAllByText('2');
      expect(counts.length).toBeGreaterThan(0);
    });

    it('shows waiting message when no players have joined', () => {
      render(<Lobby {...defaultProps} />);
      
      expect(screen.getByText('Waiting for players to join...')).toBeInTheDocument();
    });

    it('displays player names when players join', () => {
      const players = [
        { id: '1', name: 'Alice', score: 0, avatarSeed: 0.1 },
        { id: '2', name: 'Bob', score: 0, avatarSeed: 0.2 },
      ];
      render(<Lobby {...defaultProps} players={players} />);
      
      expect(screen.getByText('Alice')).toBeInTheDocument();
      expect(screen.getByText('Bob')).toBeInTheDocument();
    });
  });

  describe('host controls', () => {
    it('shows Start button for host', () => {
      render(<Lobby {...defaultProps} />);
      
      expect(screen.getByRole('button', { name: 'Start' })).toBeInTheDocument();
    });

    it('disables Start button when no players', () => {
      render(<Lobby {...defaultProps} />);
      
      expect(screen.getByRole('button', { name: 'Start' })).toBeDisabled();
    });

    it('enables Start button when players joined', () => {
      const players = [{ id: '1', name: 'Player 1', score: 0, avatarSeed: 0.1 }];
      render(<Lobby {...defaultProps} players={players} />);
      
      expect(screen.getByRole('button', { name: 'Start' })).not.toBeDisabled();
    });

    it('calls onStart when Start clicked', async () => {
      const user = userEvent.setup();
      const players = [{ id: '1', name: 'Player 1', score: 0, avatarSeed: 0.1 }];
      render(<Lobby {...defaultProps} players={players} />);
      
      await user.click(screen.getByRole('button', { name: 'Start' }));
      
      expect(defaultProps.onStart).toHaveBeenCalled();
    });

    it('shows End Game button when onEndGame provided', () => {
      render(<Lobby {...defaultProps} />);
      
      expect(screen.getByText('End Game')).toBeInTheDocument();
    });

    it('calls onEndGame when End Game clicked', async () => {
      const user = userEvent.setup();
      render(<Lobby {...defaultProps} />);
      
      await user.click(screen.getByText('End Game'));
      
      expect(defaultProps.onEndGame).toHaveBeenCalled();
    });
  });

  describe('client view', () => {
    it('shows waiting message for client', () => {
      const players = [
        { id: 'player-1', name: 'My Name', score: 0, avatarSeed: 0.5 },
      ];
      render(<Lobby {...defaultProps} role="CLIENT" players={players} currentPlayerId="player-1" />);
      
      expect(screen.getByText('Waiting for the host to start...')).toBeInTheDocument();
    });

    it('shows player name when currentPlayerId matches', () => {
      const players = [
        { id: 'player-1', name: 'My Name', score: 0, avatarSeed: 0.5 },
      ];
      render(
        <Lobby 
          {...defaultProps} 
          role="CLIENT" 
          players={players}
          currentPlayerId="player-1"
        />
      );
      
      expect(screen.getByText('My Name')).toBeInTheDocument();
    });

    it('does not show Start button for client', () => {
      render(<Lobby {...defaultProps} role="CLIENT" />);
      
      expect(screen.queryByRole('button', { name: 'Start' })).not.toBeInTheDocument();
    });
  });

  describe('QR code', () => {
    it('shows QR code for host', () => {
      render(<Lobby {...defaultProps} />);
      
      const qrCode = screen.getByTestId('qr-code');
      expect(qrCode).toBeInTheDocument();
      expect(qrCode).toHaveAttribute('data-value', 'http://localhost:5173/play/ABC123');
    });

    it('does not show QR code for client', () => {
      render(<Lobby {...defaultProps} role="CLIENT" />);
      
      expect(screen.queryByTestId('qr-code')).not.toBeInTheDocument();
    });

    it('does not show QR code when gamePin is null', () => {
      render(<Lobby {...defaultProps} gamePin={null} />);
      
      expect(screen.queryByTestId('qr-code')).not.toBeInTheDocument();
    });

    it('generates correct QR code URL with game PIN', () => {
      render(<Lobby {...defaultProps} gamePin="XYZ789" />);
      
      const qrCode = screen.getByTestId('qr-code');
      expect(qrCode).toHaveAttribute('data-value', 'http://localhost:5173/play/XYZ789');
    });
  });

  describe('host participates mode', () => {
    it('shows host in player list when hostParticipates is true', () => {
      const players = [
        { id: 'host', name: 'Host Player', score: 0, avatarSeed: 0.99 },
      ];
      render(<Lobby {...defaultProps} players={players} hostParticipates={true} />);
      
      expect(screen.getByText('Host Player')).toBeInTheDocument();
      expect(screen.getByText('HOST')).toBeInTheDocument();
    });

    it('does not show waiting message when host participates and no other players', () => {
      const players = [
        { id: 'host', name: 'Host Player', score: 0, avatarSeed: 0.99 },
      ];
      render(<Lobby {...defaultProps} players={players} hostParticipates={true} />);
      
      expect(screen.queryByText('Waiting for players to join...')).not.toBeInTheDocument();
    });
  });

  describe('presenter feature - host view', () => {
    const playersWithPresenter = [
      { id: 'player-1', name: 'Alice', score: 0, avatarSeed: 0.1 },
      { id: 'player-2', name: 'Bob', score: 0, avatarSeed: 0.2 },
    ];

    it('shows presenter selection hint when host does not participate', () => {
      render(
        <Lobby 
          {...defaultProps} 
          players={playersWithPresenter} 
          hostParticipates={false}
          onSetPresenter={vi.fn()}
        />
      );
      
      expect(screen.getByText(/Click a player to make them presenter/)).toBeInTheDocument();
    });

    it('does not show presenter selection hint when host participates', () => {
      const players = [
        { id: 'host', name: 'Host Player', score: 0, avatarSeed: 0.99 },
        ...playersWithPresenter,
      ];
      render(
        <Lobby 
          {...defaultProps} 
          players={players} 
          hostParticipates={true}
          onSetPresenter={vi.fn()}
        />
      );
      
      expect(screen.queryByText(/Click a player to make them presenter/)).not.toBeInTheDocument();
    });

    it('shows PRESENTER badge on the presenter player', () => {
      render(
        <Lobby 
          {...defaultProps} 
          players={playersWithPresenter} 
          hostParticipates={false}
          presenterId="player-1"
          onSetPresenter={vi.fn()}
        />
      );
      
      expect(screen.getByText('PRESENTER')).toBeInTheDocument();
    });

    it('calls onSetPresenter when clicking a player', async () => {
      const user = userEvent.setup();
      const onSetPresenter = vi.fn();
      
      render(
        <Lobby 
          {...defaultProps} 
          players={playersWithPresenter} 
          hostParticipates={false}
          onSetPresenter={onSetPresenter}
        />
      );
      
      await user.click(screen.getByText('Bob'));
      
      expect(onSetPresenter).toHaveBeenCalledWith('player-2');
    });

    it('does not call onSetPresenter when clicking player if host participates', async () => {
      const user = userEvent.setup();
      const onSetPresenter = vi.fn();
      const players = [
        { id: 'host', name: 'Host Player', score: 0, avatarSeed: 0.99 },
        ...playersWithPresenter,
      ];
      
      render(
        <Lobby 
          {...defaultProps} 
          players={players} 
          hostParticipates={true}
          onSetPresenter={onSetPresenter}
        />
      );
      
      await user.click(screen.getByText('Alice'));
      
      expect(onSetPresenter).not.toHaveBeenCalled();
    });

    it('does not show presenter hint when no players have joined', () => {
      render(
        <Lobby 
          {...defaultProps} 
          players={[]} 
          hostParticipates={false}
          onSetPresenter={vi.fn()}
        />
      );
      
      expect(screen.queryByText(/Click a player to make them presenter/)).not.toBeInTheDocument();
    });

    it('applies visual highlight to presenter player', () => {
      render(
        <Lobby 
          {...defaultProps} 
          players={playersWithPresenter} 
          hostParticipates={false}
          presenterId="player-1"
          onSetPresenter={vi.fn()}
        />
      );
      
      // The presenter badge exists, confirming visual distinction
      const presenterBadge = screen.getByText('PRESENTER');
      expect(presenterBadge).toHaveClass('bg-yellow-400');
    });
  });

  describe('presenter feature - client view', () => {
    it('shows presenter crown and badge when current player is presenter', () => {
      const players = [
        { id: 'player-1', name: 'My Name', score: 0, avatarSeed: 0.5 },
      ];
      render(
        <Lobby 
          {...defaultProps} 
          role="CLIENT" 
          players={players}
          currentPlayerId="player-1"
          presenterId="player-1"
        />
      );
      
      expect(screen.getByText('You are the Presenter')).toBeInTheDocument();
      expect(screen.getByText('You can advance screens during the game')).toBeInTheDocument();
    });

    it('shows waiting message when not presenter', () => {
      const players = [
        { id: 'player-1', name: 'My Name', score: 0, avatarSeed: 0.5 },
        { id: 'player-2', name: 'Other', score: 0, avatarSeed: 0.6 },
      ];
      render(
        <Lobby 
          {...defaultProps} 
          role="CLIENT" 
          players={players}
          currentPlayerId="player-1"
          presenterId="player-2"
        />
      );
      
      expect(screen.queryByText('You are the Presenter')).not.toBeInTheDocument();
      expect(screen.getByText('Waiting for the host to start...')).toBeInTheDocument();
    });

    it('shows waiting message when no presenter assigned', () => {
      const players = [
        { id: 'player-1', name: 'My Name', score: 0, avatarSeed: 0.5 },
      ];
      render(
        <Lobby 
          {...defaultProps} 
          role="CLIENT" 
          players={players}
          currentPlayerId="player-1"
          presenterId={null}
        />
      );
      
      expect(screen.queryByText('You are the Presenter')).not.toBeInTheDocument();
      expect(screen.getByText('Waiting for the host to start...')).toBeInTheDocument();
    });
  });

  describe('presenter feature - edge cases', () => {
    it('handles undefined presenterId gracefully', () => {
      const players = [
        { id: 'player-1', name: 'Alice', score: 0, avatarSeed: 0.1 },
      ];
      
      // Should not throw
      render(
        <Lobby 
          {...defaultProps} 
          players={players} 
          hostParticipates={false}
          presenterId={undefined}
          onSetPresenter={vi.fn()}
        />
      );
      
      expect(screen.queryByText('PRESENTER')).not.toBeInTheDocument();
    });

    it('handles onSetPresenter being undefined', async () => {
      const user = userEvent.setup();
      const players = [
        { id: 'player-1', name: 'Alice', score: 0, avatarSeed: 0.1 },
      ];
      
      // Should not throw when clicking
      render(
        <Lobby 
          {...defaultProps} 
          players={players} 
          hostParticipates={false}
        />
      );
      
      // Click should not cause error
      await user.click(screen.getByText('Alice'));
      // No assertion needed - just testing it doesn't crash
    });

    it('presenter badge only appears once even with multiple players', () => {
      const players = [
        { id: 'player-1', name: 'Alice', score: 0, avatarSeed: 0.1 },
        { id: 'player-2', name: 'Bob', score: 0, avatarSeed: 0.2 },
        { id: 'player-3', name: 'Charlie', score: 0, avatarSeed: 0.3 },
      ];
      
      render(
        <Lobby 
          {...defaultProps} 
          players={players} 
          hostParticipates={false}
          presenterId="player-2"
          onSetPresenter={vi.fn()}
        />
      );
      
      const presenterBadges = screen.getAllByText('PRESENTER');
      expect(presenterBadges).toHaveLength(1);
    });

    it('updates presenter display when presenterId changes', () => {
      const players = [
        { id: 'player-1', name: 'Alice', score: 0, avatarSeed: 0.1 },
        { id: 'player-2', name: 'Bob', score: 0, avatarSeed: 0.2 },
      ];
      
      const { rerender } = render(
        <Lobby 
          {...defaultProps} 
          players={players} 
          hostParticipates={false}
          presenterId="player-1"
          onSetPresenter={vi.fn()}
        />
      );
      
      // Initially Alice is presenter
      expect(screen.getByText('PRESENTER')).toBeInTheDocument();
      
      // Change presenter to Bob
      rerender(
        <Lobby 
          {...defaultProps} 
          players={players} 
          hostParticipates={false}
          presenterId="player-2"
          onSetPresenter={vi.fn()}
        />
      );
      
      // Still exactly one presenter badge
      const presenterBadges = screen.getAllByText('PRESENTER');
      expect(presenterBadges).toHaveLength(1);
    });
  });
});
