import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { render, screen, cleanup, fireEvent } from '@testing-library/react'
import LocalSetup from '../LocalSetup.tsx'

const { mockNavigate, mockSetPlayerName, mockSetPlayer2Name, mockSetPlayer1Symbol, mockSetPlayer2Symbol, mockSetPlayerColor, mockSetPlayer2Color, mockSetPlayerDrawTower, mockSetPlayer2DrawTower } = vi.hoisted(() => ({
  mockNavigate: vi.fn(),
  mockSetPlayerName: vi.fn(),
  mockSetPlayer2Name: vi.fn(),
  mockSetPlayer1Symbol: vi.fn(),
  mockSetPlayer2Symbol: vi.fn(),
  mockSetPlayerColor: vi.fn(),
  mockSetPlayer2Color: vi.fn(),
  mockSetPlayerDrawTower: vi.fn(),
  mockSetPlayer2DrawTower: vi.fn(),
}))

const mockLocation = { state: { rules: { swap2: true, noOverlines: true, boardSize: 15 } } }
vi.mock('react-router-dom', () => ({
  useNavigate: () => mockNavigate,
  useLocation: () => mockLocation,
}))

vi.mock('../../theme.ts', () => ({
  theme: {
    bgStr: '#0d1117',
    text: '#e6edf3',
    textMuted: '#8b949e',
    bgPanel: 'rgba(22,27,34,0.92)',
    border: '#21262d',
    player1Str: '#58a6ff',
    player2Str: '#f0883e',
  },
  THEMES: { midnight: { label: 'Midnight', emoji: '🌙' } },
  useThemeMode: vi.fn(() => ['midnight', vi.fn()]),
  getPlayerName: vi.fn(() => ''),
  getPlayer2Name: vi.fn(() => ''),
  setPlayerName: mockSetPlayerName,
  setPlayer2Name: mockSetPlayer2Name,
  getPlayer1Symbol: vi.fn(() => 'X'),
  getPlayer2Symbol: vi.fn(() => 'O'),
  setPlayer1Symbol: mockSetPlayer1Symbol,
  setPlayer2Symbol: mockSetPlayer2Symbol,
  getPlayerColor: vi.fn(() => null),
  setPlayerColor: mockSetPlayerColor,
  getPlayer2Color: vi.fn(() => null),
  setPlayer2Color: mockSetPlayer2Color,
  getPlayerDrawTower: vi.fn(() => true),
  setPlayerDrawTower: mockSetPlayerDrawTower,
  getPlayer2DrawTower: vi.fn(() => false),
  setPlayer2DrawTower: mockSetPlayer2DrawTower,
}))

vi.mock('../../components/ThemeSelector.tsx', () => ({
  default: () => null,
}))

vi.mock('../../components/SymbolPicker.tsx', () => ({
  default: ({ current, onSelect }: { current: string; onSelect: (s: string) => void }) => (
    <button data-testid="symbol-picker" data-current={current} onClick={() => onSelect('★')}>
      Pick Symbol
    </button>
  ),
}))

afterEach(cleanup)
beforeEach(() => {
  vi.clearAllMocks()
})

describe('LocalSetup page', () => {
  it('renders the title', () => {
    render(<LocalSetup />)
    expect(screen.getByText('Local Game Setup')).toBeInTheDocument()
  })

  it('renders Player 1 and Player 2 labels', () => {
    render(<LocalSetup />)
    expect(screen.getByText('Player 1')).toBeInTheDocument()
    expect(screen.getByText('Player 2')).toBeInTheDocument()
  })

  it('renders two name inputs', () => {
    render(<LocalSetup />)
    const inputs = screen.getAllByPlaceholderText('Enter name')
    expect(inputs).toHaveLength(2)
  })

  it('renders symbol buttons for both players', () => {
    render(<LocalSetup />)
    const symbols = screen.getAllByText(/^[XO]$/)
    expect(symbols).toHaveLength(2)
  })

  it('renders Start Game button', () => {
    render(<LocalSetup />)
    expect(screen.getByText('Start Game')).toBeInTheDocument()
  })

  it('renders Back button', () => {
    render(<LocalSetup />)
    expect(screen.getByText('← Back to menu')).toBeInTheDocument()
  })

  it('navigates back on Back click', () => {
    render(<LocalSetup />)
    fireEvent.click(screen.getByText('← Back to menu'))
    expect(mockNavigate).toHaveBeenCalledWith('/')
  })

  it('starts game with default names when inputs are empty', () => {
    render(<LocalSetup />)
    fireEvent.click(screen.getByText('Start Game'))
    expect(mockSetPlayerName).toHaveBeenCalledWith('Caro 1')
    expect(mockSetPlayer2Name).toHaveBeenCalledWith('Caro 2')
    expect(mockSetPlayer1Symbol).toHaveBeenCalledWith('X')
    expect(mockSetPlayer2Symbol).toHaveBeenCalledWith('O')
    expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
      state: {
        player1Name: 'Caro 1',
        player2Name: 'Caro 2',
        player1Symbol: 'X',
        player2Symbol: 'O',
        player1Type: 'human',
        player2Type: 'bot',
        botProfile: 'normal',
        opponentType: 'taunt',
        tauntStyle: 'funny',
        rules: { swap2: true, noOverlines: true, boardSize: 15 },
      },
    })
  })

  it('starts game with custom names', () => {
    render(<LocalSetup />)
    const inputs = screen.getAllByPlaceholderText('Enter name')
    fireEvent.change(inputs[0], { target: { value: 'Alice' } })
    fireEvent.change(inputs[1], { target: { value: 'Bob' } })
    fireEvent.click(screen.getByText('Start Game'))
    expect(mockSetPlayerName).toHaveBeenCalledWith('Alice')
    expect(mockSetPlayer2Name).toHaveBeenCalledWith('Bob')
    expect(mockSetPlayer1Symbol).toHaveBeenCalledWith('X')
    expect(mockSetPlayer2Symbol).toHaveBeenCalledWith('O')
    expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
      state: {
        player1Name: 'Alice',
        player2Name: 'Bob',
        player1Symbol: 'X',
        player2Symbol: 'O',
        player1Type: 'human',
        player2Type: 'bot',
        botProfile: 'normal',
        opponentType: 'taunt',
        tauntStyle: 'funny',
        rules: { swap2: true, noOverlines: true, boardSize: 15 },
      },
    })
  })

  it('opens symbol picker for Player 1', () => {
    render(<LocalSetup />)
    const symBtns = screen.getAllByText(/^[XO]$/)
    fireEvent.click(symBtns[0])
    expect(screen.getByTestId('symbol-picker')).toBeInTheDocument()
  })

  it('changes Player 1 symbol via picker', () => {
    render(<LocalSetup />)
    const symBtns = screen.getAllByText(/^[XO]$/)
    fireEvent.click(symBtns[0])
    fireEvent.click(screen.getByText('Pick Symbol'))
    expect(screen.getByText('★')).toBeInTheDocument()
  })

  it('opens symbol picker for Player 2', () => {
    render(<LocalSetup />)
    const symBtns = screen.getAllByText(/^[XO]$/)
    fireEvent.click(symBtns[1])
    expect(screen.getByTestId('symbol-picker')).toBeInTheDocument()
  })

  it('changes Player 2 symbol via picker', () => {
    render(<LocalSetup />)
    const symBtns = screen.getAllByText(/^[XO]$/)
    fireEvent.click(symBtns[1])
    fireEvent.click(screen.getByText('Pick Symbol'))
    expect(screen.getAllByText('★')).toHaveLength(1)
  })

  it('renders theme selector button', () => {
    render(<LocalSetup />)
    expect(screen.getByText('🌙')).toBeInTheDocument()
  })

  it('lets players choose human or bot type', () => {
    render(<LocalSetup />)
    expect(screen.getAllByText('Human')).toHaveLength(2)
    expect(screen.getAllByText('Bot')).toHaveLength(2)
    expect(screen.getByText('Bot vs Bot')).toBeInTheDocument()
  })

  it('renders read-only rules summary', () => {
    render(<LocalSetup />)
    expect(screen.getByText('GAME RULES')).toBeInTheDocument()
    expect(screen.getByText('Swap2 Enabled')).toBeInTheDocument()
    expect(screen.getByText('No overline (6+)')).toBeInTheDocument()
    expect(screen.getByText('Board 15×15')).toBeInTheDocument()
  })

  it('sets the bot profile for local bot games', () => {
    render(<LocalSetup />)
    expect(screen.getByText('Difficulty')).toBeInTheDocument()
    fireEvent.click(screen.getByText('Expert'))
    fireEvent.click(screen.getByText('Start Game'))

    expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
      state: {
        player1Name: 'Caro 1',
        player2Name: 'Caro 2',
        player1Symbol: 'X',
        player2Symbol: 'O',
        player1Type: 'human',
        player2Type: 'bot',
        botProfile: 'expert',
        opponentType: 'taunt',
        tauntStyle: 'funny',
        rules: { swap2: true, noOverlines: true, boardSize: 15 },
      },
    })
  })

  it('sets up bot vs bot defaults', () => {
    render(<LocalSetup />)
    fireEvent.click(screen.getByText('Bot vs Bot'))
    fireEvent.click(screen.getByText('Start Game'))

    expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
      state: {
        player1Name: 'Bot 1',
        player2Name: 'Bot 2',
        player1Symbol: 'X',
        player2Symbol: 'O',
        player1Type: 'bot',
        player2Type: 'bot',
        botProfile: 'normal',
        opponentType: 'taunt',
        tauntStyle: 'funny',
        rules: { swap2: true, noOverlines: true, boardSize: 15 },
      },
    })
  })

  it('allows choosing opponent type and personality style', () => {
    render(<LocalSetup />)
    expect(screen.getByText('Opponent Type')).toBeInTheDocument()
    fireEvent.click(screen.getByText(/streamer/i))
    expect(screen.queryByText('Personality')).not.toBeInTheDocument()

    fireEvent.click(screen.getByText(/taunt/i))
    expect(screen.getByText('Personality')).toBeInTheDocument()
    fireEvent.click(screen.getByText(/lover/i))

    fireEvent.click(screen.getByText('Start Game'))
    expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
      state: {
        player1Name: 'Caro 1',
        player2Name: 'Caro 2',
        player1Symbol: 'X',
        player2Symbol: 'O',
        player1Type: 'human',
        player2Type: 'bot',
        botProfile: 'normal',
        opponentType: 'taunt',
        tauntStyle: 'lover',
        rules: { swap2: true, noOverlines: true, boardSize: 15 },
      },
    })
  })

  it('swaps player configurations including display tower states', () => {
    render(<LocalSetup />)
    const inputs = screen.getAllByPlaceholderText('Enter name')
    fireEvent.change(inputs[0], { target: { value: 'Alice' } })
    fireEvent.change(inputs[1], { target: { value: 'Bob' } })

    const swapBtn = screen.getByTitle('Swap Players')
    fireEvent.click(swapBtn)

    expect(inputs[0]).toHaveValue('Bob')
    expect(inputs[1]).toHaveValue('Alice')

    fireEvent.click(screen.getByText('Start Game'))
    expect(mockSetPlayerDrawTower).toHaveBeenCalledWith(false)
    expect(mockSetPlayer2DrawTower).toHaveBeenCalledWith(true)
  })

  it('selects colors and starts game with selected colors', () => {
    render(<LocalSetup />)
    
    const buttons = screen.getAllByRole('button')
    const p1RedBtn = buttons.find(b => b.getAttribute('title') === '#ef4444')
    expect(p1RedBtn).toBeDefined()
    if (p1RedBtn) fireEvent.click(p1RedBtn)

    fireEvent.click(screen.getByText('Start Game'))
    
    expect(mockSetPlayerColor).toHaveBeenCalledWith('#ef4444')
    expect(mockSetPlayer2Color).toHaveBeenCalledWith('#f0883e')
  })

  it('saves selected tower preferences on start game', () => {
    render(<LocalSetup />)
    fireEvent.click(screen.getByText('Start Game'))
    expect(mockSetPlayerDrawTower).toHaveBeenCalledWith(true)
    expect(mockSetPlayer2DrawTower).toHaveBeenCalledWith(false)
  })
})
