import type { InboxItemAction, KvRow, MapChip, MapEdge, MapNodeModel, MapNodePulse } from '@overdeck/deck-ui'
import type { Item, Panel } from './collector-types'
import type {
  AgentsPanelData,
  ClusterPanelData,
  GhciPanelData,
  HarnessPlansPanelData,
  PrometheusHostPanelData,
  SystrayPanelData,
  BotsPanelData,
} from './panel-data'
import { ciKvRows, planKvRows, spendTodayTotal } from './overview-mappers'

const LAYOUT: Record<string, { x: number; y: number }> = {
  'host:laptop': { x: 8, y: 30 },
  github: { x: 46, y: 12 },
  'host:debian1': { x: 42, y: 58 },
  bots: { x: 14, y: 74 },
}

function panelData<T>(panels: Panel[], id: string): T | undefined {
  return panels.find((panel) => panel.id === id)?.data as T | undefined
}

function hostPanels(panels: Panel[]): Panel[] {
  return panels.filter((panel) => panel.id.startsWith('host:'))
}

function hostnameFromPanelId(panelId: string): string {
  return panelId.slice('host:'.length)
}

function pulseForHost(data: PrometheusHostPanelData | undefined, busy: boolean): MapNodePulse {
  if (data?.prochot) return 'down'
  if (busy) return 'busy'
  return 'ok'
}

function limitChip(data: SystrayPanelData | undefined): MapChip | undefined {
  const warn = data?.accounts.find((account) => account.status === 'warn' || (account.percent ?? 0) >= 75)
  if (!warn || warn.percent === null) return undefined
  return { label: `${warn.label ?? warn.slug} ${Math.round(warn.percent)}%`, hot: true }
}

function buildHostNode(
  panel: Panel,
  panels: Panel[],
  items: Item[],
): MapNodeModel {
  const hostname = hostnameFromPanelId(panel.id)
  const hostData = panel.data as PrometheusHostPanelData
  const agents = panelData<AgentsPanelData>(panels, 'agents')
  const plans = panelData<HarnessPlansPanelData>(panels, 'plans')
  const ci = panelData<GhciPanelData>(panels, 'ci')
  const limits = panelData<SystrayPanelData>(panels, 'limits')
  const cluster = panelData<ClusterPanelData>(panels, 'cluster')

  const busyRunner = ci?.repos.some((repo) => repo.runners.some((runner) => runner.busy && runner.name.includes(hostname)))
  const runningPlan = plans?.runs.find((run) => run.status === 'running')

  const chips: MapChip[] = []
  if (agents) {
    for (const entry of agents.fleet) {
      chips.push({ label: `${entry.class} ×${entry.count}` })
    }
    if (agents.orphans > 0) chips.push({ label: `orphan ×${agents.orphans}`, hot: true })
  }
  if (busyRunner) chips.push({ label: 'runner: busy', hot: true })
  if (runningPlan) chips.push({ label: `plan: ${runningPlan.title}` })
  const limit = limitChip(limits)
  if (limit) chips.push(limit)
  if (hostData.buildSliceJobCount > 0) chips.push({ label: `build.slice ${hostData.buildSliceJobCount}` })
  if (cluster && hostname === 'laptop') {
    chips.push({ label: `buildslot ${cluster.buildslot.running} run · ${cluster.buildslot.queued} queued` })
  }

  const inspectorKvs: KvRow[] = []
  if (agents) inspectorKvs.push({ label: 'Agents', value: `${agents.totalLive} (${agents.orphans} orphan)` })
  const runner = ci?.repos.flatMap((repo) => repo.runners).find((entry) => entry.name.includes(hostname))
  if (runner) {
    inspectorKvs.push({
      label: 'Runner',
      value: `${runner.name} · ${runner.busy ? 'busy' : runner.status}`,
      intent: runner.busy ? 'warn' : 'ok',
    })
  }
  if (runningPlan) {
    const wave = runningPlan.waves.at(-1)
    inspectorKvs.push({
      label: 'Plan',
      value: wave ? `${runningPlan.title} · w${wave.wave}/${runningPlan.waves.length}` : runningPlan.title,
      intent: 'info',
    })
  }
  if (hostData.cpuPsiSomePercent !== null) {
    inspectorKvs.push({ label: 'PSI cpu', value: `${hostData.cpuPsiSomePercent.toFixed(1)}% some`, intent: 'ok' })
  }
  if (cluster && hostname === 'laptop') {
    inspectorKvs.push({
      label: 'buildslot',
      value: `${cluster.buildslot.running} run · ${cluster.buildslot.queued} queued`,
    })
  }
  const warnAccount = limits?.accounts.find((account) => account.status === 'warn')
  if (warnAccount?.percent !== null && warnAccount?.percent !== undefined) {
    inspectorKvs.push({
      label: `${warnAccount.slug} limit`,
      value: `${Math.round(warnAccount.percent)}%${warnAccount.capEtaMinutes ? ' ⚠' : ''}`,
      intent: 'warn',
    })
  }

  const orphanItem = items.find((item) => item.id === 'orphan-agent')
  const actions: InboxItemAction[] =
    orphanItem?.actions.map((action) => ({ label: action.label, primary: action.recommended })) ?? []

  return {
    id: panel.id,
    title: `${hostname}${hostname === 'laptop' ? ' · e14' : hostname === 'debian1' ? ' · buildbox' : ''}`,
    subtitle: hostname === 'laptop' ? 'ThinkPad · 16 cores · Tailscale ✓' : undefined,
    pulse: pulseForHost(hostData, Boolean(busyRunner) || (agents?.orphans ?? 0) > 0),
    chips,
    x: LAYOUT[panel.id]?.x ?? 10,
    y: LAYOUT[panel.id]?.y ?? 20,
    inspectorKvs,
    actions,
  }
}

function buildGithubNode(panels: Panel[]): MapNodeModel | undefined {
  const ci = panelData<GhciPanelData>(panels, 'ci')
  if (!ci) return undefined

  const chips: MapChip[] = []
  for (const repo of ci.repos) {
    if (!repo.historyComplete) continue
    const latest = repo.runs[0]
    if (!latest) continue
    const failed = latest.conclusion === 'failure'
    chips.push({
      label: `${repo.repo.split('/').pop()} #${latest.id} ${failed ? '✗' : '✓'}`,
      hot: failed,
    })
  }
  const queueComplete = ci.repos.every(
    (repo) => repo.queueComplete && repo.queueDepth !== null,
  )
  chips.push({
    label: queueComplete
      ? `queue ${ci.repos.reduce((sum, repo) => sum + repo.queueDepth!, 0)}`
      : 'queue incomplete',
  })

  return {
    id: 'github',
    title: 'GitHub · Actions',
    pulse: ci.repos.some(
      (repo) => repo.historyComplete && repo.runs[0]?.conclusion === 'failure',
    )
      ? 'busy'
      : 'ok',
    chips,
    x: LAYOUT.github!.x,
    y: LAYOUT.github!.y,
    inspectorKvs: ciKvRows(ci),
    actions: [],
  }
}

function buildClusterHostNode(panels: Panel[]): MapNodeModel | undefined {
  const cluster = panelData<ClusterPanelData>(panels, 'cluster')
  if (!cluster) return undefined
  if (hostPanels(panels).some((panel) => panel.id === 'host:debian1')) return undefined

  const chips: MapChip[] = [
    { label: `rb-* ${cluster.debian1 === 'online' ? 'idle' : 'offline'}`, hot: cluster.debian1 === 'offline' },
    { label: 'runner online', hot: cluster.debian1 === 'offline' },
    { label: `buildslot ${cluster.buildslot.running}/${cluster.buildslot.queued}` },
  ]

  return {
    id: 'host:debian1',
    title: 'debian1 · buildbox',
    pulse: cluster.debian1 === 'online' ? 'ok' : 'down',
    chips,
    x: LAYOUT['host:debian1']!.x,
    y: LAYOUT['host:debian1']!.y,
    inspectorKvs: [
      { label: 'debian1', value: cluster.debian1, intent: cluster.debian1 === 'online' ? 'ok' : 'err' },
      { label: 'autoscaler', value: cluster.autoscaler.state, intent: cluster.autoscaler.state === 'pressure' ? 'warn' : 'ok' },
      {
        label: 'buildslot',
        value: `${cluster.buildslot.running} running · ${cluster.buildslot.queued} queued`,
      },
    ],
    actions: [],
  }
}

function buildBotsNode(panels: Panel[]): MapNodeModel | undefined {
  const bots = panelData<BotsPanelData>(panels, 'bots')
  if (!bots) return undefined

  const chips: MapChip[] = bots.bots.map((bot) => ({
    label: `${bot.name} ${bot.status === 'down' ? 'down' : bot.status}`,
    hot: bot.status === 'down',
  }))

  return {
    id: 'bots',
    title: 'Botmaster',
    pulse: bots.bots.some((bot) => bot.status === 'down') ? 'down' : bots.bots.some((bot) => bot.status === 'slow') ? 'busy' : 'ok',
    chips,
    x: LAYOUT.bots!.x,
    y: LAYOUT.bots!.y,
    inspectorKvs: bots.bots.map((bot) => ({
      label: bot.name,
      value: bot.statusDetail ?? bot.status,
      intent: bot.status === 'down' ? 'err' : bot.status === 'slow' ? 'warn' : 'ok',
    })),
    actions: [],
  }
}

function buildEdges(nodes: MapNodeModel[]): MapEdge[] {
  const ids = new Set(nodes.map((node) => node.id))
  const edges: MapEdge[] = []
  if (ids.has('host:laptop') && ids.has('github')) {
    edges.push({ from: 'host:laptop', to: 'github', dashed: true })
  }
  if (ids.has('host:laptop') && ids.has('host:debian1')) {
    edges.push({ from: 'host:laptop', to: 'host:debian1', color: 'rgba(57,217,138,0.4)' })
  }
  if (ids.has('github') && ids.has('host:debian1')) {
    edges.push({ from: 'github', to: 'host:debian1', dashed: true })
  }
  if (ids.has('host:laptop') && ids.has('bots')) {
    edges.push({ from: 'host:laptop', to: 'bots', dashed: true })
  }
  return edges
}

/** Derives map nodes and edges from whichever panels are present — no hardcoded host list. */
export function deriveMapGraph(panels: Panel[], items: Item[]): { nodes: MapNodeModel[]; edges: MapEdge[] } {
  const nodes: MapNodeModel[] = []

  for (const panel of hostPanels(panels)) {
    nodes.push(buildHostNode(panel, panels, items))
  }

  const clusterHost = buildClusterHostNode(panels)
  if (clusterHost) nodes.push(clusterHost)

  const github = buildGithubNode(panels)
  if (github) nodes.push(github)

  const bots = buildBotsNode(panels)
  if (bots) nodes.push(bots)

  return { nodes, edges: buildEdges(nodes) }
}

export function gatesKvRows(data: import('./panel-data').GatesPanelData): KvRow[] {
  const trend = data.slopgateDebtTrend7d === 0 ? '' : data.slopgateDebtTrend7d > 0 ? ` ▲${data.slopgateDebtTrend7d}` : ` ▼${Math.abs(data.slopgateDebtTrend7d)}`
  return [
    { label: 'prevent-band', value: `${data.preventOpen} open`, intent: data.preventOpen > 0 ? 'err' : 'ok' },
    {
      label: 'slopgate debt',
      value: `${data.slopgateDebtTotal}${trend}`,
      intent: data.slopgateDebtTrend7d < 0 ? 'ok' : data.slopgateDebtTotal > 0 ? 'warn' : 'ok',
    },
    { label: 'warnignore adds', value: `${data.warnignoreAdds7d} this wk`, intent: data.warnignoreAdds7d > 0 ? 'warn' : 'ok' },
    ...data.slopgateRepos.map((repo) => ({
      label: repo.repo,
      value: `${repo.debt}${repo.trend7d === 0 ? '' : repo.trend7d > 0 ? ` ▲${repo.trend7d}` : ` ▼${Math.abs(repo.trend7d)}`}`,
      intent: (repo.trend7d < 0 ? 'ok' : 'warn') as KvRow['intent'],
    })),
  ]
}

export function agentsKvRows(data: AgentsPanelData): KvRow[] {
  return [
    { label: 'processes observed now', value: String(data.totalLive), intent: 'ok' },
    { label: 'orphans', value: String(data.orphans), intent: data.orphans > 0 ? 'err' : 'ok' },
    ...data.fleet.map((entry) => ({
      label: entry.class,
      value: `×${entry.count} · ${entry.cpuPercent}% CPU`,
      intent: 'neutral' as const,
    })),
  ]
}

export function clusterKvRows(data: ClusterPanelData): KvRow[] {
  return [
    { label: 'debian1', value: data.debian1, intent: data.debian1 === 'online' ? 'ok' : 'err' },
    { label: 'autoscaler', value: data.autoscaler.state, intent: data.autoscaler.state === 'pressure' ? 'warn' : 'ok' },
    {
      label: 'buildslot running',
      value: String(data.buildslot.running),
    },
    {
      label: 'buildslot queued',
      value: String(data.buildslot.queued),
    },
    {
      label: 'p95 wait',
      value: data.buildslot.p95WaitSeconds === null ? '—' : `${data.buildslot.p95WaitSeconds}s`,
    },
  ]
}

export { planKvRows, ciKvRows, spendTodayTotal }
