/** Structural mirror of collector offload adapter panel payloads — not imported from collector. */

export type HostState = 'available' | 'draining' | 'maintenance' | 'restoring' | 'degraded'

export interface OffloadAction {
  label: string
  verb: string
  primary?: boolean
  danger?: boolean
}

export interface CapabilityProbe {
  name: string
  ok: boolean
  version?: string
  // Why this probe is not ok. Present whenever the controller could name a cause.
  detail?: string
  checkedAt?: string
}

/** Declared in the buildbox registry — distinct vocabulary from the controller's `HostState`. */
export type RegistryHostState = 'reachable' | 'unreachable' | 'bricked'

/** Live work units a box observes on itself, one field per placement path. */
export interface RemoteWorkCounts {
  /** Harness agent seats (`.harness-seat/<runId>`), whatever runtime they drive. */
  agentSeats: number
  /** Remote build jobs (`.rb/jobs/<jobId>`) placed by the build offload path. */
  remoteBuildJobs: number
  /** Offloaded interactive shells (`cdx-offload/<slug>` mux sessions). */
  offloadShells: number
}

export interface FleetHost {
  host: string
  role: 'builder' | 'workstation'
  /** Observed by the controller; null for a declared host it has not enrolled. */
  state: HostState | null
  /** Declared state from the buildbox registry, the only source of host identity. */
  registryState: RegistryHostState
  registryRoles?: string[]
  rustdesk?: string | null
  /** true observed enrolled, false observed absent, null the controller was not reachable. */
  enrolled?: boolean | null
  primary?: boolean
  enrolling?: boolean
  load: number | null
  cores: number | null
  coreLoads: number[]
  running: number | null
  slotsFree: number | null
  slotsTotal: number | null
  builds24h: number | null
  /** Live agent sessions the host's session ledger classifies as running; null when the host cannot report it. */
  sessions: number | null
  /**
   * Work units the box observed executing on itself, split by the path that placed them.
   * Null when the box published no count — an unobserved box is not an idle box.
   */
  remoteWork?: RemoteWorkCounts | null
  dispatchAccept: boolean | null
  capability: { probes: CapabilityProbe[]; circuitOpen?: boolean; missingCommand?: string }
  memUsedBytes: number | null
  memTotalBytes: number | null
  swapUsedBytes: number | null
  swapTotalBytes: number | null
  netRxBytesPerSecond: number | null
  netTxBytesPerSecond: number | null
  disk: Array<{ mountpoint: string; freeBytes: number; sizeBytes: number }>
  temp: { pkg: number | null; max: number | null; crit: number | null }
  /** Box-side guard facts; null for a host the controller never sampled. */
  guard?: HostGuardFacts | null
  /** ~/.claude config parity; null for a host the convergence timer has not probed. */
  parity?: HostParity | null
}

/** One `buildbox claude-parity` verdict, as recorded by the convergence timer. */
export interface HostParity {
  verdict: 'converged' | 'drifted' | 'unreachable' | 'error'
  detail: string
  probedAt: string
}

/** Stall, tmpfs fill and agent/build slice counters read from the box's own kernel. */
export interface HostGuardFacts {
  stall: { some60: number | null; full60: number | null; full300: number | null }
  tmpUsedBytes: number | null
  tmpSizeBytes: number | null
  workSlices: Array<{
    slice: string
    memoryBytes: number | null
    pidsCurrent: number | null
    oomKillTotal: number | null
  }>
}

export interface QueueTicket {
  position: number
  key: string
  repo: string
  owner: { pid: number; starttime: number; label?: string }
  enqueueAgeSeconds: number
  state: string
  dispatchTarget?: string
}

export interface ClusterQueueModel {
  depth: number
  oldestAgeSeconds: number
  p95AgeSeconds: number
  sloSeconds: number
  dispatchTarget?: string
  spill?: { active: boolean; stale?: boolean }
  wedge?: { host: string; detail: string }
  tickets: QueueTicket[]
}

export interface RemoteJob {
  id: string
  repo: string
  snapshot: string
  stage: string
  host: string
  rc: number | null
  pullBytes: number | null
  pullDurationSeconds: number | null
  publication: { state: string; reason?: string }
}

export interface OffloadControlModel {
  desired: HostState
  observed: HostState
  revision: number
  lease: { active: boolean; expired?: boolean; host?: string | null; reason?: string | null }
  reconciler: { healthy: boolean; lastAt?: string }
  dispatch: { state: string; detail?: string; host?: string }
  spill?: { active: boolean; stale?: boolean }
  capabilityProbes: CapabilityProbe[]
  actions: OffloadAction[]
}
