"use client";

import { useMemo, useState } from "react";

import { AgentFeatures } from "@/components/AgentFeatures";
import { AgentLink } from "@/components/AgentLink";
import { ClientPager } from "@/components/ClientPager";
import { EmptyPanel, Td, Th } from "@/components/DataPanel";
import { LabelChips } from "@/components/Labels";
import { ListToolbar } from "@/components/ListToolbar";
import { SortHeaderButton, type SortDir } from "@/components/SortHeaderButton";
import { DateTime } from "@/components/TimeZoneControls";
import type { components } from "@/lib/api-types";
import { statusBadgeClass } from "@/lib/format";
import {
  DEFAULT_PAGE_SIZE,
  clampPage,
  pageCountOf,
  paginate,
  type PageSize,
} from "@/lib/pagination";
import { usePolling } from "@/lib/usePolling";
import type { InventoryData } from "@/lib/view-types";

type Agent = components["schemas"]["Agent"];
type CheckState = components["schemas"]["CheckState"];
type CheckStatus = CheckState["status"];
type CheckCounts = Record<CheckStatus, number>;
type SortField = "host" | "status" | "last_seen_at";

export type AgentRow = { agent: Agent; checks: CheckCounts };

const checkStatuses: CheckStatus[] = ["ok", "warning", "critical", "unknown"];
const statusRank: Record<Agent["status"], number> = { alive: 0, stale: 1, dead: 2 };
const checkSortStatuses: CheckStatus[] = ["critical", "warning", "unknown", "ok"];

export function AgentsBrowser({ initialData }: { initialData: InventoryData }) {
  const { data } = usePolling(initialData, "/api/poll/inventory");
  const [query, setQuery] = useState("");
  const [sort, setSort] = useState<SortField>("status");
  const [dir, setDir] = useState<SortDir>("desc");
  const initialRows = useMemo(() => rowsFromInventory(data), [data]);

  const rows = useMemo(
    () => sortAgents(filterAgents(initialRows, query), sort, dir),
    [dir, initialRows, query, sort],
  );

  const [pageSize, setPageSize] = useState<PageSize>(DEFAULT_PAGE_SIZE);
  const [page, setPage] = useState(1);
  const pageCount = pageCountOf(rows.length, pageSize);
  const currentPage = clampPage(page, pageCount);
  const visible = paginate(rows, currentPage, pageSize);

  const onQueryChange = (value: string) => {
    setQuery(value);
    setPage(1);
  };
  const onPageSizeChange = (next: PageSize) => {
    setPageSize(next);
    setPage(1);
  };
  const chooseSort = (field: SortField) => {
    if (field === sort) {
      setDir((current) => (current === "asc" ? "desc" : "asc"));
    } else {
      setSort(field);
      setDir(fieldDefaultDir(field));
    }
    setPage(1);
  };

  return (
    <>
      <div className="mb-4 flex items-baseline gap-3">
        <h1 className="text-lg font-semibold">Agents</h1>
        <span className="text-sm text-neutral-400">
          {rows.length} of {initialRows.length} items
        </span>
      </div>
      <ListToolbar
        query={query}
        onQueryChange={onQueryChange}
        placeholder="search agent"
        pageSize={pageSize}
        onPageSizeChange={onPageSizeChange}
      />
      {rows.length === 0 ? (
        <EmptyPanel />
      ) : (
        <table className="w-full text-sm">
          <thead>
            <tr>
              <Th>
                <SortHeaderButton field="host" current={sort} dir={dir} onClick={chooseSort}>
                  HOST
                </SortHeaderButton>
              </Th>
              <Th>
                <SortHeaderButton field="status" current={sort} dir={dir} onClick={chooseSort}>
                  STATUS
                </SortHeaderButton>
              </Th>
              <Th>
                <SortHeaderButton field="last_seen_at" current={sort} dir={dir} onClick={chooseSort}>
                  LAST_SEEN_AT
                </SortHeaderButton>
              </Th>
              <Th>features</Th>
              <Th>labels</Th>
            </tr>
          </thead>
          <tbody>
            {visible.map(({ agent: a, checks }) => (
              <tr key={a.agent_id}>
                <Td>
                  <AgentLink agent={a} agentId={a.agent_id} />
                </Td>
                <Td>
                  <AgentStatus status={a.status} checks={checks} />
                </Td>
                <Td>
                  <DateTime iso={a.last_seen_at} />
                </Td>
                <Td>
                  <AgentFeatures features={a.features} />
                </Td>
                <Td>
                  <LabelChips labels={a.labels} limit={4} />
                </Td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
      <ClientPager page={currentPage} pageCount={pageCount} onPageChange={setPage} />
    </>
  );
}

function filterAgents(rows: AgentRow[], query: string): AgentRow[] {
  const needle = query.trim().toLowerCase();
  if (!needle) return rows;
  return rows.filter(({ agent }) =>
    `${agent.hostname} ${agent.agent_id}`.toLowerCase().includes(needle),
  );
}

function fieldDefaultDir(field: SortField): SortDir {
  return field === "host" ? "asc" : "desc";
}

function sortAgents(rows: AgentRow[], sort: SortField, dir: SortDir): AgentRow[] {
  return [...rows].sort((a, b) => {
    const primary = compareByField(a, b, sort);
    if (primary !== 0) return dir === "asc" ? primary : -primary;
    return hostName(a.agent).localeCompare(hostName(b.agent)) || a.agent.agent_id.localeCompare(b.agent.agent_id);
  });
}

function compareByField(a: AgentRow, b: AgentRow, sort: SortField): number {
  switch (sort) {
    case "host":
      return hostName(a.agent).localeCompare(hostName(b.agent)) || a.agent.agent_id.localeCompare(b.agent.agent_id);
    case "last_seen_at":
      return Date.parse(a.agent.last_seen_at) - Date.parse(b.agent.last_seen_at);
    case "status":
      return compareStatusImportance(a, b);
  }
}

function compareStatusImportance(a: AgentRow, b: AgentRow): number {
  const agentStatus = statusRank[a.agent.status] - statusRank[b.agent.status];
  if (agentStatus !== 0) return agentStatus;
  for (const status of checkSortStatuses) {
    const checkStatus = a.checks[status] - b.checks[status];
    if (checkStatus !== 0) return checkStatus;
  }
  return 0;
}

function hostName(agent: Agent): string {
  return agent.hostname || agent.agent_id;
}

function AgentStatus({
  status,
  checks,
}: {
  status: "alive" | "stale" | "dead";
  checks?: CheckCounts;
}) {
  const counts = checks ?? emptyCheckCounts();
  const total = checkStatuses.reduce((sum, s) => sum + counts[s], 0);
  return (
    <div className="flex flex-wrap items-center gap-x-3 gap-y-1 whitespace-nowrap">
      <span className={statusBadgeClass(status)}>{status}</span>
      <span className="text-neutral-600">checks {total}</span>
      <span className="inline-flex items-center gap-1.5 rounded border border-neutral-800 bg-neutral-900 px-2 py-0.5 text-xs">
        {checkStatuses.map((s, i) => (
          <span key={s} className="inline-flex items-center gap-1.5">
            {i > 0 ? <span className="text-neutral-700">/</span> : null}
            <span className={statusBadgeClass(s)}>
              {shortStatus(s)} {counts[s]}
            </span>
          </span>
        ))}
      </span>
    </div>
  );
}

function emptyCheckCounts(): CheckCounts {
  return { ok: 0, warning: 0, critical: 0, unknown: 0 };
}

function rowsFromInventory(data: InventoryData): AgentRow[] {
  const checksByAgent = new Map<string, CheckCounts>();
  for (const c of data.checks) {
    const counts = checksByAgent.get(c.agent_id) ?? emptyCheckCounts();
    counts[c.status] += 1;
    checksByAgent.set(c.agent_id, counts);
  }
  return data.agents.map((agent) => ({
    agent,
    checks: checksByAgent.get(agent.agent_id) ?? emptyCheckCounts(),
  }));
}

function shortStatus(status: CheckStatus): string {
  switch (status) {
    case "ok":
      return "ok";
    case "warning":
      return "warn";
    case "critical":
      return "crit";
    case "unknown":
      return "unknown";
  }
}
