import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { CommandPalette } from './CommandPalette'
import type { SearchResult } from './types'

const mockData: SearchResult[] = [
  {
    id: '1',
    category: 'exchange',
    title: 'order-intake exchange',
    meta: 'cmr-1234 · 142ms',
    timestamp: '2s ago',
    badges: [{ label: 'Completed' }],
  },
  {
    id: '2',
    category: 'route',
    title: 'content-based-routing',
    meta: 'Route · 3 processors',
    timestamp: '1m ago',
  },
  {
    id: '3',
    category: 'agent',
    title: 'agent-eu-west-1',
    meta: 'v2.4.1 · live',
    expandedContent: '{"status": "live"}',
  },
]

describe('CommandPalette', () => {
  it('renders nothing when closed', () => {
    render(
      <CommandPalette
        open={false}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
  })

  it('renders dialog when open', () => {
    render(
      <CommandPalette
        open={true}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    expect(screen.getByRole('dialog')).toBeInTheDocument()
  })

  it('shows all results when open with no query', () => {
    render(
      <CommandPalette
        open={true}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    expect(screen.getByText('order-intake exchange')).toBeInTheDocument()
    expect(screen.getByText('content-based-routing')).toBeInTheDocument()
  })

  it('filters results by search query', async () => {
    const user = userEvent.setup()
    render(
      <CommandPalette
        open={true}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    await user.type(screen.getByRole('textbox', { name: 'Search' }), 'routing')
    // The title is split into fragments by mark highlight, so use a partial text match
    expect(screen.getByText(/content-based/)).toBeInTheDocument()
    expect(screen.queryByText('order-intake exchange')).not.toBeInTheDocument()
  })

  it('calls onClose when Escape is pressed', async () => {
    const onClose = vi.fn()
    const user = userEvent.setup()
    render(
      <CommandPalette
        open={true}
        onClose={onClose}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    const input = screen.getByRole('textbox', { name: 'Search' })
    await user.click(input)
    await user.keyboard('{Escape}')
    expect(onClose).toHaveBeenCalled()
  })

  it('calls onClose when overlay is clicked', async () => {
    const onClose = vi.fn()
    const user = userEvent.setup()
    render(
      <CommandPalette
        open={true}
        onClose={onClose}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    await user.click(screen.getByTestId('command-palette-overlay'))
    expect(onClose).toHaveBeenCalled()
  })

  it('calls onSelect when a result is clicked', async () => {
    const onSelect = vi.fn()
    const onClose = vi.fn()
    const user = userEvent.setup()
    render(
      <CommandPalette
        open={true}
        onClose={onClose}
        onSelect={onSelect}
        data={mockData}
      />,
    )
    await user.click(screen.getByText('content-based-routing'))
    expect(onSelect).toHaveBeenCalledWith(mockData[1])
  })

  it('filters by category tab', async () => {
    const user = userEvent.setup()
    render(
      <CommandPalette
        open={true}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    await user.click(screen.getByRole('tab', { name: /Routes/i }))
    expect(screen.getByText('content-based-routing')).toBeInTheDocument()
    expect(screen.queryByText('order-intake exchange')).not.toBeInTheDocument()
  })

  it('shows category tabs with counts', () => {
    render(
      <CommandPalette
        open={true}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    // All tab should show total count
    expect(screen.getByRole('tab', { name: /All/i })).toBeInTheDocument()
  })

  it('navigates with arrow keys', async () => {
    const user = userEvent.setup()
    render(
      <CommandPalette
        open={true}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    const input = screen.getByRole('textbox', { name: 'Search' })
    await user.click(input)
    await user.keyboard('{ArrowDown}')
    // Second item gets focused
    const items = screen.getAllByRole('option')
    expect(items[1]).toHaveClass('focused')
  })

  it('selects focused item on Enter', async () => {
    const onSelect = vi.fn()
    const onClose = vi.fn()
    const user = userEvent.setup()
    render(
      <CommandPalette
        open={true}
        onClose={onClose}
        onSelect={onSelect}
        data={mockData}
      />,
    )
    const input = screen.getByRole('textbox', { name: 'Search' })
    await user.click(input)
    await user.keyboard('{Enter}')
    expect(onSelect).toHaveBeenCalledWith(mockData[0])
  })

  it('shows expandable detail toggle for items with expandedContent', () => {
    render(
      <CommandPalette
        open={true}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    expect(screen.getByRole('button', { name: 'Toggle detail' })).toBeInTheDocument()
  })

  it('expands detail when expand button is clicked', async () => {
    const user = userEvent.setup()
    render(
      <CommandPalette
        open={true}
        onClose={() => {}}
        onSelect={() => {}}
        data={mockData}
      />,
    )
    const expandBtn = screen.getByRole('button', { name: 'Toggle detail' })
    await user.click(expandBtn)
    expect(expandBtn).toHaveAttribute('aria-expanded', 'true')
  })
})
