import { useMemo, useState } from 'react'
import {
  DataTable,
  withSorting,
  type DataTableColumn,
  type DataTableSort,
} from '@platform-modules/ui-primitives'
import { formatRelativeTime, SectionCard, StatusChip, useDeckTooltip } from '@overdeck/deck-ui'
import {
  COVERAGE_CHIP_STATUS,
  COVERAGE_LABEL,
  COVERAGE_NOTE,
  hostBreakdown,
  type HostBreakdownRow,
} from '../../lib/session-hosts'
import type { AgentSessionRow, SessionsPanelData } from '../../lib/session-types'

function CountCell({ value }: { value: number }) {
  return <span className="tabular-nums text-fg">{value}</span>
}

function CoverageCell({ row }: { row: HostBreakdownRow }) {
  const tooltip = useDeckTooltip(COVERAGE_NOTE[row.coverage], 'Coverage')
  const registryTooltip = useDeckTooltip(row.facts?.notes ?? '', 'Registry note')
  const probeTooltip = useDeckTooltip(row.probe?.at ?? '', 'Probe time')
  return (
    <span className="flex flex-col items-start gap-0.5">
      <span {...tooltip}>
        <StatusChip status={COVERAGE_CHIP_STATUS[row.coverage]} label={COVERAGE_LABEL[row.coverage]} />
      </span>
      {row.probe?.status === 'ok' && row.probe.at ? (
        <span className="text-[11px] text-fg-subtle" {...probeTooltip}>as of {formatRelativeTime(Date.parse(row.probe.at))}</span>
      ) : row.probe?.status === 'failed' ? (
        <span className="text-[11px] text-fg-subtle">probe failed: {row.probe.error}</span>
      ) : row.probe?.status === 'not-reachable' ? (
        <span className="text-[11px] text-fg-subtle">{row.probe.error}</span>
      ) : null}
      {row.facts ? (
        <span className="text-[11px] text-fg-subtle" {...registryTooltip}>
          registry: {row.facts.state ?? 'unlisted'}
        </span>
      ) : row.host !== null ? (
        <span className="text-[11px] text-fg-subtle">not in the buildbox registry</span>
      ) : null}
    </span>
  )
}

function CliCell({ row }: { row: HostBreakdownRow }) {
  if (row.runningByCli.length === 0) {
    return <span className="tabular-nums text-fg">0</span>
  }
  return (
    <span className="flex flex-wrap gap-x-2 gap-y-0.5 text-fg-muted">
      {row.runningByCli.map((entry) => (
        <span key={entry.cli} className="tabular-nums">{entry.cli} {entry.count}</span>
      ))}
    </span>
  )
}

function LedgerCell({ row }: { row: HostBreakdownRow }) {
  if (row.ledgerEntries === 0) return <span className="text-fg-subtle">never</span>
  return <span className="tabular-nums text-fg-muted">{row.ledgerEntries}</span>
}

function buildColumns(): Array<DataTableColumn<HostBreakdownRow>> {
  return [
    {
      id: 'host',
      header: 'Host',
      sortable: true,
      sortValue: (row) => row.label,
      cell: (row) => <span className="font-semibold text-fg">{row.label}</span>,
    },
    {
      id: 'coverage',
      header: 'Coverage',
      sortable: true,
      sortValue: (row) => row.coverage,
      cell: (row) => <CoverageCell row={row} />,
    },
    {
      id: 'live',
      header: 'Live',
      sortable: true,
      sortValue: (row) => row.live,
      cell: (row) => <CountCell value={row.live} />,
    },
    {
      id: 'idle',
      header: 'Idle',
      sortable: true,
      sortValue: (row) => row.idle,
      cell: (row) => <CountCell value={row.idle} />,
    },
    {
      id: 'reconnectable',
      header: 'Reconnectable',
      sortable: true,
      sortValue: (row) => row.reconnectable,
      cell: (row) => <CountCell value={row.reconnectable} />,
    },
    {
      id: 'cli',
      header: 'Running by CLI',
      cell: (row) => <CliCell row={row} />,
    },
    {
      id: 'ledger',
      header: 'In ledger',
      sortable: true,
      sortValue: (row) => row.ledgerEntries,
      cell: (row) => <LedgerCell row={row} />,
    },
  ]
}

/**
 * Sessions per host. Every host the deck can name gets a row — a machine with nothing
 * observed on it is the point, not noise to filter out.
 */
export function SessionHostsTable({
  rows,
  panel,
}: {
  rows: AgentSessionRow[]
  panel: Pick<SessionsPanelData, 'hosts' | 'localHost' | 'hostProbes' | 'hostsRegistryPath' | 'hostsRegistryMissing'>
}) {
  const [sort, setSort] = useState<DataTableSort | null>(null)
  const columns = useMemo(() => buildColumns(), [])
  const hosts = useMemo(() => hostBreakdown(rows, panel), [rows, panel])

  return (
    <SectionCard title="Sessions by host">
      <DataTable
        caption="Sessions per host"
        columns={columns}
        rows={hosts}
        getRowId={(row) => row.host ?? 'host-not-recorded'}
        capabilities={[withSorting({ sort, onSortChange: setSort, manual: false })]}
        emptyState={<span className="text-fg-muted">No hosts to show.</span>}
      />
      <p className="mt-2 text-[11px] text-fg-muted">
        Hosts come from the buildbox registry at <code>{panel.hostsRegistryPath}</code>, plus the
        collector host and any host the ledger names. The classifier runs directly on{' '}
        <code>{panel.localHost}</code> and over SSH on registry-reachable remote hosts; probe status
        and freshness are shown per host, and every numeric zero is an observed count.
        {panel.hostsRegistryMissing
          ? ` The registry at ${panel.hostsRegistryPath} is unreadable, so only hosts the ledger names are listed.`
          : ''}
      </p>
    </SectionCard>
  )
}
