import type { MouseEvent } from 'react'
import { Badge, Card, ProgressBar, StatusDot } from '@astryxdesign/core'
import type { FleetHost, OffloadAction } from './offload-types'
import { formatPercent, formatRelativeTime, ratioPercent } from './format'
import { CapabilityProbeList } from './CapabilityProbeList'
import { StatusChip } from './StatusChip'
import { useDeckTooltip } from './DeckTooltip'
import { Button } from './Button'

export interface MachineCardProps {
  machine: FleetHost
  statusPill?: string
  statusPillIntent?: 'ok' | 'warn' | 'neutral'
  warn?: boolean
  note?: string
  actions: OffloadAction[]
  onClick?: () => void
  onMouseEnter?: (event: MouseEvent) => void
  onMouseMove?: (event: MouseEvent) => void
  onMouseLeave?: () => void
  /** Reference clock for parity "x ago" text. Defaults to the render-time clock; pass a pinned value for deterministic SSR/hydration. */
  nowMs?: number
}

function loadBarVariant(percent: number): 'error' | 'warning' | 'success' {
  if (percent >= 85) return 'error'
  if (percent >= 60) return 'warning'
  return 'success'
}

function roleLabel(machine: FleetHost): string {
  if (machine.enrolling) return 'builder · joining'
  if (machine.role === 'workstation') return 'workstation'
  return machine.primary ? 'builder · primary' : 'builder'
}

function statusDotVariant(machine: FleetHost, warn?: boolean): 'warning' | 'success' | 'neutral' {
  if (machine.enrolling) return 'neutral'
  if (warn) return 'warning'
  if (machine.dispatchAccept === false && machine.role === 'builder') return 'warning'
  return 'success'
}

/**
 * An absent load is shown as absent WITH its cause: the telemetry probe carries why the
 * box published no numbers, and a bare dash taught the operator nothing.
 */
function noLoadLabel(machine: FleetHost, enrolling: boolean): string {
  const telemetry = machine.capability.probes.find((probe) => probe.name === 'telemetry')
  if (telemetry && !telemetry.ok) return `— · ${telemetry.detail ?? 'no telemetry'}`
  if (enrolling) return '— · enrolling'
  return '— · no telemetry reported'
}

/** Reads as "not looked at", where a dash or a zero both read as "nothing is happening". */
const UNOBSERVED = 'n/a'

export interface StatCellState {
  value: string
  hot?: boolean
  cause?: string
  breakdown?: string
}

function pluralize(count: number, noun: string): string {
  return `${count} ${noun}${count === 1 ? '' : 's'}`
}

/**
 * The controller's job table only ever sees work the controller placed, so it reported zero
 * for every box while agent seats and remote builds ran on them. The box's own process-level
 * count covers every placement path, and is the only source this cell trusts.
 */
function runningCell(machine: FleetHost, enrolling: boolean): StatCellState {
  if (enrolling) return { value: UNOBSERVED, cause: 'this host is still enrolling and reports no work yet' }
  const work = machine.remoteWork
  if (!work) {
    return {
      value: UNOBSERVED,
      cause: 'this host published no work count — the deck did not look, it is not reporting an idle box',
    }
  }
  const total = work.agentSeats + work.remoteBuildJobs + work.offloadShells
  return {
    value: String(total),
    hot: total === 0 && machine.role === 'builder',
    breakdown: [
      pluralize(work.agentSeats, 'agent seat'),
      pluralize(work.remoteBuildJobs, 'remote build'),
      pluralize(work.offloadShells, 'offload shell'),
    ].join(' · '),
  }
}

/**
 * The session ledger only knows runtimes launched through it; a container seat is an agent
 * session it never records. Both sources are counted, and each is named in the tooltip.
 */
export function sessionsCell(machine: FleetHost, enrolling: boolean): StatCellState {
  if (enrolling) return { value: UNOBSERVED, cause: 'this host is still enrolling and reports no sessions yet' }
  const work = machine.remoteWork
  const untracked = work ? work.agentSeats + work.offloadShells : null
  if (machine.sessions === null && untracked === null) {
    return { value: UNOBSERVED, cause: 'this host published neither a session ledger count nor a seat count' }
  }
  const parts: string[] = []
  if (machine.sessions === null) parts.push('ledger count not published')
  else parts.push(pluralize(machine.sessions, 'ledger-tracked session'))
  if (untracked === null) parts.push('seat count not published')
  else parts.push(`${pluralize(work!.agentSeats, 'agent seat')} · ${pluralize(work!.offloadShells, 'offload shell')}`)
  // One source missing makes the sum a floor, not a total, and the cell says so with `≥`
  // rather than presenting a partial count as a complete one.
  const partial = machine.sessions === null || untracked === null
  return {
    value: `${partial ? '≥' : ''}${(machine.sessions ?? 0) + (untracked ?? 0)}`,
    breakdown: parts.join(' · '),
  }
}

/** `.mach` — per-host fleet card: load meter, running/slots/24h/sessions, dispatch-accept, capability probes. */
export function MachineCard({
  machine,
  statusPill,
  statusPillIntent = 'neutral',
  warn,
  note,
  actions,
  onClick,
  onMouseEnter,
  onMouseMove,
  onMouseLeave,
  nowMs,
}: MachineCardProps) {
  const enrolling = machine.enrolling === true
  const loadPercent =
    machine.load !== null && machine.cores
      ? Math.min(100, ratioPercent(machine.load, machine.cores))
      : 0
  const saturated = machine.load !== null && machine.cores ? machine.load >= machine.cores : false

  return (
    <Card
      role="button"
      tabIndex={0}
      data-testid={`machine-card-${machine.host}`}
      data-host={machine.host}
      onClick={onClick}
      onKeyDown={(event) => {
        if (event.key === 'Enter' || event.key === ' ') {
          event.preventDefault()
          onClick?.()
        }
      }}
      onMouseEnter={onMouseEnter}
      onMouseMove={onMouseMove}
      onMouseLeave={onMouseLeave}
      className={`flex cursor-pointer flex-col gap-[11px] transition-[border-color] duration-[120ms] hover:border-accent ${
        enrolling ? 'border-dashed' : warn ? 'border-warning' : ''
      }`}
    >
      <div className="flex flex-wrap items-center gap-2">
        <span className={`text-[14px] font-bold ${enrolling ? 'text-fg-subtle' : ''}`}>{machine.host}</span>
        <Badge variant="neutral" label={roleLabel(machine)} />
        {statusPill ? (
          <Badge
            variant={statusPillIntent === 'warn' ? 'warning' : statusPillIntent === 'ok' ? 'success' : 'neutral'}
            label={statusPill}
          />
        ) : null}
        <StatusDot
          variant={statusDotVariant(machine, warn)}
          label={`${machine.host} status`}
          className="ml-auto"
        />
      </div>

      <div>
        <div className="mb-[5px] flex justify-between text-[11.5px] text-fg-muted">
          <span>load</span>
          <b className="font-medium tabular-nums text-fg">
            {machine.load === null || !machine.cores ? (
              <span className="text-fg-subtle">{noLoadLabel(machine, enrolling)}</span>
            ) : saturated ? (
              <span className="text-warning">
                {machine.load.toFixed(1)} / {machine.cores} · saturated
              </span>
            ) : (
              <span className="text-success">
                {machine.load.toFixed(1)} / {machine.cores} · {formatPercent(loadPercent)}
              </span>
            )}
          </b>
        </div>
        <ProgressBar
          variant={loadBarVariant(loadPercent)}
          value={machine.load === null || !machine.cores ? 0 : loadPercent}
          label={`${machine.host} load`}
          isLabelHidden
        />
      </div>

      <div className="grid grid-cols-4 gap-1.5 text-center">
        <StatCell label="running" {...runningCell(machine, enrolling)} />
        <StatCell
          label="slots"
          value={
            machine.slotsFree !== null && machine.slotsTotal !== null
              ? `${machine.slotsTotal - machine.slotsFree}/${machine.slotsTotal}`
              : UNOBSERVED
          }
          cause={
            machine.slotsFree !== null && machine.slotsTotal !== null
              ? undefined
              : 'the controller published no slot capacity for this host'
          }
        />
        <StatCell
          label="24h"
          value={enrolling || machine.builds24h === null ? UNOBSERVED : String(machine.builds24h)}
          cause={
            enrolling
              ? 'this host is still enrolling, so it has no 24-hour history yet'
              : machine.builds24h === null
                ? 'no 24-hour build history is recorded for this host'
                : undefined
          }
        />
        <StatCell label="sessions" {...sessionsCell(machine, enrolling)} />
      </div>

      <div className="flex flex-wrap gap-[5px]">
        <CapabilityProbeList probes={machine.capability.probes} showVersion reasons />
        {enrolling && machine.capability.probes.length === 0 ? (
          <Badge
            variant="neutral"
            icon={<i className="h-1.5 w-1.5 rounded-full bg-fg-subtle" />}
            label="capability probe pending"
          />
        ) : null}
      </div>

      {machine.dispatchAccept !== null && !enrolling ? (
        <div className="text-[10.5px] text-fg-muted">
          dispatch-accept{' '}
          <span className={machine.dispatchAccept ? 'text-success' : 'text-warning'}>
            {machine.dispatchAccept ? 'yes' : 'no'}
          </span>
        </div>
      ) : null}

      <ConfigParityRow machine={machine} nowMs={nowMs} />

      {note ? <div className="text-[10.5px] text-fg-muted">{note}</div> : null}

      <div className="mt-auto flex gap-1.5">
        {actions.map((action) => (
          <Button
            key={action.label}
            type="button"
            data-action-verb={action.verb}
            isDisabled
            tooltip="Actions land in wave 4"
            variant={action.danger ? 'outline' : action.primary ? 'solid' : 'outline'}
            tone={action.danger ? 'danger' : action.primary ? 'accent' : 'neutral'}
            size="sm"
            className="opacity-50"
            onClick={(event) => event.stopPropagation()}
          >
            {action.label}
          </Button>
        ))}
      </div>
    </Card>
  )
}

/**
 * A verdict with no probe time is not evidence, so an unprobed host says so rather than
 * borrowing the fleet's last-known state.
 */
function ConfigParityRow({ machine, nowMs }: { machine: FleetHost; nowMs?: number }) {
  const parity = machine.parity ?? null
  const probedMs = parity ? Date.parse(parity.probedAt) : Number.NaN
  const tooltip = useDeckTooltip(
    parity ? parity.detail : 'buildbox-parity.timer has not recorded a verdict for this host',
    parity && !Number.isNaN(probedMs) ? parity.probedAt : undefined,
  )

  return (
    <div className="flex items-center gap-1.5 text-[10.5px] text-fg-muted" data-testid={`machine-parity-${machine.host}`} {...tooltip}>
      <span>~/.claude</span>
      {parity ? (
        <StatusChip status={parity.verdict} />
      ) : (
        <span className="text-fg-subtle">— not probed</span>
      )}
      {parity && !Number.isNaN(probedMs) ? (
        <span className="tabular-nums text-fg-subtle">{formatRelativeTime(probedMs, nowMs)}</span>
      ) : null}
    </div>
  )
}

function StatCell({
  label,
  value,
  hot,
  cause,
  breakdown,
}: {
  label: string
  value: string
  hot?: boolean
  cause?: string
  breakdown?: string
}) {
  const tooltip = useDeckTooltip(cause ?? breakdown ?? `${label}: ${value}`)
  return (
    <div
      className="rounded-lg bg-surface-raised px-1 py-[7px]"
      data-testid={`machine-stat-${label}`}
      data-unobserved={cause ? 'true' : undefined}
      {...tooltip}
    >
      <div
        className={`font-bold leading-[1.1] tabular-nums ${
          cause ? 'text-[11px] text-fg-subtle' : hot ? 'text-[18px] text-danger' : 'text-[18px]'
        }`}
      >
        {value}
      </div>
      <div className="mt-0.5 text-[9.5px] uppercase tracking-[0.05em] text-fg-subtle">{label}</div>
    </div>
  )
}