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

function renderCard(train: TrainViewModel = CI_TRAIN) {
  const onAction = vi.fn()
  const onOpen = vi.fn()
  render(<TrainCard train={train} onAction={onAction} onOpen={onOpen} />)
  return { onAction, onOpen }
}

describe('TrainCard', () => {
  it('builds member links exclusively from the train repository', () => {
    renderCard({
      ...CI_TRAIN,
      repo: 'alexcodeplace/overdeck',
      members: [79, 74],
      prUrl: 'https://github.com/attacker/wrong/pull/1',
    })

    expect(screen.getByRole('link', { name: '#79' })).toHaveAttribute(
      'href',
      'https://github.com/alexcodeplace/overdeck/pull/79',
    )
    expect(screen.getByRole('link', { name: '#74' })).toHaveAttribute(
      'href',
      'https://github.com/alexcodeplace/overdeck/pull/74',
    )
  })

  it('reruns a complete failed gate with exact string mutation args', () => {
    const { onAction } = renderCard()
    fireEvent.click(screen.getByRole('button', { name: 'Rerun failed' }))
    expect(onAction).toHaveBeenCalledWith('ci.rerunFailed', {
      repo: 'alexcodeplace/multideal',
      runId: '123456',
    })
    expect(screen.getByText('recommended')).toBeInTheDocument()
    expect(screen.getByText('infra, rerunnable')).toBeInTheDocument()
  })

  it.each(['gating', 'rerunning'] as const)('cancels a complete %s gate', (state) => {
    const { onAction } = renderCard({ ...CI_TRAIN, state })
    fireEvent.click(screen.getByRole('button', { name: 'Cancel superseded' }))
    expect(onAction).toHaveBeenCalledWith('ci.cancelRun', {
      repo: 'alexcodeplace/multideal',
      runId: '123456',
    })
  })

  it('opens a valid GitHub pull URL without invoking a mutation', () => {
    const { onAction, onOpen } = renderCard()
    fireEvent.click(screen.getByRole('button', { name: 'Open' }))
    expect(onOpen).toHaveBeenCalledWith('https://github.com/alexcodeplace/multideal/pull/901')
    expect(onAction).not.toHaveBeenCalled()
  })

  it.each([
    'http://github.com/alexcodeplace/multideal/pull/901',
    'https://user:secret@github.com/alexcodeplace/multideal/pull/901',
    'https://github.example/alexcodeplace/multideal/pull/901',
    'not a URL',
  ])('omits Open for invalid URL %s', (prUrl) => {
    renderCard({ ...CI_TRAIN, prUrl })
    expect(screen.queryByRole('button', { name: 'Open' })).not.toBeInTheDocument()
  })

  it.each([
    ['refsComplete', 'train refs incomplete'],
    ['prsComplete', 'PRs incomplete'],
    ['trainRunsComplete', 'train runs incomplete'],
    ['trainsComplete', 'train data incomplete'],
  ] as const)('renders the %s gap and suppresses every action', (flag, gap) => {
    renderCard({ ...CI_TRAIN, [flag]: false })
    expect(screen.getByText(gap)).toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'Rerun failed' })).not.toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'Cancel superseded' })).not.toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'Open' })).not.toBeInTheDocument()
  })

  it('uses completeness precedence over a stale state gap', () => {
    renderCard({
      ...CI_TRAIN,
      refsComplete: false,
      prsComplete: false,
      trainRunsComplete: false,
      trainsComplete: false,
      state: null,
      stateGap: 'stale cached gap',
    })
    expect(screen.getByText('train refs incomplete')).toBeInTheDocument()
    expect(screen.queryByText('stale cached gap')).not.toBeInTheDocument()
  })

  it('renders nullable state gap and deterministic blocking line', () => {
    const { rerender } = render(
      <TrainCard
        train={{ ...CI_TRAIN, state: null, stateGap: 'run status unsupported' }}
        onAction={() => undefined}
        onOpen={() => undefined}
      />,
    )
    expect(screen.getByText('run status unsupported')).toBeInTheDocument()

    rerender(<TrainCard train={CI_TRAIN} onAction={() => undefined} onOpen={() => undefined} />)
    expect(screen.getByText('Integration database lanes — ENOTEMPTY (infra)')).toBeInTheDocument()
  })

  it('keeps incomplete jobs visible but suppresses blocking, classification, striping, and recommendation claims', () => {
    renderCard({
      ...CI_TRAIN,
      gateRun: { ...CI_TRAIN.gateRun!, jobsComplete: false },
    })

    expect(screen.getByText('jobs incomplete')).toBeInTheDocument()
    expect(screen.queryByText('Integration database lanes — ENOTEMPTY (infra)')).not.toBeInTheDocument()
    expect(screen.queryByText('recommended')).not.toBeInTheDocument()
    expect(screen.queryByText('infra, rerunnable')).not.toBeInTheDocument()
    expect(screen.queryByText('code failure')).not.toBeInTheDocument()
    expect(screen.queryByText('failure classification unknown')).not.toBeInTheDocument()
    expect(screen.getByLabelText('Integration database lanes')).not.toHaveAttribute(
      'data-infra-striped',
    )
    expect(screen.getByRole('button', { name: 'Rerun failed' })).toBeInTheDocument()
  })

  it('does not recommend an eligible rerun without exact infra classification', () => {
    renderCard({ ...CI_TRAIN, infraFlag: false })
    expect(screen.getByRole('button', { name: 'Rerun failed' })).toBeInTheDocument()
    expect(screen.queryByText('recommended')).not.toBeInTheDocument()
    expect(screen.getByText('code failure')).toBeInTheDocument()
  })

  it('labels null complete-job classification as unknown', () => {
    renderCard({ ...CI_TRAIN, infraFlag: null })
    expect(screen.getByText('failure classification unknown')).toBeInTheDocument()
  })

  it('renders actions independently from one another', () => {
    renderCard({ ...CI_TRAIN, state: 'green', gateRun: null })
    expect(screen.getByRole('button', { name: 'Open' })).toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'Rerun failed' })).not.toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'Cancel superseded' })).not.toBeInTheDocument()
  })
})
