"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { get, post } from "@/lib/api";
import { Badge, Notice, Spinner } from "@/components/ui";
import { DataGrid, type ColDef } from "@/components/DataGrid";
import { Modal } from "@/components/Modal";
import { MoreIcon, PlayIcon, PauseIcon } from "@/components/icons";
import { useRoles } from "@/components/RolesProvider";
import { canControlRunners } from "@/lib/roles";

type Runner = {
  name: string;
  label: string;
  description: string;
  command: string;
  running: boolean;
  pid: number | null;
  instances: number;
  concurrency: number;
  since: string | null;
  logBytes: number;
};
type Row = Runner & { _busy: boolean };
type LogRow = { time: string | null; level: string | null; channel: string | null; message: string };

function StatusCell(p: { data?: Row }) {
  const r = p.data;
  if (!r) return null;
  if (!r.running) {
    return (
      <span className="flex items-center gap-2">
        <Badge kind="neutral">stopped</Badge>
        {r.concurrency > 1 && <span className="text-xs text-slate-400">{r.concurrency}× on start</span>}
      </span>
    );
  }
  const since = r.since ? new Date(r.since).toLocaleTimeString() : null;
  return (
    <span className="flex items-center gap-2">
      <Badge kind="ok">running</Badge>
      <span className="text-xs text-slate-500">
        {r.instances > 1 ? `${r.instances} instances` : `pid ${r.pid}`}{since ? ` · since ${since}` : ""}
      </span>
    </span>
  );
}

function ActionsCell(p: { data?: Row; context?: { onToggle: (n: string, running: boolean) => void; onLogs: (n: string) => void } }) {
  const r = p.data;
  if (!r) return null;
  return (
    <span className="flex items-center gap-1">
      <button
        type="button"
        disabled={r._busy}
        onClick={() => p.context?.onToggle(r.name, r.running)}
        aria-label={r.running ? `Stop ${r.label}` : `Start ${r.label}`}
        title={r.running ? "Stop" : "Start"}
        className={`rounded-sm p-1 disabled:opacity-40 ${r.running ? "text-amber-600 hover:bg-amber-50" : "text-emerald-600 hover:bg-emerald-50"}`}
      >
        {r.running ? <PauseIcon className="h-5 w-5" /> : <PlayIcon className="h-5 w-5" />}
      </button>
      <button
        type="button"
        onClick={() => p.context?.onLogs(r.name)}
        aria-label={`Logs for ${r.label}`}
        title="View CLI log"
        className="rounded-sm p-1 text-slate-500 hover:bg-slate-100 hover:text-slate-800"
      >
        <MoreIcon className="h-5 w-5" />
      </button>
    </span>
  );
}

export default function RunnersPanel() {
  const roles = useRoles();
  const [runners, setRunners] = useState<Runner[] | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [busy, setBusy] = useState<Record<string, true>>({});
  const [logName, setLogName] = useState<string | null>(null);

  const allowed = canControlRunners(roles);

  const load = useCallback(async () => {
    try {
      const d = await get<{ runners: Runner[] }>("runners");
      setRunners(d.runners);
      setError(null);
    } catch (e) {
      setError((e as Error).message);
    }
  }, []);

  useEffect(() => {
    if (!allowed) return;
    load();
    const t = setInterval(load, 4000); // keep status fresh (a runner can exit on its own)
    return () => clearInterval(t);
  }, [allowed, load]);

  const toggle = useCallback(
    async (name: string, running: boolean) => {
      setBusy((b) => ({ ...b, [name]: true }));
      try {
        await post(`runners/${name}/${running ? "stop" : "start"}`);
        await load();
      } catch (e) {
        setError((e as Error).message);
      } finally {
        setBusy((b) => {
          const n = { ...b };
          delete n[name];
          return n;
        });
      }
    },
    [load],
  );

  const gridContext = useMemo(() => ({ onToggle: toggle, onLogs: (n: string) => setLogName(n) }), [toggle]);

  const rowData = useMemo<Row[]>(() => (runners ?? []).map((r) => ({ ...r, _busy: !!busy[r.name] })), [runners, busy]);

  const columnDefs = useMemo<ColDef[]>(
    () => [
      { field: "label", headerName: "Runner", width: 140 },
      { field: "description", headerName: "Description", flex: 2, minWidth: 320, tooltipField: "description", wrapText: true, autoHeight: true, cellClass: "leading-snug text-slate-600" },
      { headerName: "Status", colId: "status", minWidth: 220, sortable: false, filter: false, cellRenderer: StatusCell, cellClass: "flex items-center" },
      { headerName: "", colId: "actions", width: 96, pinned: "right", sortable: false, filter: false, resizable: false, cellRenderer: ActionsCell, cellClass: "flex items-center justify-center" },
    ],
    [],
  );

  if (!allowed) return null; // Service Manager can schedule services but not control runners.

  return (
    <section className="card mt-6" aria-labelledby="runners-h">
      <div className="mb-1 flex flex-wrap items-center justify-between gap-2">
        <h2 id="runners-h" className="font-semibold text-slate-900">Process runners</h2>
        <span className="text-xs text-slate-500">backend-managed · same host</span>
      </div>
      <p className="mb-3 text-sm text-slate-600">
        Start or stop the background CLI runners directly. The <strong>Worker</strong> consumes the queue and runs the AI
        pipeline (incurs Vertex cost while running); the <strong>Poller</strong> auto-discovers New products. Use the
        play/pause action to toggle a runner, and the <strong>⋯</strong> menu to view its live CLI log.
      </p>
      {error && <Notice kind="error">{error}</Notice>}
      {!runners ? (
        <Spinner label="Loading runners…" />
      ) : (
        <DataGrid rowData={rowData} columnDefs={columnDefs} context={gridContext} getRowId={(p: { data: Row }) => p.data.name} autoHeight />
      )}

      <RunnerLogModal name={logName} onClose={() => setLogName(null)} />
    </section>
  );
}

function levelBadge(level: string | null) {
  if (!level) return null;
  const kind = level === "ERROR" || level === "CRITICAL" ? "danger" : level === "WARNING" ? "warn" : "neutral";
  return <Badge kind={kind}>{level}</Badge>;
}

function RunnerLogModal({ name, onClose }: { name: string | null; onClose: () => void }) {
  const [lines, setLines] = useState<LogRow[] | null>(null);
  const [running, setRunning] = useState(false);
  const [err, setErr] = useState<string | null>(null);
  const [follow, setFollow] = useState(true);
  const bodyRef = useRef<HTMLDivElement | null>(null);

  const fetchLogs = useCallback(async () => {
    if (!name) return;
    try {
      const d = await get<{ lines: LogRow[]; status: { running: boolean } }>(`runners/${name}/logs?limit=400`);
      setLines(d.lines);
      setRunning(!!d.status?.running);
      setErr(null);
    } catch (e) {
      setErr((e as Error).message);
    }
  }, [name]);

  useEffect(() => {
    if (!name) {
      setLines(null);
      return;
    }
    fetchLogs();
    const t = setInterval(fetchLogs, 3000); // live tail
    return () => clearInterval(t);
  }, [name, fetchLogs]);

  useEffect(() => {
    if (follow && bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
  }, [lines, follow]);

  return (
    <Modal open={!!name} onClose={onClose} title={`CLI log — ${name ?? ""}`} width="max-w-4xl">
      <div className="mb-3 flex flex-wrap items-center gap-3 text-sm">
        {running ? <Badge kind="ok">running</Badge> : <Badge kind="neutral">stopped</Badge>}
        <label className="flex items-center gap-1.5 text-slate-600">
          <input type="checkbox" checked={follow} onChange={(e) => setFollow(e.target.checked)} /> Follow tail
        </label>
        <button type="button" className="btn-secondary" onClick={fetchLogs}>Refresh</button>
        <span className="text-xs text-slate-400">auto-refreshes every 3s</span>
      </div>
      {err && <Notice kind="error">{err}</Notice>}
      <div ref={bodyRef} className="max-h-[60vh] overflow-auto rounded-sm border border-slate-200">
        <table className="w-full border-collapse text-xs">
          <thead className="sticky top-0 bg-slate-50 text-left text-slate-500">
            <tr>
              <th className="w-20 px-2 py-1 font-medium">Time</th>
              <th className="w-24 px-2 py-1 font-medium">Level</th>
              <th className="w-24 px-2 py-1 font-medium">Channel</th>
              <th className="px-2 py-1 font-medium">Message</th>
            </tr>
          </thead>
          <tbody className="font-mono">
            {(lines ?? []).map((l, i) => (
              <tr key={i} className="border-t border-slate-100 align-top">
                <td className="whitespace-nowrap px-2 py-1 text-slate-400">{l.time ?? ""}</td>
                <td className="px-2 py-1">{levelBadge(l.level)}</td>
                <td className="px-2 py-1 text-slate-500">{l.channel ?? ""}</td>
                <td className="px-2 py-1 text-slate-700">{l.message}</td>
              </tr>
            ))}
            {lines && lines.length === 0 && (
              <tr><td colSpan={4} className="px-2 py-6 text-center text-slate-400">No log output yet.</td></tr>
            )}
          </tbody>
        </table>
        {!lines && <div className="p-4"><Spinner label="Loading log…" /></div>}
      </div>
    </Modal>
  );
}
