import { useCallback, useEffect, useMemo, useState } from "react"
import {
  Activity,
  AlertTriangle,
  Cpu,
  Database,
  Globe,
  HardDrive,
  Loader2,
  RefreshCw,
  RotateCw,
  Server,
  Users,
  Zap,
} from "lucide-react"

import { ArcadiaError, useArcadiaClient } from "@crema/arcadia-client"
import { AlertBanner } from "@crema/feedback-ui"
import {
  BarChart,
  Donut,
  Heatmap,
  LineChart,
  Sparkline,
  type ChartDatum,
  type SeriesPoint,
} from "@crema/chart-ui"
import { KpiTile, formatCompact, formatPercent } from "@crema/dashboard-ui"
import {
  ComponentRow,
  IncidentTimeline,
  OverallStatus,
  type ComponentState,
  type StatusComponent,
  type StatusIncident,
} from "@crema/status-ui"
import {
  ChoroplethMap,
  WorldMapSvg,
} from "@crema/map-ui"
import { formatBytes } from "@crema/file-ui"

import { AppShell } from "~/components/layout/app-shell"
import { Badge } from "~/components/ui/badge"
import { Button } from "~/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "~/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"
import {
  getActiveSessions,
  getAuditStats,
  getInfrastructureSummary,
  getJobStats,
  getRateLimits,
  getRecentJobs,
  getSpaces,
  listDroplets,
  retryJob,
  type ActiveSession,
  type AuditStats,
  type Droplet,
  type InfrastructureSummary,
  type JobStats,
  type ObanJob,
  type RateLimit,
  type Space,
} from "~/lib/arcadia/monitoring"
import {
  getHealth,
  getHostStats,
  SUBSYSTEMS,
  type HealthSubsystem,
  type HostStats,
  type OverallHealth,
  type SubsystemHealth,
} from "~/lib/arcadia/health"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterContext } from "@crema/aifirst-ui/context"

export const meta = () => pageTitle("Monitoring")

interface DashboardData {
  jobStats: JobStats | null
  recentJobs: ObanJob[]
  sessions: { sessions: ActiveSession[]; count: number } | null
  rateLimits: RateLimit[]
  infraSummary: InfrastructureSummary | null
  spaces: Space[]
  droplets: Droplet[]
  auditStats: AuditStats | null
  health: OverallHealth | null
  host: HostStats | null
}

const EMPTY: DashboardData = {
  jobStats: null,
  recentJobs: [],
  sessions: null,
  rateLimits: [],
  infraSummary: null,
  spaces: [],
  droplets: [],
  auditStats: null,
  health: null,
  host: null,
}

export default function MonitoringRoute() {
  const session = useSession()
  const arcadia = useArcadiaClient()

  const [data, setData] = useState<DashboardData>(EMPTY)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [autoRefresh, setAutoRefresh] = useState(true)

  const refresh = useCallback(async () => {
    setError(null)
    setLoading(true)
    try {
      const [
        jobStats,
        recentJobs,
        sessions,
        rateLimits,
        infraSummary,
        spaces,
        droplets,
        auditStats,
        health,
        host,
      ] = await Promise.all([
        getJobStats(arcadia).catch(() => null),
        getRecentJobs(arcadia, { limit: 50 }).catch(() => []),
        getActiveSessions(arcadia).catch(() => null),
        getRateLimits(arcadia).catch(() => []),
        getInfrastructureSummary(arcadia),
        getSpaces(arcadia),
        listDroplets(arcadia),
        getAuditStats(arcadia, {
          from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
        }).catch(() => null),
        getHealth(arcadia).catch(() => null),
        getHostStats(arcadia).catch(() => null),
      ])
      setData({
        jobStats,
        recentJobs,
        sessions,
        rateLimits,
        infraSummary,
        spaces,
        droplets,
        auditStats,
        health,
        host,
      })
    } catch (err) {
      setError(err instanceof ArcadiaError ? err.message : "Failed to load monitoring data.")
    } finally {
      setLoading(false)
    }
  }, [arcadia])

  useEffect(() => {
    if (session) refresh()
  }, [session, refresh])

  // Auto-refresh every 30s for the live feel.
  useEffect(() => {
    if (!session || !autoRefresh) return
    const t = setInterval(refresh, 30000)
    return () => clearInterval(t)
  }, [session, autoRefresh, refresh])

  const components = useMemo(() => buildStatusComponents(data), [data])

  const summary = useMemo(
    () => ({
      jobs: data.jobStats?.counts ?? {},
      jobs_executing: data.jobStats?.counts?.executing ?? 0,
      jobs_retryable: data.jobStats?.counts?.retryable ?? 0,
      sessions_24h: data.sessions?.count ?? 0,
      droplets: data.droplets.length,
      spaces: data.spaces.length,
      audit_total_7d: data.auditStats?.total ?? 0,
    }),
    [data],
  )
  useRegisterContext("monitoring", summary)

  return (
    <AppShell>
      <div className="flex flex-col gap-4">
        <header className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
          <div>
            <h1 className="text-2xl font-semibold tracking-tight">Server stats &amp; health</h1>
            <p className="text-sm text-muted-foreground">
              Live view of background jobs, active sessions, infrastructure, and audit
              activity. Refreshes every 30s.
            </p>
          </div>
          <div className="flex items-center gap-2">
            <Button
              variant={autoRefresh ? "outline" : "ghost"}
              size="sm"
              onClick={() => setAutoRefresh((v) => !v)}
              data-action="monitoring-auto-refresh"
            >
              <RotateCw className={`size-4 ${autoRefresh ? "animate-pulse" : ""}`} />
              Auto-refresh {autoRefresh ? "on" : "off"}
            </Button>
            <Button
              size="sm"
              onClick={refresh}
              disabled={loading}
              data-action="monitoring-refresh"
            >
              <RefreshCw className={`size-4 ${loading ? "animate-spin" : ""}`} />
              Refresh
            </Button>
          </div>
        </header>

        {error ? (
          <AlertBanner variant="error" dismissible onDismiss={() => setError(null)}>
            {error}
          </AlertBanner>
        ) : null}

        {/* Service status board */}
        <Card>
          <CardHeader className="flex flex-row items-center justify-between">
            <div>
              <CardTitle>Service health</CardTitle>
              <CardDescription>
                {data.health
                  ? `Live probes from /api/v1/health · checked ${new Date(
                      data.health.checked_at,
                    ).toLocaleTimeString()}`
                  : "Live probes from /api/v1/health (unavailable — backend may be down or older than the per-subsystem probe rollout)."}
              </CardDescription>
            </div>
            <OverallStatus components={components} />
          </CardHeader>
          <CardContent className="flex flex-col gap-1">
            {components.map((c) => (
              <ComponentRow key={c.id} component={c} showUptime={false} />
            ))}
          </CardContent>
        </Card>

        {/* KPI tiles */}
        <div className="grid grid-cols-2 gap-3 md:grid-cols-4">
          <KpiTile
            label="Active sessions (24h)"
            value={formatCompact(data.sessions?.count ?? 0)}
            icon={<Users className="size-4" />}
          />
          <KpiTile
            label="Jobs executing"
            value={String(data.jobStats?.counts?.executing ?? 0)}
            icon={<Zap className="size-4" />}
            tone={
              (data.jobStats?.counts?.executing ?? 0) > 0 ? "info" : "neutral"
            }
          />
          <KpiTile
            label="Retryable jobs"
            value={String(data.jobStats?.counts?.retryable ?? 0)}
            icon={<AlertTriangle className="size-4" />}
            tone={
              (data.jobStats?.counts?.retryable ?? 0) > 0 ? "warning" : "neutral"
            }
          />
          <KpiTile
            label="Audit events (7d)"
            value={formatCompact(data.auditStats?.total ?? 0)}
            icon={<Activity className="size-4" />}
          />
        </div>

        {data.host ? (
          <div className="grid grid-cols-2 gap-3 md:grid-cols-4">
            <KpiTile
              label="CPU usage"
              value={
                data.host.cpu.util_pct != null
                  ? formatPercent(data.host.cpu.util_pct / 100)
                  : "—"
              }
              icon={<Cpu className="size-4" />}
              tone={
                (data.host.cpu.util_pct ?? 0) > 90
                  ? "negative"
                  : (data.host.cpu.util_pct ?? 0) > 70
                    ? "warning"
                    : "neutral"
              }
            />
            <KpiTile
              label="Load avg (1m)"
              value={
                data.host.cpu.load_avg_1 != null
                  ? data.host.cpu.load_avg_1.toFixed(2)
                  : "—"
              }
              icon={<Activity className="size-4" />}
              tone={
                data.host.cpu.load_avg_1 != null &&
                data.host.cpu.num_cpus &&
                data.host.cpu.load_avg_1 > data.host.cpu.num_cpus
                  ? "warning"
                  : "neutral"
              }
            />
            <KpiTile
              label="Memory used"
              value={memoryUsedLabel(data.host.memory)}
              icon={<HardDrive className="size-4" />}
              tone={
                memoryUsedPct(data.host.memory) > 90
                  ? "negative"
                  : memoryUsedPct(data.host.memory) > 75
                    ? "warning"
                    : "neutral"
              }
            />
            <KpiTile
              label="Disk (busiest mount)"
              value={busiestDiskLabel(data.host.disks)}
              icon={<Database className="size-4" />}
              tone={
                busiestDiskPct(data.host.disks) > 90
                  ? "negative"
                  : busiestDiskPct(data.host.disks) > 75
                    ? "warning"
                    : "neutral"
              }
            />
          </div>
        ) : null}

        <Tabs defaultValue="host">
          <TabsList>
            <TabsTrigger value="host" data-action="monitoring-tab-host">
              Host
            </TabsTrigger>
            <TabsTrigger value="jobs" data-action="monitoring-tab-jobs">
              Background jobs
            </TabsTrigger>
            <TabsTrigger value="sessions" data-action="monitoring-tab-sessions">
              Sessions
            </TabsTrigger>
            <TabsTrigger value="audit" data-action="monitoring-tab-audit">
              Audit activity
            </TabsTrigger>
            <TabsTrigger value="infra" data-action="monitoring-tab-infra">
              Infrastructure
            </TabsTrigger>
            <TabsTrigger value="rate-limits" data-action="monitoring-tab-rate-limits">
              Rate limits
            </TabsTrigger>
          </TabsList>

          <TabsContent value="host" className="pt-4">
            <HostPanel host={data.host} />
          </TabsContent>

          <TabsContent value="jobs" className="pt-4">
            <JobsPanel
              stats={data.jobStats}
              recent={data.recentJobs}
              onRetry={async (id) => {
                try {
                  await retryJob(arcadia, id)
                  await refresh()
                } catch (err) {
                  setError(
                    err instanceof ArcadiaError ? err.message : "Retry failed.",
                  )
                }
              }}
            />
          </TabsContent>

          <TabsContent value="sessions" className="pt-4">
            <SessionsPanel sessions={data.sessions} />
          </TabsContent>

          <TabsContent value="audit" className="pt-4">
            <AuditPanel stats={data.auditStats} />
          </TabsContent>

          <TabsContent value="infra" className="pt-4">
            <InfraPanel
              summary={data.infraSummary}
              spaces={data.spaces}
              droplets={data.droplets}
            />
          </TabsContent>

          <TabsContent value="rate-limits" className="pt-4">
            <RateLimitsPanel limits={data.rateLimits} />
          </TabsContent>
        </Tabs>
      </div>
    </AppShell>
  )
}

// Map arcadia /health probe results onto the status-ui component model.
// "ok" → operational, "degraded" → degraded, "error" → partial-outage,
// "unconfigured" → operational (storage with no configured backend is ok).
function buildStatusComponents(d: DashboardData): StatusComponent[] {
  const subsystems = d.health?.subsystems
  const meta: Record<HealthSubsystem, { name: string; description: string }> = {
    api: { name: "API", description: "/api/v1 — auth, REST endpoints" },
    db: { name: "Database", description: "Postgres — sessions, audit log" },
    workers: {
      name: "Background workers",
      description: "Oban — webhook delivery, scheduled tasks",
    },
    storage: {
      name: "Storage",
      description: "S3-compatible object storage (per platform default)",
    },
  }

  return SUBSYSTEMS.map((id) => {
    const probe = subsystems?.[id]
    return {
      id,
      name: meta[id].name,
      description: probe?.message ?? meta[id].description,
      state: probe ? mapHealthState(probe) : "partial-outage",
    } satisfies StatusComponent
  })
}

function mapHealthState(probe: SubsystemHealth): ComponentState {
  switch (probe.status) {
    case "ok":
    case "unconfigured":
      return "operational"
    case "degraded":
      return "degraded"
    case "error":
      return "major-outage"
    default:
      return "partial-outage"
  }
}

// --- Host panel --------------------------------------------------------

function HostPanel({ host }: { host: HostStats | null }) {
  if (!host) {
    return (
      <PanelStub
        icon={<Cpu className="size-5" />}
        text="Host stats unavailable. The /api/v1/health/host endpoint may not be deployed yet, or os_mon daemons aren't reachable."
      />
    )
  }

  const memUsed = memoryUsedBytes(host.memory)
  const memTotal = host.memory.total_bytes ?? null
  const memPct = memoryUsedPct(host.memory)
  const swapTotal = host.memory.swap_total_bytes ?? null
  const swapUsed =
    swapTotal != null && host.memory.swap_free_bytes != null
      ? swapTotal - host.memory.swap_free_bytes
      : null

  return (
    <div className="flex flex-col gap-4">
      {/* CPU + load */}
      <div className="grid grid-cols-1 gap-3 lg:grid-cols-2">
        <Card>
          <CardHeader>
            <CardTitle className="text-base">CPU</CardTitle>
            <CardDescription>
              {host.cpu.num_cpus
                ? `${host.cpu.num_cpus} cores · ${host.cpu.schedulers_online} BEAM schedulers online`
                : `${host.cpu.schedulers_online} BEAM schedulers online`}
            </CardDescription>
          </CardHeader>
          <CardContent className="flex flex-col gap-3">
            <UsageBar
              label="Overall utilisation"
              pct={host.cpu.util_pct ?? null}
              valueText={
                host.cpu.util_pct != null ? `${host.cpu.util_pct.toFixed(1)}%` : "—"
              }
            />
            {host.cpu.per_cpu_pct.length > 0 ? (
              <div className="flex flex-col gap-1">
                <span className="text-xs text-muted-foreground">Per core</span>
                <div className="flex flex-wrap gap-1.5">
                  {host.cpu.per_cpu_pct.map((p, i) => (
                    <div
                      key={i}
                      className="flex h-6 w-12 items-center justify-center rounded text-[11px] font-mono"
                      style={{
                        background: `linear-gradient(to right, var(--primary) ${p}%, var(--muted) ${p}%)`,
                        color: p > 50 ? "var(--primary-foreground)" : "var(--foreground)",
                      }}
                      title={`Core ${i}: ${p.toFixed(1)}%`}
                    >
                      {p.toFixed(0)}
                    </div>
                  ))}
                </div>
              </div>
            ) : null}
          </CardContent>
        </Card>

        <Card>
          <CardHeader>
            <CardTitle className="text-base">Load average</CardTitle>
            <CardDescription>
              Unix-style load average. A value above the core count means the
              run-queue is saturated.
            </CardDescription>
          </CardHeader>
          <CardContent>
            <div className="grid grid-cols-3 gap-3">
              <LoadAvgCell label="1 min" value={host.cpu.load_avg_1} cores={host.cpu.num_cpus} />
              <LoadAvgCell label="5 min" value={host.cpu.load_avg_5} cores={host.cpu.num_cpus} />
              <LoadAvgCell label="15 min" value={host.cpu.load_avg_15} cores={host.cpu.num_cpus} />
            </div>
          </CardContent>
        </Card>
      </div>

      {/* Memory */}
      <Card>
        <CardHeader>
          <CardTitle className="text-base">Memory</CardTitle>
          <CardDescription>
            {memTotal != null ? `${formatBytes(memTotal)} total` : "Total memory unknown"}
            {host.memory.available_bytes != null
              ? ` · ${formatBytes(host.memory.available_bytes)} available`
              : ""}
          </CardDescription>
        </CardHeader>
        <CardContent className="flex flex-col gap-3">
          <UsageBar
            label="Used"
            pct={memPct}
            valueText={
              memUsed != null && memTotal != null
                ? `${formatBytes(memUsed)} / ${formatBytes(memTotal)} (${memPct.toFixed(1)}%)`
                : "—"
            }
          />
          {(host.memory.buffered_bytes != null || host.memory.cached_bytes != null) && (
            <div className="grid grid-cols-2 gap-2 text-xs text-muted-foreground">
              {host.memory.buffered_bytes != null && (
                <span>Buffered: {formatBytes(host.memory.buffered_bytes)}</span>
              )}
              {host.memory.cached_bytes != null && (
                <span>Cached: {formatBytes(host.memory.cached_bytes)}</span>
              )}
            </div>
          )}
          {swapTotal != null && swapTotal > 0 ? (
            <UsageBar
              label="Swap"
              pct={swapUsed != null ? (swapUsed / swapTotal) * 100 : null}
              valueText={
                swapUsed != null
                  ? `${formatBytes(swapUsed)} / ${formatBytes(swapTotal)}`
                  : "—"
              }
            />
          ) : null}
        </CardContent>
      </Card>

      {/* Disks */}
      <Card>
        <CardHeader>
          <CardTitle className="text-base">Disks</CardTitle>
          <CardDescription>One row per mount point.</CardDescription>
        </CardHeader>
        <CardContent>
          {host.disks.length === 0 ? (
            <p className="py-4 text-sm text-muted-foreground">No disks reported.</p>
          ) : (
            <div className="flex flex-col gap-2">
              {host.disks.map((d) => (
                <UsageBar
                  key={d.mount}
                  label={d.mount}
                  pct={d.used_pct}
                  valueText={`${d.used_pct}% of ${formatBytes(d.total_kb * 1024)}`}
                />
              ))}
            </div>
          )}
        </CardContent>
      </Card>
    </div>
  )
}

function UsageBar({
  label,
  pct,
  valueText,
}: {
  label: string
  pct: number | null
  valueText: string
}) {
  const clamped = pct == null ? 0 : Math.max(0, Math.min(100, pct))
  const tone = pct == null ? "var(--muted-foreground)" : barColor(pct)
  return (
    <div className="flex flex-col gap-1">
      <div className="flex items-baseline justify-between gap-2 text-xs">
        <span className="font-medium">{label}</span>
        <span className="font-mono text-muted-foreground">{valueText}</span>
      </div>
      <div className="h-2 w-full overflow-hidden rounded-full bg-muted">
        <div
          className="h-full rounded-full transition-[width] duration-500"
          style={{ width: `${clamped}%`, background: tone }}
        />
      </div>
    </div>
  )
}

function LoadAvgCell({
  label,
  value,
  cores,
}: {
  label: string
  value: number | null
  cores: number | null
}) {
  const saturated = value != null && cores != null && value > cores
  return (
    <div className="flex flex-col items-start gap-0.5 rounded-md border bg-card p-3">
      <span className="text-xs text-muted-foreground">{label}</span>
      <span
        className="font-mono text-2xl font-semibold tabular-nums"
        style={{ color: saturated ? "var(--destructive)" : "var(--foreground)" }}
      >
        {value != null ? value.toFixed(2) : "—"}
      </span>
      {cores ? (
        <span className="text-[11px] text-muted-foreground">/ {cores} cores</span>
      ) : null}
    </div>
  )
}

function memoryUsedBytes(m: HostStats["memory"]): number | null {
  if (m.total_bytes == null) return null
  // Prefer "available" over "free" — on Linux, free excludes reclaimable
  // buffer/cache memory and overstates pressure.
  const available = m.available_bytes ?? m.free_bytes
  if (available == null) return null
  return Math.max(0, m.total_bytes - available)
}

function memoryUsedPct(m: HostStats["memory"]): number {
  const used = memoryUsedBytes(m)
  if (used == null || m.total_bytes == null || m.total_bytes === 0) return 0
  return (used / m.total_bytes) * 100
}

function memoryUsedLabel(m: HostStats["memory"]): string {
  const used = memoryUsedBytes(m)
  if (used == null || m.total_bytes == null) return "—"
  return `${formatBytes(used)} / ${formatBytes(m.total_bytes)}`
}

function busiestDiskPct(disks: HostStats["disks"]): number {
  return disks.reduce((m, d) => Math.max(m, d.used_pct), 0)
}

function busiestDiskLabel(disks: HostStats["disks"]): string {
  if (disks.length === 0) return "—"
  const busiest = disks.reduce((a, b) => (b.used_pct > a.used_pct ? b : a))
  return `${busiest.used_pct}% (${busiest.mount})`
}

function barColor(pct: number): string {
  if (pct >= 90) return "var(--destructive)"
  if (pct >= 75) return "#f59e0b"
  return "var(--primary)"
}

// --- Jobs panel --------------------------------------------------------

function JobsPanel({
  stats,
  recent,
  onRetry,
}: {
  stats: JobStats | null
  recent: ObanJob[]
  onRetry: (id: number) => Promise<void>
}) {
  if (!stats) {
    return <PanelStub icon={<Server className="size-5" />} text="No job stats available." />
  }

  const stateData: ChartDatum[] = (Object.entries(stats.counts) as [string, number][])
    .filter(([, n]) => n > 0)
    .map(([state, n]) => ({ label: state, value: n, color: jobStateColor(state) }))

  const queueData: ChartDatum[] = stats.queues.map((q) => {
    const totals = stats.by_queue[q] ?? {}
    const sum = Object.values(totals).reduce<number>((a, n) => a + (n ?? 0), 0)
    return { label: q, value: sum }
  })

  return (
    <div className="flex flex-col gap-4">
      <div className="grid grid-cols-1 gap-3 lg:grid-cols-3">
        <Card>
          <CardHeader>
            <CardTitle className="text-base">Jobs by state</CardTitle>
          </CardHeader>
          <CardContent className="flex items-center justify-center">
            {stateData.length === 0 ? (
              <p className="py-6 text-sm text-muted-foreground">No active jobs.</p>
            ) : (
              <Donut data={stateData} size={180} thickness={28} />
            )}
            <ul className="ml-4 flex flex-col gap-1 text-xs">
              {stateData.map((d) => (
                <li key={d.label} className="flex items-center gap-2">
                  <span
                    className="size-2.5 rounded-full"
                    style={{ background: d.color }}
                    aria-hidden
                  />
                  <span className="font-mono">{d.label}</span>
                  <span className="ml-auto text-muted-foreground">{d.value}</span>
                </li>
              ))}
            </ul>
          </CardContent>
        </Card>

        <Card className="lg:col-span-2">
          <CardHeader>
            <CardTitle className="text-base">Active jobs by queue</CardTitle>
          </CardHeader>
          <CardContent>
            {queueData.length === 0 ? (
              <p className="py-6 text-center text-sm text-muted-foreground">
                No queued or executing jobs.
              </p>
            ) : (
              <BarChart data={queueData} width={520} height={180} />
            )}
          </CardContent>
        </Card>
      </div>

      <Card>
        <CardHeader>
          <CardTitle className="text-base">Recent jobs</CardTitle>
          <CardDescription>Latest 50 — newest first.</CardDescription>
        </CardHeader>
        <CardContent className="p-0">
          {recent.length === 0 ? (
            <p className="py-6 text-center text-sm text-muted-foreground">No recent jobs.</p>
          ) : (
            <ul className="divide-y border-y">
              {recent.map((j) => (
                <li key={j.id} className="flex items-center justify-between gap-3 px-3 py-2">
                  <div className="flex flex-col gap-0.5">
                    <span className="flex items-center gap-2 text-sm">
                      <Badge variant={jobStateVariant(j.state)}>{j.state}</Badge>
                      <code className="font-mono text-xs">{j.worker}</code>
                      <span className="text-xs text-muted-foreground">queue: {j.queue}</span>
                    </span>
                    <span className="text-[11px] text-muted-foreground">
                      attempt {j.attempt}/{j.max_attempts} · inserted{" "}
                      {new Date(j.inserted_at).toLocaleString()}
                      {j.completed_at
                        ? ` · completed ${new Date(j.completed_at).toLocaleString()}`
                        : ""}
                    </span>
                    {j.errors && j.errors.length > 0 ? (
                      <span className="text-xs text-destructive">
                        {j.errors[j.errors.length - 1]?.error ?? "(error)"}
                      </span>
                    ) : null}
                  </div>
                  {j.state === "retryable" || j.state === "discarded" ? (
                    <Button
                      variant="outline"
                      size="sm"
                      onClick={() => onRetry(j.id)}
                      data-action={`monitoring-job-${j.id}-retry`}
                    >
                      <RotateCw className="size-3.5" />
                      Retry
                    </Button>
                  ) : null}
                </li>
              ))}
            </ul>
          )}
        </CardContent>
      </Card>
    </div>
  )
}

// --- Sessions panel ----------------------------------------------------

function SessionsPanel({
  sessions,
}: {
  sessions: { sessions: ActiveSession[]; count: number } | null
}) {
  if (!sessions) {
    return <PanelStub icon={<Users className="size-5" />} text="No session data available." />
  }

  // Bucket sign-ins by hour for a 24h sparkline.
  const hourly = useMemo(() => {
    const now = Date.now()
    const buckets = Array.from({ length: 24 }, (_, i) => ({
      x: i,
      y: 0,
    }))
    for (const s of sessions.sessions) {
      const t = new Date(s.last_sign_in_at).getTime()
      const ago = (now - t) / (60 * 60 * 1000)
      const idx = 23 - Math.floor(ago)
      if (idx >= 0 && idx < 24) buckets[idx].y++
    }
    return buckets
  }, [sessions])

  return (
    <div className="flex flex-col gap-4">
      <Card>
        <CardHeader className="flex flex-row items-center justify-between">
          <div>
            <CardTitle className="text-base">Sign-ins over the last 24h</CardTitle>
            <CardDescription>One bar per hour, latest on the right.</CardDescription>
          </div>
          <Sparkline data={hourly} width={240} height={48} stroke="var(--primary)" />
        </CardHeader>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="text-base">{sessions.count} recent sessions</CardTitle>
        </CardHeader>
        <CardContent className="p-0">
          {sessions.sessions.length === 0 ? (
            <p className="py-6 text-center text-sm text-muted-foreground">
              No sign-ins in the last 24 hours.
            </p>
          ) : (
            <ul className="divide-y border-y">
              {sessions.sessions.map((s) => (
                <li
                  key={s.user_id}
                  className="flex items-center justify-between gap-3 px-3 py-2"
                >
                  <div className="flex flex-col gap-0.5">
                    <span className="text-sm font-medium">{s.email}</span>
                    <span className="text-xs text-muted-foreground">
                      {s.first_name || s.last_name
                        ? `${s.first_name ?? ""} ${s.last_name ?? ""}`.trim() + " · "
                        : ""}
                      {s.user_type ?? "user"} · status: {s.status}
                      {s.two_factor_enabled ? " · 2FA" : ""}
                    </span>
                  </div>
                  <span className="text-xs text-muted-foreground">
                    {new Date(s.last_sign_in_at).toLocaleString()}
                  </span>
                </li>
              ))}
            </ul>
          )}
        </CardContent>
      </Card>
    </div>
  )
}

// --- Audit panel -------------------------------------------------------

function AuditPanel({ stats }: { stats: AuditStats | null }) {
  if (!stats) {
    return <PanelStub icon={<Activity className="size-5" />} text="No audit stats available." />
  }

  const bySeverity: ChartDatum[] = Object.entries(stats.by_severity ?? {}).map(
    ([k, v]) => ({ label: k, value: v, color: severityColor(k) }),
  )
  const byResource: ChartDatum[] = Object.entries(stats.by_resource_type ?? {})
    .sort((a, b) => b[1] - a[1])
    .slice(0, 10)
    .map(([k, v]) => ({ label: k, value: v }))

  // Time series — only render if backend supplied it.
  const series: SeriesPoint[] | null = stats.over_time
    ? stats.over_time.map((p, i) => ({ x: i, y: p.total }))
    : null

  return (
    <div className="grid grid-cols-1 gap-3 lg:grid-cols-2">
      <Card>
        <CardHeader>
          <CardTitle className="text-base">Events by severity (7d)</CardTitle>
        </CardHeader>
        <CardContent>
          {bySeverity.length === 0 ? (
            <p className="py-6 text-center text-sm text-muted-foreground">No events.</p>
          ) : (
            <BarChart data={bySeverity} width={500} height={180} />
          )}
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="text-base">Top resource types</CardTitle>
        </CardHeader>
        <CardContent>
          {byResource.length === 0 ? (
            <p className="py-6 text-center text-sm text-muted-foreground">No events.</p>
          ) : (
            <BarChart data={byResource} width={500} height={Math.max(180, byResource.length * 22)} />
          )}
        </CardContent>
      </Card>

      {series ? (
        <Card className="lg:col-span-2">
          <CardHeader>
            <CardTitle className="text-base">Events over time</CardTitle>
          </CardHeader>
          <CardContent>
            <LineChart data={series} width={900} height={200} />
          </CardContent>
        </Card>
      ) : null}
    </div>
  )
}

// --- Infrastructure panel ----------------------------------------------

function InfraPanel({
  summary,
  spaces,
  droplets,
}: {
  summary: InfrastructureSummary | null
  spaces: Space[]
  droplets: Droplet[]
}) {
  const dropletsByRegion = useMemo(() => {
    const out: Record<string, number> = {}
    for (const d of droplets) {
      const r =
        typeof d.region === "string"
          ? d.region
          : d.region?.slug ?? d.region?.name ?? "unknown"
      out[r] = (out[r] ?? 0) + 1
    }
    return out
  }, [droplets])

  if (!summary && spaces.length === 0 && droplets.length === 0) {
    return (
      <PanelStub
        icon={<Globe className="size-5" />}
        text="No infrastructure connected. Wire a DigitalOcean token in arcadia's .env to see this section populate."
      />
    )
  }

  return (
    <div className="flex flex-col gap-4">
      {summary ? (
        <Card>
          <CardHeader>
            <CardTitle className="text-base">DigitalOcean summary</CardTitle>
          </CardHeader>
          <CardContent>
            <pre className="overflow-x-auto rounded-md border bg-muted/50 p-3 text-xs">
              {JSON.stringify(summary, null, 2)}
            </pre>
          </CardContent>
        </Card>
      ) : null}

      <div className="grid grid-cols-1 gap-3 lg:grid-cols-3">
        <KpiTile
          label="Droplets"
          value={String(droplets.length)}
          icon={<Server className="size-4" />}
        />
        <KpiTile
          label="Spaces"
          value={String(spaces.length)}
          icon={<HardDrive className="size-4" />}
        />
        <KpiTile
          label="Regions in use"
          value={String(Object.keys(dropletsByRegion).length)}
          icon={<Globe className="size-4" />}
        />
      </div>

      {droplets.length > 0 ? (
        <div className="grid grid-cols-1 gap-3 lg:grid-cols-2">
          <Card>
            <CardHeader>
              <CardTitle className="text-base">Droplet regions</CardTitle>
              <CardDescription>Coloured continents indicate any droplets in that hemisphere.</CardDescription>
            </CardHeader>
            <CardContent>
              <WorldMapSvg
                regionColors={regionColorsFor(dropletsByRegion)}
                className="w-full"
              />
            </CardContent>
          </Card>

          <Card>
            <CardHeader>
              <CardTitle className="text-base">Droplets ({droplets.length})</CardTitle>
            </CardHeader>
            <CardContent className="p-0">
              <ul className="divide-y border-y">
                {droplets.slice(0, 20).map((d) => (
                  <li
                    key={String(d.id)}
                    className="flex items-center justify-between gap-3 px-3 py-2"
                  >
                    <div className="flex flex-col gap-0.5">
                      <span className="flex items-center gap-2 text-sm font-medium">
                        <Cpu className="size-3.5 text-muted-foreground" />
                        {d.name}
                      </span>
                      <span className="text-xs text-muted-foreground">
                        {typeof d.region === "string"
                          ? d.region
                          : d.region?.slug ?? "—"}
                        {d.size_slug ? ` · ${d.size_slug}` : ""}
                        {d.vcpus ? ` · ${d.vcpus} vCPU` : ""}
                        {d.memory ? ` · ${formatBytes(d.memory * 1024 * 1024)}` : ""}
                      </span>
                    </div>
                    <Badge
                      variant={d.status === "active" ? "default" : "secondary"}
                    >
                      {d.status}
                    </Badge>
                  </li>
                ))}
                {droplets.length > 20 ? (
                  <li className="px-3 py-2 text-center text-xs text-muted-foreground">
                    + {droplets.length - 20} more
                  </li>
                ) : null}
              </ul>
            </CardContent>
          </Card>
        </div>
      ) : null}

      {spaces.length > 0 ? (
        <Card>
          <CardHeader>
            <CardTitle className="text-base">Spaces ({spaces.length})</CardTitle>
          </CardHeader>
          <CardContent>
            <pre className="overflow-x-auto rounded-md border bg-muted/50 p-3 text-xs">
              {JSON.stringify(spaces, null, 2)}
            </pre>
          </CardContent>
        </Card>
      ) : null}
    </div>
  )
}

function regionColorsFor(byRegion: Record<string, number>): Record<string, string> {
  // Best-effort mapping from DO region slugs to continent IDs the WorldMapSvg knows.
  // The lib exposes regions like "north-america", "europe", "asia", etc.
  const colors: Record<string, string> = {}
  const continentOf = (r: string): string | null => {
    const lc = r.toLowerCase()
    if (/^(nyc|sfo|tor|ams_tor)/.test(lc)) return "north-america"
    if (/^(lon|ams|fra)/.test(lc)) return "europe"
    if (/^(blr|sgp)/.test(lc)) return "asia"
    if (/^(syd)/.test(lc)) return "oceania"
    return null
  }
  for (const r of Object.keys(byRegion)) {
    const c = continentOf(r)
    if (c) colors[c] = "var(--primary)"
  }
  return colors
}

// --- Rate limits panel -------------------------------------------------

function RateLimitsPanel({ limits }: { limits: RateLimit[] }) {
  if (limits.length === 0) {
    return (
      <PanelStub
        icon={<Database className="size-5" />}
        text="No rate-limit configuration available."
      />
    )
  }
  return (
    <Card>
      <CardHeader>
        <CardTitle className="text-base">Configured rate limits</CardTitle>
        <CardDescription>
          The maximum requests allowed per window for each authenticated bucket.
        </CardDescription>
      </CardHeader>
      <CardContent className="p-0">
        <ul className="divide-y border-y">
          {limits.map((l) => (
            <li key={l.type} className="flex items-center justify-between gap-3 px-3 py-2">
              <div className="flex flex-col">
                <span className="text-sm font-medium capitalize">{l.type}</span>
                <span className="text-xs text-muted-foreground">
                  Window: {l.window_seconds}s
                </span>
              </div>
              <span className="font-mono text-sm">
                {l.max_requests.toLocaleString()} req
              </span>
            </li>
          ))}
        </ul>
      </CardContent>
    </Card>
  )
}

// --- helpers ----------------------------------------------------------

function PanelStub({ icon, text }: { icon: React.ReactNode; text: string }) {
  return (
    <Card>
      <CardContent className="flex items-center gap-3 py-8 text-sm text-muted-foreground">
        {icon}
        {text}
      </CardContent>
    </Card>
  )
}

function jobStateColor(state: string): string {
  switch (state) {
    case "executing":
      return "#3b82f6"
    case "available":
    case "scheduled":
      return "#a3a3a3"
    case "retryable":
      return "#f59e0b"
    case "discarded":
      return "#ef4444"
    case "cancelled":
      return "#737373"
    case "completed":
      return "#10b981"
    default:
      return "#9ca3af"
  }
}

function jobStateVariant(
  state: string,
): "default" | "secondary" | "destructive" | "outline" {
  if (state === "executing" || state === "completed") return "default"
  if (state === "discarded") return "destructive"
  if (state === "retryable" || state === "scheduled") return "secondary"
  return "outline"
}

function severityColor(s: string): string {
  if (s === "critical" || s === "error") return "#ef4444"
  if (s === "warning") return "#f59e0b"
  return "#94a3b8"
}
