import {
  AmplificationSpark,
  ClusterQueue,
  InboxItem,
  KpiTile,
  KvPanel,
  type KvRow,
  OffloadControl,
  PrQueueTable,
  RepoCiStrip,
  RemoteJobsTable,
  RunnerFleetStrip,
  SectionCard,
  StaleBadge,
  StatusChip,
  TrainRail,
  formatDurationMs,
} from '@overdeck/deck-ui'
import { DataTable, type DataTableColumn } from '@overdeck/deck-ui'
import { useDeckToast } from '../../lib/use-deck-toast'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { postCollectorAction } from '../../lib/action-client'
import { MachinesWidget } from '../machines/MachinesWidget'
export { buildOffloadActionArgs } from '../machines/MachinesWidget'
import { useCollectorItems, useCollectorState } from '../../lib/collector-queries'
import type { GhciPanelData, LandqRepoState, LandqTicketState } from '../../lib/panel-data'
import { deliveryLagLabel, parseCiDeliveryData, watcherFailureLabel } from '../../lib/ci-delivery-data'
import { ciKvRows } from '../../lib/page-mappers'
import { severityDotFor } from '../inbox/inbox-mappers'
import {
  buildItems,
  amplificationProps,
  clusterQueueFromPanel,
  fleetFromPanel,
  hostChipSummary,
  landqStateFromPanel,
  offloadControlFromPanel,
  incidentAlertActions,
  offloadKpiTiles,
  panelById,
  prQueueItems,
  prQueueRepoCompleteness,
  remoteJobsFromPanel,
  repoCiSummaries,
  runnerFleetFromPanel,
  stalledQueueItem,
  trainModels,
  trainRepoCompleteness,
} from './ci-mappers'

export function CiContent() {
  return <CiContentInner />
}

function CiContentInner() {
  const { toast } = useDeckToast()
  const stateQuery = useCollectorState()
  const itemsQuery = useCollectorItems('build')

  const panels = stateQuery.data?.panels ?? []
  const adapters = stateQuery.data?.adapters ?? []
  const buildKindItems = itemsQuery.data?.items ?? buildItems([])

  const control = useMemo(() => offloadControlFromPanel(panels), [panels])
  const queue = useMemo(() => clusterQueueFromPanel(panels), [panels])
  const fleet = useMemo(() => fleetFromPanel(panels), [panels])
  const jobs = useMemo(() => remoteJobsFromPanel(panels), [panels])
  const kpis = useMemo(() => offloadKpiTiles(queue, buildKindItems), [queue, buildKindItems])
  const stalled = useMemo(() => stalledQueueItem(buildKindItems), [buildKindItems])
  const ciData = panelById<GhciPanelData>(panels, 'ci')
  const offloadStatus = adapters.find((adapter) => adapter.id === 'offload')
  const deliveryPanel = panels.find((panel) => panel.id === 'deploy-status')
  const deliveryAdapter = adapters.find((adapter) => adapter.id === 'deploy-status')
  const delivery = useMemo(() => parseCiDeliveryData(
    deliveryPanel?.data,
    deliveryAdapter?.stale ? 'stale' : deliveryPanel ? 'fresh' : 'unknown',
  ), [deliveryAdapter?.stale, deliveryPanel])

  const [selectedRepo, setSelectedRepo] = useState<string | null>(null)
  const repos = useMemo(() => ciData?.repos ?? [], [ciData])
  const scopedRepos = useMemo(() => selectedRepo === null ? repos : repos.filter((repo) => repo.repo === selectedRepo), [repos, selectedRepo])
  const repoSummaries = useMemo(() => repoCiSummaries(ciData ?? { repos: [], runners: [], runnersComplete: false, oldestQueuedAgeH: null }), [ciData])
  const trains = useMemo(() => trainModels(scopedRepos), [scopedRepos])
  const trainRepos = useMemo(() => trainRepoCompleteness(scopedRepos), [scopedRepos])
  const prs = useMemo(() => prQueueItems(scopedRepos), [scopedRepos])
  const prRepos = useMemo(() => prQueueRepoCompleteness(scopedRepos), [scopedRepos])
  const runnerFleet = useMemo(() => runnerFleetFromPanel(ciData), [ciData])
  const hostChips = useMemo(() => hostChipSummary(fleet, control), [fleet, control])
  const landqState = useMemo(() => landqStateFromPanel(panels), [panels])

  useEffect(() => {
    if (selectedRepo !== null && !repos.some((repo) => repo.repo === selectedRepo)) setSelectedRepo(null)
  }, [repos, selectedRepo])

  const onTrainAction = useCallback((verb: string, args: Record<string, string>) => {
    const label = verb === 'ci.rerunFailed' ? 'Rerun failed' : 'Cancel superseded'
    void postCollectorAction(verb, { args, requestedBy: 'overdeck-web' })
      .then((response) => {
        toast({
          title: `${label} succeeded`,
          description: typeof response.result === 'string' ? response.result : undefined,
          tone: 'success',
        })
      })
      .catch((error: unknown) => {
        toast({
          title: `${label} failed`,
          description: error instanceof Error ? error.message : String(error),
          tone: 'danger',
        })
      })
  }, [toast])

  const onTrainOpen = useCallback((value: string) => {
    try {
      const url = new URL(value)
      if (
        url.protocol !== 'https:'
        || url.hostname !== 'github.com'
        || url.username !== ''
        || url.password !== ''
      ) throw new Error('invalid')
      window.location.assign(value)
    } catch {
      toast({ title: 'Open failed', description: 'Invalid GitHub URL', tone: 'danger' })
    }
  }, [toast])

  if (stateQuery.isLoading) return <div className="text-fg-muted">Loading CI & Build…</div>

  return (
    <div className="flex flex-col gap-3.5" data-testid="ci-build-page">
      <SectionCard title="Delivery" titleBadge={deliveryAdapter ? <StaleBadge status={deliveryAdapter} /> : undefined}>
        <div className="flex flex-wrap items-start justify-between gap-3" data-testid="ci-delivery-state">
          <div className="min-w-0">
            <div className="flex flex-wrap items-center gap-2">
              <StatusChip status={delivery.status} />
              {delivery.current?.operation ? <span className="font-mono text-xs text-fg">{delivery.current.operation}</span> : null}
              {delivery.current?.holder ? <span className="text-xs text-fg-muted">on {delivery.current.holder}</span> : null}
            </div>
            {delivery.reason ? <p className="mt-2 text-xs text-danger">{delivery.reason}</p> : null}
            {delivery.latestEvent?.detail && delivery.latestEvent.detail !== delivery.reason
              ? <p className="mt-1 text-xs text-fg-muted">{delivery.latestEvent.detail}</p>
              : null}
            <p className="mt-2 text-xs text-fg-muted" data-testid="ci-delivery-lag">
              {deliveryLagLabel(delivery.identity)}
              {delivery.identity.servedSha ? <span className="ml-1 font-mono">({delivery.identity.servedSha.slice(0, 7)})</span> : null}
            </p>
            {watcherFailureLabel(delivery.watcher) ? (
              <p className="mt-1 text-xs text-danger" data-testid="ci-delivery-watcher-failure">
                {watcherFailureLabel(delivery.watcher)}
              </p>
            ) : null}
          </div>
          <div className="text-right text-xs text-fg-muted">
            <div>{delivery.queue ? `${delivery.queue.depth} queued` : 'Queue not recorded'}</div>
            <div className="mt-1">Next action not recorded</div>
            <div className="mt-1">Exact wait not recorded</div>
            <div className="mt-1">Lease owner not recorded</div>
            <div className="mt-1">Remote node not recorded</div>
            <div className="mt-1">Last receipt not recorded</div>
            <div className="mt-1">Active and blocked time not recorded</div>
            <div className="mt-1">Safe retire/resume state not recorded</div>
          </div>
        </div>
      </SectionCard>

      <RunnerFleetStrip {...runnerFleet} />
      <RepoCiStrip repos={repoSummaries} selectedRepo={selectedRepo} onScope={setSelectedRepo} />
      <TrainRail trains={trains} repos={trainRepos} onAction={onTrainAction} onOpen={onTrainOpen} />
      <LocalLandQueuePanel data={landqState?.repos} />
      <PrQueueTable prs={prs} repos={prRepos} allScope={selectedRepo === null} />
      {scopedRepos.map((repo) => (
        <AmplificationSpark key={repo.repo} {...amplificationProps(repo)} />
      ))}

      <div className="flex flex-col gap-3.5" data-testid="ci-build-offload">
      <div className="flex flex-wrap items-center gap-3.5">
        {control ? (
          <span className="inline-flex items-center gap-[7px] rounded-[20px] border border-border bg-surface px-[11px] py-1 text-[11.5px] text-fg-muted">
            <i className="h-[7px] w-[7px] rounded-full bg-success" />
            offload: {control.observed} · rev {control.revision}
          </span>
        ) : <div className="text-[12px] text-fg-muted">Offload control not recorded.</div>}
        {panels.some((panel) => panel.id === 'fleet') ? (
          <div className="text-[11.5px] text-fg-muted" data-testid="ci-build-host-chips">
            {hostChips.map((chip) => (
              <span key={chip} className="mr-3">
                {chip}
              </span>
            ))}
          </div>
        ) : null}
      </div>

      {stalled ? (
        <div className="flex items-start gap-3 rounded-xl border border-danger border-l-4 border-l-danger bg-surface px-4 py-3.5">
          <div className="grid h-[30px] w-[30px] flex-shrink-0 place-items-center rounded-lg bg-[var(--mod-color-nhot-bg)] text-danger">
            ▲
          </div>
          <div className="min-w-0 flex-1">
            <div className="text-[13.5px] font-bold">{stalled.title}</div>
            <div className="mt-0.5 text-[12px] leading-relaxed text-fg-muted">{stalled.detail}</div>
          </div>
          <div className="ml-auto flex flex-shrink-0 flex-wrap justify-end gap-1.5">
            {incidentAlertActions(stalled).map((action) => (
              <button
                key={action.label}
                type="button"
                data-action-verb={action.verb}
                disabled
                title="Actions land in wave 4"
                className={
                  action.primary
                    ? 'cursor-not-allowed rounded-[7px] border border-[var(--mod-color-accent-tint)] bg-[var(--mod-color-accent-tint)] px-3 py-1 text-[11.5px] font-semibold text-accent-fg opacity-50'
                    : 'cursor-not-allowed rounded-[7px] border border-border-strong bg-surface-raised px-3 py-1 text-[11.5px] text-fg opacity-50'
                }
              >
                {action.label}
              </button>
            ))}
          </div>
        </div>
      ) : null}

      <div className="grid grid-cols-6 gap-3">
        {kpis.map((tile) => (
          <div key={tile.key} data-testid={`offload-kpi-${tile.key}`} className="rounded-xl border border-border bg-surface px-[15px] py-[13px]">
            {tile.value === null ? (
              <div>
                <div className="text-[16px] font-semibold text-fg-muted">not recorded</div>
                <div className="mt-0.5 text-[12px] text-fg-muted">{tile.label}</div>
              </div>
            ) : <KpiTile tile={{ key: tile.key, label: tile.label, value: tile.value }} />}
            {tile.sub ? <div className="mt-[3px] text-[11px] leading-snug text-fg-subtle">{tile.sub}</div> : null}
          </div>
        ))}
      </div>

      <MachinesWidget panels={panels}>
        {({ control: machinesControl, selectedMachine, onAction, pendingVerb, actionError }) => <>
      <div className="grid grid-cols-[1fr_1.35fr] gap-3">

        <SectionCard
          title="Offload control"
          titleBadge={offloadStatus ? <StaleBadge status={offloadStatus} /> : undefined}
          action={{ label: 'history →' }}
        >
          {machinesControl ? (
            <OffloadControl
              control={machinesControl}
              selectedHost={selectedMachine}
              onAction={(action) => onAction(action, selectedMachine)}
              pendingVerb={pendingVerb}
              actionError={actionError}
            />
          ) : (
            <div className="text-[12px] text-fg-muted">Offload control not recorded.</div>
          )}
        </SectionCard>
        <SectionCard
          title="Cluster queue"
          titleBadge={offloadStatus ? <StaleBadge status={offloadStatus} /> : undefined}
        >
          {queue ? <ClusterQueue queue={queue} /> : <div className="text-[12px] text-fg-muted">Cluster queue not recorded.</div>}
        </SectionCard>
      </div>

      <SectionCard title={queue?.dispatchTarget ? `Remote jobs (${queue.dispatchTarget})` : 'Remote jobs'} action={{ label: 'rb-* units →' }}>
        {jobs
          ? <RemoteJobsTable jobs={jobs} hostLabel={queue?.dispatchTarget} />
          : <div className="text-[12px] text-fg-muted">Remote jobs not recorded.</div>}
      </SectionCard>

      <div className="grid grid-cols-2 gap-3">
        <SectionCard title="CI runners" action={{ label: 'workflows →' }}>
          {ciData ? <CiRunnersSummary data={ciData} /> : <div className="text-[12px] text-fg-muted">No CI panel.</div>}
        </SectionCard>
        <SectionCard title="Incidents" action={{ label: 'all →' }}>
          <div data-testid="ci-build-incidents">
            {buildKindItems.map((item, index) => (
              <InboxItem
                key={item.id}
                dot={severityDotFor(item.severity)}
                title={item.title}
                subtitle={item.detail}
                isLast={index === buildKindItems.length - 1}
                actions={item.actions.map((action) => ({
                  label: action.label,
                  primary: action.recommended,
                  verb: action.verb,
                }))}
              />
            ))}
          </div>
        </SectionCard>
      </div>

        </>}
      </MachinesWidget>
      </div>
    </div>
  )
}

function shortTicket(ticket: string): string {
  return ticket.startsWith('ticket.') ? ticket.slice('ticket.'.length, 'ticket.'.length + 8) : ticket
}

function conductorRow(repo: LandqRepoState): KvRow {
  const label = `conductor (${repo.project})`
  if (repo.conductorHeld === null) return { label, value: 'unknown — /proc/locks unreadable', intent: 'warn' }
  if (repo.conductorHeld === false) return { label, value: 'not held', intent: 'ok' }
  const pid = repo.conductorHolderPid !== null ? `pid ${repo.conductorHolderPid}` : 'pid unknown'
  const age = repo.conductorHolderAgeSeconds !== null
    ? `, holder process running ${formatDurationMs(repo.conductorHolderAgeSeconds * 1000)}`
    : ''
  return { label, value: `held by ${pid}${age}`, intent: 'warn' }
}

const LANDQ_COLUMNS: DataTableColumn<{ repo: LandqRepoState; ticket: LandqTicketState }>[] = [
  { id: 'project', header: 'Repo', minWidth: 90, cell: ({ repo }) => repo.project },
  { id: 'position', header: '#', minWidth: 40, sortable: true, sortValue: ({ ticket }) => ticket.position, cell: ({ ticket }) => ticket.position },
  { id: 'ticket', header: 'Ticket', minWidth: 90, cell: ({ ticket }) => shortTicket(ticket.ticket) },
  { id: 'branch', header: 'Branch', minWidth: 140, cell: ({ ticket }) => ticket.branch ?? 'not recorded' },
  { id: 'gateClass', header: 'Gate class', minWidth: 90, cell: ({ ticket }) => ticket.gateClass ?? 'not recorded' },
  {
    id: 'wait',
    header: 'Waiting',
    minWidth: 90,
    sortable: true,
    sortValue: ({ ticket }) => ticket.waitSeconds ?? -1,
    cell: ({ ticket }) => (ticket.waitSeconds === null ? 'not recorded' : formatDurationMs(ticket.waitSeconds * 1000)),
  },
  {
    id: 'owner',
    header: 'Owner',
    minWidth: 90,
    cell: ({ ticket }) => (ticket.ownerLockHeld === null ? 'unknown' : ticket.ownerLockHeld ? 'alive' : 'gone'),
  },
]

function LocalLandQueuePanel({ data }: { data: LandqRepoState[] | undefined }) {
  if (!data) {
    return (
      <SectionCard title="Local land queue">
        <div className="text-[12px] text-fg-muted">Local land queue not recorded.</div>
      </SectionCard>
    )
  }
  const rows = data.flatMap((repo) => repo.waiting.map((ticket) => ({ repo, ticket })))
  const kvRows: KvRow[] = [
    ...data.map((repo): KvRow => ({
      label: `queue depth (${repo.project})`,
      value: repo.waitingComplete ? String(repo.queueDepth) : 'partial — read error',
      intent: repo.waitingComplete ? 'neutral' : 'warn',
    })),
    ...data.map((repo): KvRow => conductorRow(repo)),
  ]

  return (
    <div data-testid="local-landq-panel">
      <SectionCard title="Local land queue">
        <div className="flex flex-col gap-3">
          <KvPanel rows={kvRows} />
          {rows.length > 0
            ? (
              <DataTable
                caption="Waiting tickets"
                columns={LANDQ_COLUMNS}
                rows={rows}
                getRowId={({ ticket }) => ticket.ticket}
              />
            )
            : <div className="text-[12px] text-fg-muted">No tickets waiting.</div>}
        </div>
      </SectionCard>
    </div>
  )
}

function CiRunnersSummary({ data }: { data: GhciPanelData }) {
  const rows = ciKvRows(data)
  return (
    <div>
      {rows.map((row) => (
        <div key={row.label} className="flex justify-between border-b border-[#1d2027] py-1 text-[12px] text-fg-muted">
          <span>{row.label}</span>
          <b className="font-medium text-fg">{row.value}</b>
        </div>
      ))}
    </div>
  )
}
