import { LaneBar } from './LaneBar'
import type { TrainJob } from './LaneSegment'
import { StatusChip } from './StatusChip'

export type TrainState = 'building' | 'gating' | 'failed' | 'rerunning' | 'green' | 'merged'

export interface TrainGateRun {
  id: number
  attempt: number
  status: string
  conclusion: string | null
  jobsComplete: boolean
  jobs: TrainJob[]
}

export interface TrainViewModel {
  repo: string
  prNumber: number | null
  prUrl: string | null
  branch: string
  createdAt: string | null
  members: number[]
  state: TrainState | null
  stateGap: string | null
  gateRun: TrainGateRun | null
  blockedBy: string | null
  infraFlag: boolean | null
  refsComplete: boolean
  prsComplete: boolean
  trainRunsComplete: boolean
  trainsComplete: boolean
}

export interface TrainCardProps {
  train: TrainViewModel
  onAction: (verb: string, args: Record<string, string>) => void
  onOpen: (url: string) => void
}

const STATE_CHIP_CLASS: Record<TrainState, string> = {
  building:
    '[&_[data-testid=status-chip]]:border-[var(--mod-color-train-building)] [&_[data-testid=status-chip]]:text-[var(--mod-color-train-building)]',
  gating:
    '[&_[data-testid=status-chip]]:border-[var(--mod-color-train-gating)] [&_[data-testid=status-chip]]:text-[var(--mod-color-train-gating)]',
  failed:
    '[&_[data-testid=status-chip]]:border-[var(--mod-color-train-failed)] [&_[data-testid=status-chip]]:text-[var(--mod-color-train-failed)]',
  rerunning:
    '[&_[data-testid=status-chip]]:border-[var(--mod-color-train-rerunning)] [&_[data-testid=status-chip]]:text-[var(--mod-color-train-rerunning)]',
  green:
    '[&_[data-testid=status-chip]]:border-[var(--mod-color-train-green)] [&_[data-testid=status-chip]]:text-[var(--mod-color-train-green)]',
  merged:
    '[&_[data-testid=status-chip]]:border-[var(--mod-color-train-merged)] [&_[data-testid=status-chip]]:text-[var(--mod-color-train-merged)]',
}

function completenessGap(train: TrainViewModel): string | null {
  if (!train.refsComplete) return 'train refs incomplete'
  if (!train.prsComplete) return 'PRs incomplete'
  if (!train.trainRunsComplete) return 'train runs incomplete'
  if (!train.trainsComplete) return 'train data incomplete'
  return null
}

function validGitHubUrl(value: string | null): value is string {
  if (!value) return false
  try {
    const url = new URL(value)
    return (
      url.protocol === 'https:' &&
      url.hostname === 'github.com' &&
      url.username === '' &&
      url.password === ''
    )
  } catch {
    return false
  }
}

function failureClassification(infraFlag: boolean | null): string {
  if (infraFlag === true) return 'infra, rerunnable'
  if (infraFlag === false) return 'code failure'
  return 'failure classification unknown'
}

function stateChipClass(state: TrainState | null, gap: string | null): string {
  if (gap) {
    return '[&_[data-testid=status-chip]]:border-warning/40 [&_[data-testid=status-chip]]:bg-warning/10 [&_[data-testid=status-chip]]:text-warning'
  }
  if (!state) return ''
  return STATE_CHIP_CLASS[state]
}

export function TrainCard({ train, onAction, onOpen }: TrainCardProps) {
  const gap = completenessGap(train)
  const actionsAllowed =
    train.refsComplete &&
    train.prsComplete &&
    train.trainRunsComplete &&
    train.trainsComplete
  const jobsComplete = train.gateRun?.jobsComplete
  const canRerun = actionsAllowed && train.state === 'failed' && train.gateRun !== null
  const canCancel =
    actionsAllowed &&
    (train.state === 'gating' || train.state === 'rerunning') &&
    train.gateRun !== null
  const openUrl = actionsAllowed && validGitHubUrl(train.prUrl) ? train.prUrl : null

  const runAction = (verb: 'ci.rerunFailed' | 'ci.cancelRun') => {
    if (!train.gateRun) return
    onAction(verb, { repo: train.repo, runId: String(train.gateRun.id) })
  }

  return (
    <article
      data-testid="train-card"
      data-branch={train.branch}
      data-repo={train.repo}
      className="min-w-80 rounded-xl border border-border bg-surface p-4"
    >
      <header className="flex items-start justify-between gap-3">
        <span className="min-w-0">
          <strong className="block truncate text-sm text-fg">{train.branch}</strong>
          <span className="block truncate text-xs text-fg-muted">{train.repo}</span>
        </span>
        <span className={stateChipClass(train.state, gap)}>
          <StatusChip status={gap ?? train.state ?? train.stateGap ?? 'state unknown'} />
        </span>
      </header>

      <div className="mt-3 flex flex-wrap gap-1" aria-label="Train members">
        {train.members.length === 0 ? (
          <span className="text-xs text-fg-muted">No recorded members</span>
        ) : (
          train.members.map((number) => (
            <a
              key={number}
              href={`https://github.com/${train.repo}/pull/${number}`}
              className="inline-flex min-h-11 items-center rounded-full border border-border bg-surface-raised px-3 text-xs font-semibold text-accent hover:border-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
            >
              #{number}
            </a>
          ))
        )}
      </div>

      {train.gateRun && (
        <div className="mt-3">
          <LaneBar jobs={train.gateRun.jobs} jobsComplete={train.gateRun.jobsComplete} />
        </div>
      )}

      {train.gateRun && jobsComplete === true && train.blockedBy && (
        <p className="mt-3 text-sm text-fg">{train.blockedBy}</p>
      )}
      {train.state === 'failed' && train.gateRun && jobsComplete === true && (
        <p className="mt-1 text-xs text-fg-muted">{failureClassification(train.infraFlag)}</p>
      )}

      {(canRerun || canCancel || openUrl !== null) && (
        <div className="mt-4 flex flex-wrap items-center gap-2">
          {canRerun && (
            <button
              type="button"
              onClick={() => runAction('ci.rerunFailed')}
              className="min-h-11 rounded-md bg-accent px-3 text-xs font-semibold text-on-accent hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
            >
              Rerun failed
            </button>
          )}
          {canRerun && jobsComplete === true && train.infraFlag === true && (
            <span className="text-xs font-semibold text-warning">recommended</span>
          )}
          {canCancel && (
            <button
              type="button"
              onClick={() => runAction('ci.cancelRun')}
              className="min-h-11 rounded-md border border-border px-3 text-xs font-semibold text-fg hover:border-border-strong hover:bg-surface-raised focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
            >
              Cancel superseded
            </button>
          )}
          {openUrl !== null && (
            <button
              type="button"
              onClick={() => onOpen(openUrl)}
              className="min-h-11 rounded-md border border-border px-3 text-xs font-semibold text-fg hover:border-border-strong hover:bg-surface-raised focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
            >
              Open
            </button>
          )}
        </div>
      )}
    </article>
  )
}
