import { fireEvent, render, screen, within } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import {
  CI_PR_QUEUE,
  CI_PR_QUEUE_REPOS,
} from './fixtures/ci-trains'
import { PrQueueTable } from './PrQueueTable'

vi.mock('./DeckTooltip', () => ({
  useDeckTooltip: (tipb: string, tips?: string) => ({
    'data-tipb': tipb,
    'data-tips': tips,
  }),
  DeckTooltipLayer: () => null,
}))

describe('PrQueueTable', () => {
  it('sorts by age descending by default and toggles deterministically', () => {
    render(<PrQueueTable prs={[...CI_PR_QUEUE].reverse()} repos={CI_PR_QUEUE_REPOS} allScope />)

    expect(Array.from(screen.getByRole('table', { name: 'PR queue' }).querySelectorAll('[data-pr-identity]'))
      .map((row) => (row as HTMLElement).dataset.prIdentity)).toEqual([
      'alexcodeplace/multideal#79',
      'alexcodeplace/overdeck#79',
      'alexcodeplace/multideal#74',
    ])

    fireEvent.click(screen.getByRole('button', { name: /Age/i }))
    fireEvent.click(screen.getByRole('button', { name: /Age/i }))
    expect(Array.from(screen.getByRole('table', { name: 'PR queue' }).querySelectorAll('[data-pr-identity]'))
      .map((row) => (row as HTMLElement).dataset.prIdentity)).toEqual([
      'alexcodeplace/multideal#74',
      'alexcodeplace/multideal#79',
      'alexcodeplace/overdeck#79',
    ])
  })

  it('renders a Repo column for all scope and omits it for single-repo scope', () => {
    const { rerender } = render(
      <PrQueueTable prs={CI_PR_QUEUE} repos={CI_PR_QUEUE_REPOS} allScope />,
    )
    expect(screen.getByRole('columnheader', { name: 'Repo' })).not.toHaveClass('hidden')
    expect(screen.getAllByRole('cell', { name: 'alexcodeplace/multideal' })).toHaveLength(2)
    expect(screen.getByRole('cell', { name: 'alexcodeplace/overdeck' })).toBeInTheDocument()

    rerender(
      <PrQueueTable
        prs={CI_PR_QUEUE.filter((pr) => pr.repo === 'alexcodeplace/multideal')}
        repos={[CI_PR_QUEUE_REPOS[0]!]}
        allScope={false}
      />,
    )
    expect(screen.queryByRole('columnheader', { name: 'Repo' })).not.toBeInTheDocument()
    expect(screen.queryByRole('cell', { name: 'alexcodeplace/multideal' })).not.toBeInTheDocument()
  })

  it('opens the right checks in a single detail drawer for duplicate PR numbers', () => {
    render(<PrQueueTable prs={CI_PR_QUEUE} repos={CI_PR_QUEUE_REPOS} allScope />)
    const multideal = screen.getByRole('button', { name: 'Details for checks for alexcodeplace/multideal #79' })
    const overdeck = screen.getByRole('button', { name: 'Details for checks for alexcodeplace/overdeck #79' })

    fireEvent.click(overdeck)

    expect(multideal).toHaveAttribute('aria-expanded', 'false')
    expect(overdeck).toHaveAttribute('aria-expanded', 'true')
    const drawer = screen.getByRole('dialog')
    expect(within(drawer).getByText('Browser build')).toBeInTheDocument()
    expect(within(drawer).queryByText('Unit tests')).not.toBeInTheDocument()
  })

  it('renders conflict, train membership, stale, and unknown states honestly', () => {
    render(<PrQueueTable prs={CI_PR_QUEUE} repos={CI_PR_QUEUE_REPOS} allScope />)

    const conflictRow = screen.getByTestId('pr-queue-row-alexcodeplace-multideal-79').closest('tr')!
    expect(within(conflictRow).getByText('conflicting')).toBeInTheDocument()
    expect(within(conflictRow).getByText('train #901')).toBeInTheDocument()
    expect(within(conflictRow).getByText('needs rebase')).toBeInTheDocument()

    const unknownRow = screen.getByTestId('pr-queue-row-alexcodeplace-overdeck-79').closest('tr')!
    expect(within(unknownRow).getByText('mergeability unknown')).toBeInTheDocument()
    expect(within(unknownRow).getByText('rebase unknown')).toBeInTheDocument()
    expect(within(unknownRow).queryByText('up to date')).not.toBeInTheDocument()

    const currentRow = screen.getByTestId('pr-queue-row-alexcodeplace-multideal-74').closest('tr')!
    expect(within(currentRow).getByText('up to date')).toBeInTheDocument()
    expect(within(currentRow).getByText('not in train')).toBeInTheDocument()
  })

  it('keeps returned checks visible but suppresses passing rollups when checks are incomplete', () => {
    render(<PrQueueTable prs={CI_PR_QUEUE} repos={CI_PR_QUEUE_REPOS} allScope />)
    const row = screen.getByTestId('pr-queue-row-alexcodeplace-overdeck-79').closest('tr')!

    expect(within(row).getByText('checks incomplete')).toBeInTheDocument()
    expect(within(row).queryByText('checks passing')).not.toBeInTheDocument()
    fireEvent.click(within(row).getByRole('button', { name: 'Details for checks for alexcodeplace/overdeck #79' }))
    expect(screen.getByText('Browser build')).toBeInTheDocument()
    expect(screen.getByText('pass')).toBeInTheDocument()
  })

  it('renders complete failed, pending, and passing check rollups', () => {
    render(<PrQueueTable prs={CI_PR_QUEUE} repos={CI_PR_QUEUE_REPOS} allScope />)
    expect(screen.getByText('checks failed')).toBeInTheDocument()
    expect(screen.getByText('checks pending')).toBeInTheDocument()
    expect(screen.queryByText('checks passing')).not.toBeInTheDocument()

    const passing = {
      ...CI_PR_QUEUE[0]!,
      number: 80,
      mergeable: 'MERGEABLE' as const,
      staleVsMain: false,
      checks: [{ name: 'Unit', bucket: 'pass' as const }],
    }
    render(<PrQueueTable prs={[passing]} repos={[CI_PR_QUEUE_REPOS[0]!]} allScope={false} />)
    expect(screen.getByText('checks passing')).toBeInTheDocument()
  })

  it('labels every incomplete repository while retaining its rows', () => {
    render(
      <PrQueueTable
        prs={CI_PR_QUEUE}
        repos={CI_PR_QUEUE_REPOS.map((repo) => ({ ...repo, prsComplete: false }))}
        allScope
      />,
    )
    expect(screen.getByText('alexcodeplace/multideal: Open PR list truncated')).toBeInTheDocument()
    expect(screen.getByText('alexcodeplace/overdeck: Open PR list truncated')).toBeInTheDocument()
    expect(within(screen.getByTestId('pr-queue-row-alexcodeplace-multideal-79')).getByText('PR list may be incomplete')).toBeInTheDocument()
    expect(within(screen.getByTestId('pr-queue-row-alexcodeplace-overdeck-79')).getByText('PR list may be incomplete')).toBeInTheDocument()
    expect(screen.getByRole('table', { name: 'PR queue' }).querySelectorAll('[data-pr-identity]')).toHaveLength(3)
  })

  it('resets selection and sorts deterministically when a PR head OID changes', () => {
    const original = CI_PR_QUEUE[0]!
    const replacement = {
      ...original,
      headRefOid: 'oid-multideal-79-b',
      checks: [{ name: 'Replacement check', bucket: 'pending' as const }],
    }
    const { rerender } = render(
      <PrQueueTable prs={[original]} repos={[CI_PR_QUEUE_REPOS[0]!]} allScope={false} />,
    )
    fireEvent.click(screen.getByRole('button', { name: 'Details for checks for #79' }))
    expect(screen.getByText('Unit tests')).toBeInTheDocument()

    rerender(<PrQueueTable prs={[replacement]} repos={[CI_PR_QUEUE_REPOS[0]!]} allScope={false} />)
    expect(screen.getByRole('button', { name: 'Details for checks for #79' })).toHaveAttribute('aria-expanded', 'false')
    expect(screen.queryByText('Replacement check')).not.toBeInTheDocument()

    render(
      <PrQueueTable
        prs={[replacement, original]}
        repos={[CI_PR_QUEUE_REPOS[0]!]}
        allScope={false}
      />,
    )
    expect(Array.from(screen.getAllByRole('table', { name: 'PR queue' })[1]!.querySelectorAll('[data-pr-oid]'))
      .map((row) => (row as HTMLElement).dataset.prOid)).toEqual([
      'oid-multideal-79-a',
      'oid-multideal-79-b',
    ])
  })

  it('distinguishes complete empty scope from incomplete empty scope', () => {
    const { rerender } = render(
      <PrQueueTable prs={[]} repos={CI_PR_QUEUE_REPOS} allScope />,
    )
    expect(screen.getByText('No open PRs')).toBeInTheDocument()

    rerender(
      <PrQueueTable
        prs={[]}
        repos={[{ repo: 'alexcodeplace/multideal', prsComplete: false }]}
        allScope={false}
      />,
    )
    expect(screen.getByText('alexcodeplace/multideal: Open PR list truncated')).toBeInTheDocument()
    expect(screen.queryByText('No open PRs')).not.toBeInTheDocument()
  })
})
