import { fireEvent, render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import {
  CI_TRAIN,
  CI_TRAIN_REPO_COMPLETENESS,
  CI_TRAIN_SECONDARY,
} from './fixtures/ci-trains'
import { TrainRail, type TrainRepoCompleteness } from './TrainRail'

vi.mock('./DeckTooltip', async (importOriginal) => {
  const original = await importOriginal<typeof import('./DeckTooltip')>()
  return {
    ...original,
    DeckTooltipLayer: () => <div data-testid="deck-tooltip-layer" />,
  }
})

describe('TrainRail', () => {
  it('uses the canonical total order and excludes merged trains', () => {
    const branchOnly = {
      ...CI_TRAIN,
      prNumber: null,
      prUrl: null,
      members: [],
      state: 'building' as const,
      gateRun: null,
      blockedBy: null,
      infraFlag: null,
    }
    render(
      <TrainRail
        trains={[
          { ...branchOnly, branch: 'feature/not-a-train', createdAt: '2026-07-19T12:00:00.000Z' },
          { ...branchOnly, branch: 'integration/batch-train-2', createdAt: null },
          { ...CI_TRAIN_SECONDARY, branch: 'integration/batch-train-12', createdAt: CI_TRAIN.createdAt },
          { ...CI_TRAIN, state: 'merged' },
          { ...branchOnly, branch: 'integration/batch-train-10', createdAt: null },
        ]}
        repos={CI_TRAIN_REPO_COMPLETENESS}
        onAction={() => undefined}
        onOpen={() => undefined}
      />,
    )

    expect(screen.getAllByTestId('train-card').map((card) => card.dataset.branch)).toEqual([
      'integration/batch-train-12',
      'integration/batch-train-10',
      'integration/batch-train-2',
      'feature/not-a-train',
    ])
  })

  it('uses repo ascending only after all train keys tie', () => {
    const tied = { ...CI_TRAIN, prNumber: null, prUrl: null, members: [], gateRun: null }
    render(
      <TrainRail
        trains={[
          { ...tied, repo: 'z/repo' },
          { ...tied, repo: 'a/repo' },
        ]}
        repos={[]}
        onAction={() => undefined}
        onOpen={() => undefined}
      />,
    )
    expect(screen.getAllByTestId('train-card').map((card) => card.dataset.repo)).toEqual([
      'a/repo',
      'z/repo',
    ])
  })

  it('renders each scoped repository completeness gap even with returned trains', () => {
    const repos: TrainRepoCompleteness[] = [
      {
        ...CI_TRAIN_REPO_COMPLETENESS[0]!,
        refsComplete: false,
        trainsComplete: false,
      },
      {
        ...CI_TRAIN_REPO_COMPLETENESS[1]!,
        prsComplete: false,
        trainsComplete: false,
      },
      {
        repo: 'alexcodeplace/platform',
        queueComplete: true,
        historyComplete: true,
        runnersComplete: true,
        refsComplete: true,
        prsComplete: true,
        trainRunsComplete: false,
        trainsComplete: false,
      },
    ]
    render(
      <TrainRail
        trains={[CI_TRAIN]}
        repos={repos}
        onAction={() => undefined}
        onOpen={() => undefined}
      />,
    )
    expect(screen.getByText('alexcodeplace/multideal: Train branch list truncated')).toBeInTheDocument()
    expect(screen.getByText('alexcodeplace/overdeck: Open PR list truncated')).toBeInTheDocument()
    expect(screen.getByText('alexcodeplace/platform: Train run history truncated')).toBeInTheDocument()
  })

  it('never renders the complete empty state for a zero-return truncated-ref repository', () => {
    render(
      <TrainRail
        trains={[]}
        repos={[{ ...CI_TRAIN_REPO_COMPLETENESS[0]!, refsComplete: false, trainsComplete: false }]}
        onAction={() => undefined}
        onOpen={() => undefined}
      />,
    )
    expect(screen.getByText('alexcodeplace/multideal: Train branch list truncated')).toBeInTheDocument()
    expect(screen.queryByText(/No trains/)).not.toBeInTheDocument()
  })

  it('renders the exact complete empty state', () => {
    render(
      <TrainRail
        trains={[]}
        repos={CI_TRAIN_REPO_COMPLETENESS}
        onAction={() => undefined}
        onOpen={() => undefined}
      />,
    )
    expect(screen.getByText('No trains. Queue >2 green-intent PRs? → assemble a train')).toBeInTheDocument()
  })

  it('renders exactly one tooltip layer per rail composition', () => {
    render(
      <TrainRail
        trains={[CI_TRAIN, CI_TRAIN_SECONDARY]}
        repos={CI_TRAIN_REPO_COMPLETENESS}
        onAction={() => undefined}
        onOpen={() => undefined}
      />,
    )
    expect(screen.getAllByTestId('deck-tooltip-layer')).toHaveLength(1)
  })

  it('keeps duplicate PR numbers scoped to each train repo for links and mutations', () => {
    const onAction = vi.fn()
    render(
      <TrainRail
        trains={[
          { ...CI_TRAIN, members: [79] },
          { ...CI_TRAIN, repo: 'alexcodeplace/overdeck', members: [79] },
        ]}
        repos={CI_TRAIN_REPO_COMPLETENESS}
        onAction={onAction}
        onOpen={() => undefined}
      />,
    )
    expect(screen.getAllByRole('link', { name: '#79' }).map((link) => link.getAttribute('href'))).toEqual([
      'https://github.com/alexcodeplace/multideal/pull/79',
      'https://github.com/alexcodeplace/overdeck/pull/79',
    ])
    fireEvent.click(screen.getAllByRole('button', { name: 'Rerun failed' })[1]!)
    expect(onAction).toHaveBeenCalledWith('ci.rerunFailed', {
      repo: 'alexcodeplace/overdeck',
      runId: '123456',
    })
  })
})
