"use client";

import { useEffect, useRef, useState, Fragment } from "react";
import { useRouter } from "next/navigation";
import { useSystems } from "@/lib/useSystems";
import { c2api } from "@/lib/c2api";
import { useAuth } from "@/components/AuthProvider";
import type { SystemRecord, VocabularyPendingTerm } from "@/lib/types";

// ── P25 structured config types ───────────────────────────────────────────────

interface TalkgroupEntry {
  id: string;
  name: string;
  tag: string;
}

interface P25Config {
  nac: string;
  system_id: string;
  wacn: string;
  control_channels: string;
  voice_channels: string;
  talkgroups: TalkgroupEntry[];
}

const DEFAULT_P25: P25Config = {
  nac: "",
  system_id: "",
  wacn: "",
  control_channels: "",
  voice_channels: "",
  talkgroups: [],
};

const TG_TAGS = ["fire", "police", "ems", "transit", "public works", "other"];

function recordToP25Config(c: Record<string, unknown>): P25Config {
  return {
    nac: String(c.nac ?? ""),
    system_id: String(c.system_id ?? ""),
    wacn: String(c.wacn ?? ""),
    control_channels: Array.isArray(c.control_channels)
      ? (c.control_channels as number[]).join(", ")
      : "",
    voice_channels: Array.isArray(c.voice_channels)
      ? (c.voice_channels as number[]).join(", ")
      : "",
    talkgroups: Array.isArray(c.talkgroups)
      ? (c.talkgroups as Array<{ id: number; name: string; tag: string }>).map((tg) => ({
          id: String(tg.id),
          name: tg.name,
          tag: tg.tag ?? "other",
        }))
      : [],
  };
}

function p25ConfigToRecord(p: P25Config): Record<string, unknown> {
  const parseFreqs = (s: string) =>
    s
      .split(",")
      .map((f) => parseFloat(f.trim()))
      .filter((f) => !isNaN(f));
  return {
    nac: p.nac,
    system_id: p.system_id ? parseInt(p.system_id, 10) : undefined,
    wacn: p.wacn,
    control_channels: parseFreqs(p.control_channels),
    voice_channels: parseFreqs(p.voice_channels),
    talkgroups: p.talkgroups
      .filter((tg) => tg.id && tg.name)
      .map((tg) => ({ id: parseInt(tg.id, 10), name: tg.name, tag: tg.tag })),
  };
}

// ── RadioReference parser types ───────────────────────────────────────────────

interface RRTalkgroup {
  dec: number;
  alphaTag: string;
  description: string;
  tag: string;
}

interface RRCategory {
  name: string;
  talkgroups: RRTalkgroup[];
}

interface RRSystem {
  name: string;
  location: string;
  sysIds: string;
  systemType: string;
  categories: RRCategory[];
}

function mapRRTag(rrTag: string): string {
  const t = rrTag.toLowerCase();
  if (t.includes("fire")) return "fire";
  if (t.includes("law") || t.includes("police")) return "police";
  if (t.includes("ems") || t.includes("emergency medical")) return "ems";
  if (t.includes("transport") || t.includes("transit")) return "transit";
  if (t.includes("public works")) return "public works";
  return "other";
}

function parseRadioReference(html: string): RRSystem | null {
  const doc = new DOMParser().parseFromString(html, "text/html");

  // Validate: RadioReference system pages have rrlblue header cells
  if (!doc.querySelector(".rrlblue")) return null;

  // System info table (first table with rrlblue headers)
  const infoMap: Record<string, string> = {};
  const infoTable = doc.querySelector("table.table-sm.table-bordered");
  if (infoTable) {
    infoTable.querySelectorAll("tr").forEach((row) => {
      const th = row.querySelector("th.rrlblue");
      const td = row.querySelector("td");
      if (th && td) infoMap[th.textContent?.trim() ?? ""] = td.textContent?.trim() ?? "";
    });
  }

  const name       = infoMap["System Name"] ?? doc.title ?? "Unknown System";
  const location   = infoMap["Location"] ?? "";
  const sysIds     = infoMap["System IDs"] ?? "";
  const systemType = infoMap["System Type"] ?? "";

  // Talkgroup tables — find all with class rrdbTable or datatable-lite
  // For each, find the nearest preceding h5 to use as category name
  const tgTables = Array.from(
    doc.querySelectorAll("table.rrdbTable, table.datatable-lite")
  ) as HTMLTableElement[];

  const allH5s = Array.from(doc.querySelectorAll("h5")) as HTMLElement[];

  function categoryForTable(table: HTMLTableElement): string {
    // Find the last h5 that appears before this table in document order
    let best: HTMLElement | null = null;
    for (const h5 of allH5s) {
      const pos = h5.compareDocumentPosition(table);
      if (pos & Node.DOCUMENT_POSITION_FOLLOWING) best = h5;
    }
    if (!best) return "Uncategorized";
    const clone = best.cloneNode(true) as HTMLElement;
    clone.querySelectorAll("div, button, span.badge").forEach((el) => el.remove());
    return clone.textContent?.trim() || "Uncategorized";
  }

  const categories: RRCategory[] = [];

  for (const table of tgTables) {
    // Confirm it has the expected talkgroup columns (DEC, HEX, Mode, Alpha Tag, …)
    const headers = Array.from(table.querySelectorAll("thead th")).map((th) =>
      th.textContent?.trim().toLowerCase()
    );
    if (!headers.includes("dec") && !headers.includes("hex")) continue;

    const catName = categoryForTable(table);
    const talkgroups: RRTalkgroup[] = [];

    table.querySelectorAll("tbody tr").forEach((row) => {
      const cells = Array.from(row.querySelectorAll("td"));
      if (cells.length < 6) return;

      // DEC cell may wrap in a Broadcastify link
      const decText =
        cells[0].querySelector("a")?.textContent?.trim() ??
        cells[0].textContent?.trim() ??
        "";
      const dec = parseInt(decText.replace(/\D/g, ""), 10);
      if (isNaN(dec)) return;

      const alphaTag   = cells[3].textContent?.trim() ?? "";
      const description = cells[4].textContent?.trim() ?? "";
      const tag        = cells[5].textContent?.trim() ?? "";

      talkgroups.push({ dec, alphaTag, description, tag });
    });

    if (talkgroups.length > 0) {
      // Merge into an existing category with same name if present
      const existing = categories.find((c) => c.name === catName);
      if (existing) {
        existing.talkgroups.push(...talkgroups);
      } else {
        categories.push({ name: catName, talkgroups });
      }
    }
  }

  if (categories.length === 0) return null;

  return { name, location, sysIds, systemType, categories };
}

// ── RadioReference import modal ───────────────────────────────────────────────

function RRImportModal({
  system,
  onImport,
  onCancel,
}: {
  system: RRSystem;
  onImport: (tgs: TalkgroupEntry[]) => void;
  onCancel: () => void;
}) {
  const [selected, setSelected] = useState<Set<string>>(
    () => new Set(system.categories.map((c) => c.name))
  );

  function toggle(name: string) {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(name)) next.delete(name); else next.add(name);
      return next;
    });
  }

  function handleImport() {
    const tgs: TalkgroupEntry[] = [];
    for (const cat of system.categories) {
      if (!selected.has(cat.name)) continue;
      for (const tg of cat.talkgroups) {
        tgs.push({
          id: String(tg.dec),
          name: `${cat.name.split(" - ")[0]} - ${tg.description || tg.alphaTag}`,
          tag: mapRRTag(tg.tag),
        });
      }
    }
    onImport(tgs);
  }

  const total = system.categories.reduce((s, c) => s + c.talkgroups.length, 0);
  const selectedCount = system.categories
    .filter((c) => selected.has(c.name))
    .reduce((s, c) => s + c.talkgroups.length, 0);

  return (
    <div className="fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4">
      <div className="bg-gray-900 border border-gray-700 rounded-xl w-full max-w-lg max-h-[90vh] flex flex-col font-mono">
        {/* Header */}
        <div className="px-5 pt-5 pb-4 border-b border-gray-800">
          <h2 className="text-white font-semibold">{system.name}</h2>
          <p className="text-xs text-gray-500 mt-0.5">
            {system.systemType}{system.location ? ` · ${system.location}` : ""}
          </p>
          {system.sysIds && (
            <p className="text-xs text-gray-600 mt-0.5">System IDs: {system.sysIds}</p>
          )}
          <p className="text-xs text-gray-500 mt-1">
            {system.categories.length} categor{system.categories.length !== 1 ? "ies" : "y"} · {total} talkgroups
          </p>
        </div>

        {/* Category list */}
        <div className="flex-1 overflow-y-auto px-5 py-4 space-y-1.5">
          <div className="flex items-center justify-between mb-2">
            <p className="text-xs text-gray-400 uppercase tracking-wider">Talkgroup Categories</p>
            <div className="flex gap-3 text-xs text-indigo-400">
              <button
                type="button"
                onClick={() => setSelected(new Set(system.categories.map((c) => c.name)))}
                className="hover:text-indigo-300 transition-colors"
              >
                All
              </button>
              <button
                type="button"
                onClick={() => setSelected(new Set())}
                className="hover:text-indigo-300 transition-colors"
              >
                None
              </button>
            </div>
          </div>
          {system.categories.map((cat) => (
            <label key={cat.name} className="flex items-center gap-3 cursor-pointer group py-0.5">
              <input
                type="checkbox"
                checked={selected.has(cat.name)}
                onChange={() => toggle(cat.name)}
                className="rounded border-gray-600 bg-gray-800 accent-indigo-500"
              />
              <span className="text-sm text-gray-200 flex-1 group-hover:text-white transition-colors truncate">
                {cat.name}
              </span>
              <span className="text-xs text-gray-600 shrink-0">{cat.talkgroups.length} TGs</span>
            </label>
          ))}
        </div>

        {/* Footer */}
        <div className="px-5 pb-5 pt-4 border-t border-gray-800 flex gap-3">
          <button
            type="button"
            onClick={handleImport}
            disabled={selectedCount === 0}
            className="flex-1 bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white rounded-lg py-2 text-sm font-semibold transition-colors"
          >
            Import {selectedCount} talkgroup{selectedCount !== 1 ? "s" : ""}
          </button>
          <button
            type="button"
            onClick={onCancel}
            className="flex-1 bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg py-2 text-sm transition-colors"
          >
            Cancel
          </button>
        </div>
      </div>
    </div>
  );
}

// ── Talkgroup table editor ────────────────────────────────────────────────────

function TalkgroupEditor({
  talkgroups,
  onChange,
}: {
  talkgroups: TalkgroupEntry[];
  onChange: (tgs: TalkgroupEntry[]) => void;
}) {
  const [showPaste, setShowPaste] = useState(false);
  const [pasteText, setPasteText] = useState("");
  const [rrSystem, setRrSystem]   = useState<RRSystem | null>(null);
  const [rrError, setRrError]     = useState<string | null>(null);
  const rrInputRef                = useRef<HTMLInputElement>(null);

  function addRow() {
    onChange([...talkgroups, { id: "", name: "", tag: "other" }]);
  }

  function removeRow(i: number) {
    onChange(talkgroups.filter((_, idx) => idx !== i));
  }

  function updateRow(i: number, field: keyof TalkgroupEntry, value: string) {
    const updated = [...talkgroups];
    updated[i] = { ...updated[i], [field]: value };
    onChange(updated);
  }

  function handlePasteImport() {
    const lines = pasteText.trim().split("\n");
    const parsed: TalkgroupEntry[] = lines
      .map((line) => {
        const parts = line.includes("\t") ? line.split("\t") : line.split(",");
        const id = parts[0]?.trim() ?? "";
        const name = parts[1]?.trim() ?? "";
        const rawTag = parts[2]?.trim()?.toLowerCase() ?? "other";
        const tag = TG_TAGS.includes(rawTag) ? rawTag : "other";
        return { id, name, tag };
      })
      .filter((e) => e.id && e.name);
    onChange([...talkgroups, ...parsed]);
    setPasteText("");
    setShowPaste(false);
  }

  async function handleRRFile(file: File) {
    setRrError(null);
    const html = await file.text();
    const parsed = parseRadioReference(html);
    if (!parsed) {
      setRrError(
        "This doesn't look like a RadioReference trunked system page. " +
        "Download the HTML from a system page on radioreference.com and try again."
      );
      return;
    }
    setRrSystem(parsed);
  }

  function handleRRImport(newTgs: TalkgroupEntry[]) {
    onChange([...talkgroups, ...newTgs]);
    setRrSystem(null);
    setRrError(null);
  }

  return (
    <div className="space-y-2">
      {rrSystem && (
        <RRImportModal
          system={rrSystem}
          onImport={handleRRImport}
          onCancel={() => setRrSystem(null)}
        />
      )}

      <div className="flex items-center justify-between">
        <label className="text-xs text-gray-400">
          Talkgroups{talkgroups.length > 0 && <span className="text-gray-600 ml-1">({talkgroups.length})</span>}
        </label>
        <div className="flex gap-3">
          {/* RadioReference file import */}
          <button
            type="button"
            onClick={() => rrInputRef.current?.click()}
            className="text-xs text-indigo-400 hover:text-indigo-300 transition-colors"
          >
            RadioReference import
          </button>
          <input
            ref={rrInputRef}
            type="file"
            accept=".html,.htm"
            className="hidden"
            onChange={(e) => {
              const f = e.target.files?.[0];
              if (f) handleRRFile(f);
              e.target.value = "";
            }}
          />

          <button
            type="button"
            onClick={() => setShowPaste(!showPaste)}
            className="text-xs text-indigo-400 hover:text-indigo-300 transition-colors"
          >
            {showPaste ? "Cancel paste" : "Paste import"}
          </button>
          <button
            type="button"
            onClick={addRow}
            className="text-xs text-indigo-400 hover:text-indigo-300 transition-colors"
          >
            + Add row
          </button>
        </div>
      </div>

      {rrError && (
        <p className="text-xs text-red-400 bg-red-950/30 border border-red-900/50 rounded px-3 py-2">
          {rrError}
        </p>
      )}

      {showPaste && (
        <div className="space-y-2 p-3 bg-gray-800 rounded-lg border border-gray-700">
          <p className="text-xs text-gray-500">
            Paste rows from RadioReference — tab- or comma-separated:{" "}
            <span className="text-gray-400">ID, Name, Tag</span>
            <br />Tags: fire · police · ems · transit · public works · other
          </p>
          <textarea
            value={pasteText}
            onChange={(e) => setPasteText(e.target.value)}
            rows={6}
            placeholder={"1234\tFire Dispatch\tfire\n5678\tPolice Zone 1\tpolice\n9012\tEMS\tems"}
            className="w-full bg-gray-900 border border-gray-700 rounded px-3 py-2 text-white text-xs font-mono focus:outline-none focus:border-indigo-500"
          />
          <button
            type="button"
            onClick={handlePasteImport}
            disabled={!pasteText.trim()}
            className="bg-indigo-700 hover:bg-indigo-600 disabled:opacity-50 text-white px-3 py-1.5 rounded text-xs font-semibold transition-colors"
          >
            Import rows
          </button>
        </div>
      )}

      {talkgroups.length > 0 ? (
        <div className="border border-gray-800 rounded-lg overflow-hidden">
          <table className="w-full text-xs font-mono">
            <thead>
              <tr className="bg-gray-800 text-gray-400">
                <th className="px-3 py-1.5 text-left w-20">Dec ID</th>
                <th className="px-3 py-1.5 text-left">Name</th>
                <th className="px-3 py-1.5 text-left w-28">Tag</th>
                <th className="px-3 py-1.5 w-8"></th>
              </tr>
            </thead>
            <tbody>
              {talkgroups.map((tg, i) => (
                <tr key={i} className="border-t border-gray-800 hover:bg-gray-800/30">
                  <td className="px-2 py-1">
                    <input
                      value={tg.id}
                      onChange={(e) => updateRow(i, "id", e.target.value)}
                      className="w-full bg-transparent text-white focus:outline-none"
                      placeholder="1234"
                    />
                  </td>
                  <td className="px-2 py-1">
                    <input
                      value={tg.name}
                      onChange={(e) => updateRow(i, "name", e.target.value)}
                      className="w-full bg-transparent text-white focus:outline-none"
                      placeholder="Fire Dispatch"
                    />
                  </td>
                  <td className="px-2 py-1">
                    <select
                      value={tg.tag}
                      onChange={(e) => updateRow(i, "tag", e.target.value)}
                      className="w-full bg-gray-900 text-gray-300 focus:outline-none rounded px-1"
                    >
                      {TG_TAGS.map((t) => (
                        <option key={t} value={t}>{t}</option>
                      ))}
                    </select>
                  </td>
                  <td className="px-2 py-1 text-center">
                    <button
                      type="button"
                      onClick={() => removeRow(i)}
                      className="text-gray-600 hover:text-red-400 transition-colors font-bold"
                    >
                      ×
                    </button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      ) : (
        <p className="text-xs text-gray-600 italic py-1">
          No talkgroups — add rows, paste from RadioReference, or use the RadioReference import button.
        </p>
      )}
    </div>
  );
}

// ── P25 structured form ───────────────────────────────────────────────────────

function P25Form({ value, onChange }: { value: P25Config; onChange: (v: P25Config) => void }) {
  return (
    <div className="space-y-4">
      <div className="grid grid-cols-3 gap-3">
        <div>
          <label className="text-xs text-gray-400 block mb-1">NAC (hex)</label>
          <input
            value={value.nac}
            onChange={(e) => onChange({ ...value, nac: e.target.value })}
            className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm font-mono focus:outline-none focus:border-indigo-500"
            placeholder="0x293"
          />
        </div>
        <div>
          <label className="text-xs text-gray-400 block mb-1">System ID</label>
          <input
            value={value.system_id}
            onChange={(e) => onChange({ ...value, system_id: e.target.value })}
            className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm font-mono focus:outline-none focus:border-indigo-500"
            placeholder="50513"
          />
        </div>
        <div>
          <label className="text-xs text-gray-400 block mb-1">WACN (hex)</label>
          <input
            value={value.wacn}
            onChange={(e) => onChange({ ...value, wacn: e.target.value })}
            className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm font-mono focus:outline-none focus:border-indigo-500"
            placeholder="0xBEE00"
          />
        </div>
      </div>
      <div>
        <label className="text-xs text-gray-400 block mb-1">
          Control Channels <span className="text-gray-600">(MHz, comma-separated)</span>
        </label>
        <input
          value={value.control_channels}
          onChange={(e) => onChange({ ...value, control_channels: e.target.value })}
          className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm font-mono focus:outline-none focus:border-indigo-500"
          placeholder="851.0125, 851.5125, 852.0125"
        />
      </div>
      <div>
        <label className="text-xs text-gray-400 block mb-1">
          Voice Channels <span className="text-gray-600">(MHz, comma-separated — leave blank for auto-discovery)</span>
        </label>
        <input
          value={value.voice_channels}
          onChange={(e) => onChange({ ...value, voice_channels: e.target.value })}
          className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm font-mono focus:outline-none focus:border-indigo-500"
          placeholder="Optional"
        />
      </div>
      <TalkgroupEditor
        talkgroups={value.talkgroups}
        onChange={(tgs) => onChange({ ...value, talkgroups: tgs })}
      />
    </div>
  );
}

// ── Main system form ──────────────────────────────────────────────────────────

function SystemForm({
  initial,
  createOnly,
  onSave,
  onCancel,
}: {
  initial?: SystemRecord;
  createOnly?: boolean;
  onSave: () => void;
  onCancel: () => void;
}) {
  const [name, setName] = useState(initial?.name ?? "");
  const [type, setType] = useState(initial?.type ?? "P25");
  const [p25, setP25] = useState<P25Config>(
    initial?.type === "P25" && initial.config
      ? recordToP25Config(initial.config)
      : DEFAULT_P25
  );
  const [rawJson, setRawJson] = useState(
    initial?.type !== "P25" && initial?.config
      ? JSON.stringify(initial.config, null, 2)
      : "{}"
  );
  const [showRaw, setShowRaw] = useState(initial?.type !== "P25");
  const [saving, setSaving]   = useState(false);
  const [error, setError]     = useState<string | null>(null);

  function handleTypeChange(t: string) {
    setType(t);
    setShowRaw(t !== "P25");
  }

  function toggleRaw() {
    if (!showRaw) {
      setRawJson(JSON.stringify(p25ConfigToRecord(p25), null, 2));
    } else {
      try {
        setP25(recordToP25Config(JSON.parse(rawJson)));
      } catch {
        // keep current p25 state if parse fails
      }
    }
    setShowRaw(!showRaw);
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setSaving(true);
    setError(null);
    try {
      let config: Record<string, unknown>;
      if (type === "P25" && !showRaw) {
        config = p25ConfigToRecord(p25);
      } else {
        config = JSON.parse(rawJson);
      }
      if (initial && !createOnly) {
        await c2api.updateSystem(initial.system_id, { name, type, config });
      } else {
        await c2api.createSystem({ name, type, config });
      }
      onSave();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Invalid config or save failed.");
    } finally {
      setSaving(false);
    }
  }

  const title = initial && !createOnly ? "Edit System" : createOnly ? "Duplicate System" : "New System";

  return (
    <form onSubmit={handleSubmit} className="bg-gray-900 border border-gray-700 rounded-xl p-5 space-y-4 font-mono">
      <h3 className="text-white font-semibold">{title}</h3>

      <div className="grid grid-cols-2 gap-3">
        <div>
          <label className="text-xs text-gray-400 block mb-1">Name</label>
          <input
            value={name}
            onChange={(e) => setName(e.target.value)}
            required
            className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
            placeholder="Westchester County P25"
          />
        </div>
        <div>
          <label className="text-xs text-gray-400 block mb-1">Type</label>
          <select
            value={type}
            onChange={(e) => handleTypeChange(e.target.value)}
            className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
          >
            <option>P25</option>
            <option>DMR</option>
            <option>NBFM</option>
          </select>
        </div>
      </div>

      {type === "P25" && !showRaw ? (
        <P25Form value={p25} onChange={setP25} />
      ) : (
        <div>
          <label className="text-xs text-gray-400 block mb-1">Config (JSON)</label>
          <textarea
            value={rawJson}
            onChange={(e) => setRawJson(e.target.value)}
            rows={10}
            className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-xs font-mono focus:outline-none focus:border-indigo-500"
          />
        </div>
      )}

      {type === "P25" && (
        <button
          type="button"
          onClick={toggleRaw}
          className="text-xs text-gray-600 hover:text-gray-400 transition-colors"
        >
          {showRaw ? "← Use structured form" : "Edit raw JSON →"}
        </button>
      )}

      {error && <p className="text-red-400 text-xs">{error}</p>}

      <div className="flex gap-3">
        <button
          type="submit"
          disabled={saving}
          className="flex-1 bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white rounded-lg py-2 text-sm font-semibold transition-colors"
        >
          {saving ? "Saving…" : "Save"}
        </button>
        <button
          type="button"
          onClick={onCancel}
          className="flex-1 bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg py-2 text-sm transition-colors"
        >
          Cancel
        </button>
      </div>
    </form>
  );
}

// ── Preferred bot token panel ─────────────────────────────────────────────────

interface TokenOption {
  token_id: string;
  name: string;
  in_use: boolean;
}

function PreferredTokenPanel({ systemId, initialTokenId }: { systemId: string; initialTokenId?: string | null }) {
  const [preferredId, setPreferredId] = useState<string | null>(initialTokenId ?? null);
  const [tokens, setTokens]           = useState<TokenOption[] | null>(null);
  const [saving, setSaving]           = useState(false);
  const [open, setOpen]               = useState(false);

  async function load() {
    if (tokens !== null) return;
    try {
      const data = await c2api.getTokens();
      setTokens(data as TokenOption[]);
    } catch {
      setTokens([]);
    }
  }

  function toggle() {
    if (!open) load();
    setOpen((v) => !v);
  }

  async function handleSet(tokenId: string) {
    setSaving(true);
    try {
      await c2api.setPreferredToken(tokenId, systemId);
      setPreferredId(tokenId);
    } finally {
      setSaving(false);
    }
  }

  async function handleClear() {
    if (!preferredId) return;
    setSaving(true);
    try {
      await c2api.setPreferredToken(preferredId, "_none");
      setPreferredId(null);
    } finally {
      setSaving(false);
    }
  }

  const currentToken = tokens?.find((t) => t.token_id === preferredId);

  return (
    <div className="mt-3 border-t border-gray-800 pt-3">
      <button
        onClick={toggle}
        className="text-xs text-gray-500 hover:text-gray-300 font-mono transition-colors flex items-center gap-1"
      >
        <span>{open ? "▲" : "▼"}</span>
        <span>
          Preferred Bot Token
          {preferredId && <span className="ml-1.5 text-indigo-400">● set</span>}
        </span>
      </button>

      {open && (
        <div className="mt-3 space-y-2 font-mono text-xs">
          {tokens === null ? (
            <p className="text-gray-600 italic">Loading tokens…</p>
          ) : tokens.length === 0 ? (
            <p className="text-gray-600 italic">No tokens in pool.</p>
          ) : (
            <>
              <p className="text-gray-600">
                When a node on this system joins a voice channel, this token is tried first.
              </p>
              <div className="space-y-1.5">
                {tokens.map((t) => (
                  <label key={t.token_id} className="flex items-center gap-2.5 cursor-pointer">
                    <input
                      type="radio"
                      name={`preferred-token-${systemId}`}
                      checked={preferredId === t.token_id}
                      onChange={() => handleSet(t.token_id)}
                      disabled={saving}
                      className="accent-indigo-500"
                    />
                    <span className={`flex-1 ${t.in_use && preferredId !== t.token_id ? "text-gray-600" : "text-gray-300"}`}>
                      {t.name}
                      {t.in_use && <span className="ml-1.5 text-green-600">in use</span>}
                    </span>
                  </label>
                ))}
              </div>
              {preferredId && (
                <button
                  onClick={handleClear}
                  disabled={saving}
                  className="text-gray-600 hover:text-gray-400 transition-colors disabled:opacity-50"
                >
                  Clear preference (use any free token)
                </button>
              )}
              {!preferredId && (
                <p className="text-gray-700">No preference — any free token will be used.</p>
              )}
              {currentToken && (
                <p className="text-indigo-500">Preferred: {currentToken.name}</p>
              )}
            </>
          )}
        </div>
      )}
    </div>
  );
}

// ── Per-system AI flags panel ─────────────────────────────────────────────────

interface SystemAiFlags {
  stt_enabled?: boolean;
  correlation_enabled?: boolean;
}

function AiFlagsPanel({ systemId, initial }: { systemId: string; initial: SystemAiFlags }) {
  const [flags, setFlags]   = useState<SystemAiFlags>(initial);
  const [saving, setSaving] = useState<string | null>(null);
  const [open, setOpen]     = useState(false);

  async function handleToggle(key: keyof SystemAiFlags, value: boolean) {
    setSaving(key);
    try {
      const result = await c2api.setSystemAiFlags(systemId, { [key]: value });
      setFlags(result.ai_flags as SystemAiFlags);
    } finally {
      setSaving(null);
    }
  }

  async function handleClear(key: keyof SystemAiFlags) {
    setSaving(key);
    try {
      const result = await c2api.setSystemAiFlags(systemId, { [key]: null });
      setFlags(result.ai_flags as SystemAiFlags);
    } finally {
      setSaving(null);
    }
  }

  const rows: { key: keyof SystemAiFlags; label: string }[] = [
    { key: "stt_enabled",         label: "Speech-to-Text" },
    { key: "correlation_enabled", label: "Incident Correlation" },
  ];

  return (
    <div className="mt-3 border-t border-gray-800 pt-3">
      <button
        onClick={() => setOpen((v) => !v)}
        className="text-xs text-gray-500 hover:text-gray-300 font-mono transition-colors flex items-center gap-1"
      >
        <span>{open ? "▲" : "▼"}</span>
        <span>AI Flags</span>
        {(flags.stt_enabled === false || flags.correlation_enabled === false) && (
          <span className="ml-1.5 text-yellow-600 font-bold">!</span>
        )}
      </button>

      {open && (
        <div className="mt-3 space-y-2 font-mono text-xs">
          {rows.map(({ key, label }) => {
            const override = flags[key];
            const isSet    = override !== undefined;
            const isOn     = override !== false;
            return (
              <div key={key} className="flex items-center gap-3">
                <button
                  onClick={() => handleToggle(key, !isOn)}
                  disabled={saving === key}
                  className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors disabled:opacity-50 ${
                    isOn ? "bg-indigo-600" : "bg-gray-700"
                  }`}
                >
                  <span className={`inline-block h-3.5 w-3.5 rounded-full bg-white shadow transition-transform ${isOn ? "translate-x-4" : "translate-x-0.5"}`} />
                </button>
                <span className="text-gray-300 flex-1">{label}</span>
                {isSet ? (
                  <button
                    onClick={() => handleClear(key)}
                    disabled={saving === key}
                    className="text-gray-600 hover:text-gray-400 transition-colors"
                    title="Clear override (inherit global)"
                  >
                    reset
                  </button>
                ) : (
                  <span className="text-gray-700">inherits global</span>
                )}
              </div>
            );
          })}
          <p className="text-gray-700 pt-1">Overrides apply on top of global AI flags. "reset" restores global default.</p>
        </div>
      )}
    </div>
  );
}

// ── Source call audio player ──────────────────────────────────────────────────

function SourceCallPlayer({ callId }: { callId: string }) {
  const [call, setCall] = useState<{ audio_url?: string | null; transcript?: string | null; transcript_corrected?: string | null } | null>(null);
  const [loading, setLoading] = useState(false);
  const [open, setOpen] = useState(false);

  async function toggle() {
    if (!open && !call) {
      setLoading(true);
      try {
        const c = await c2api.getCall(callId);
        setCall(c as unknown as typeof call);
      } finally {
        setLoading(false);
      }
    }
    setOpen((v) => !v);
  }

  const transcript = call?.transcript_corrected || call?.transcript;

  return (
    <div className="text-xs">
      <button
        onClick={toggle}
        disabled={loading}
        className="text-indigo-500 hover:text-indigo-400 transition-colors disabled:opacity-50"
        title={callId}
      >
        {loading ? "loading…" : open ? "▲ source" : "▶ source"}
      </button>
      {open && call && (
        <div className="mt-1.5 space-y-1 pl-2 border-l border-gray-700">
          {call.audio_url ? (
            <audio src={call.audio_url} controls className="w-full" style={{ height: "1.75rem" }} />
          ) : (
            <p className="text-gray-600 italic">No audio</p>
          )}
          {transcript && (
            <p className="text-gray-500 italic line-clamp-2">{transcript}</p>
          )}
        </div>
      )}
    </div>
  );
}

// ── Vocabulary panel ──────────────────────────────────────────────────────────

function VocabularyPanel({ systemId }: { systemId: string }) {
  const [vocab, setVocab]               = useState<string[] | null>(null);
  const [pending, setPending]           = useState<VocabularyPendingTerm[]>([]);
  const [bootstrapped, setBootstrapped] = useState(false);
  const [loading, setLoading]           = useState(false);
  const [bootstrapping, setBootstrapping] = useState(false);
  const [newTerm, setNewTerm]           = useState("");
  const [adding, setAdding]             = useState(false);
  const [open, setOpen]                 = useState(false);

  async function load() {
    if (vocab !== null) return;
    setLoading(true);
    try {
      const data = await c2api.getVocabulary(systemId);
      setVocab(data.vocabulary);
      setPending(data.vocabulary_pending);
      setBootstrapped(data.vocabulary_bootstrapped);
    } finally {
      setLoading(false);
    }
  }

  function toggle() {
    if (!open) load();
    setOpen((v) => !v);
  }

  async function handleBootstrap() {
    setBootstrapping(true);
    try {
      const result = await c2api.bootstrapVocabulary(systemId);
      const data   = await c2api.getVocabulary(systemId);
      setVocab(data.vocabulary);
      setPending(data.vocabulary_pending);
      setBootstrapped(true);
      alert(`Bootstrap added ${result.added} term(s).`);
    } finally {
      setBootstrapping(false);
    }
  }

  async function handleAdd(e: React.FormEvent) {
    e.preventDefault();
    const term = newTerm.trim();
    if (!term) return;
    setAdding(true);
    try {
      await c2api.addVocabularyTerm(systemId, term);
      setVocab((v) => (v ? [...v, term] : [term]));
      setNewTerm("");
    } finally {
      setAdding(false);
    }
  }

  async function handleRemove(term: string) {
    await c2api.removeVocabularyTerm(systemId, term);
    setVocab((v) => (v ?? []).filter((t) => t !== term));
  }

  async function handleApprove(term: string) {
    await c2api.approvePendingTerm(systemId, term);
    setVocab((v) => (v ? [...v, term] : [term]));
    setPending((p) => p.filter((t) => t.term !== term));
  }

  async function handleDismiss(term: string) {
    await c2api.dismissPendingTerm(systemId, term);
    setPending((p) => p.filter((t) => t.term !== term));
  }

  return (
    <div className="mt-3 border-t border-gray-800 pt-3">
      <button
        onClick={toggle}
        className="text-xs text-gray-500 hover:text-gray-300 font-mono transition-colors flex items-center gap-1"
      >
        <span>{open ? "▲" : "▼"}</span>
        <span>
          Vocabulary
          {vocab !== null && (
            <span className="text-gray-600 ml-1">
              ({vocab.length} terms{pending.length > 0 ? `, ${pending.length} pending` : ""})
            </span>
          )}
        </span>
      </button>

      {open && (
        <div className="mt-3 space-y-3 font-mono text-xs">
          {loading && <p className="text-gray-600 italic">Loading…</p>}

          {!loading && vocab !== null && (
            <>
              <div className="flex items-center gap-3">
                <button
                  onClick={handleBootstrap}
                  disabled={bootstrapping}
                  className="bg-indigo-800 hover:bg-indigo-700 disabled:opacity-50 text-indigo-200 px-3 py-1.5 rounded-lg text-xs transition-colors"
                >
                  {bootstrapping ? "Bootstrapping…" : bootstrapped ? "Re-bootstrap with AI" : "Bootstrap with AI"}
                </button>
                <span className="text-gray-600">GPT-4o generates local knowledge (agencies, units, streets)</span>
              </div>

              <div>
                <p className="text-gray-500 uppercase tracking-wider mb-1.5">Approved ({vocab.length})</p>
                {vocab.length > 0 ? (
                  <div className="flex flex-wrap gap-1.5">
                    {vocab.map((term) => (
                      <span key={term} className="inline-flex items-center gap-1 bg-gray-800 text-gray-300 px-2 py-0.5 rounded-full">
                        {term}
                        <button
                          onClick={() => handleRemove(term)}
                          className="text-gray-600 hover:text-red-400 transition-colors leading-none"
                        >
                          ×
                        </button>
                      </span>
                    ))}
                  </div>
                ) : (
                  <p className="text-gray-600 italic">No terms yet — bootstrap or add manually.</p>
                )}
              </div>

              <form onSubmit={handleAdd} className="flex gap-2">
                <input
                  value={newTerm}
                  onChange={(e) => setNewTerm(e.target.value)}
                  placeholder="Add term (e.g. 5-baker, YVAC)"
                  className="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-1.5 text-white text-xs font-mono focus:outline-none focus:border-indigo-500"
                />
                <button
                  type="submit"
                  disabled={adding || !newTerm.trim()}
                  className="bg-gray-700 hover:bg-gray-600 disabled:opacity-50 text-gray-200 px-3 py-1.5 rounded-lg transition-colors"
                >
                  Add
                </button>
              </form>

              {pending.length > 0 && (
                <div>
                  <p className="text-gray-500 uppercase tracking-wider mb-1.5">
                    Induction suggestions ({pending.length})
                  </p>
                  <div className="space-y-2">
                    {pending.map((p) => (
                      <div key={p.term} className="space-y-1">
                        <div className="flex items-center gap-2">
                          <span className="text-gray-300 flex-1">{p.term}</span>
                          <span className="text-gray-600">{p.source}</span>
                          <button onClick={() => handleApprove(p.term)} className="text-green-500 hover:text-green-400 transition-colors px-1">✓</button>
                          <button onClick={() => handleDismiss(p.term)} className="text-gray-600 hover:text-red-400 transition-colors px-1">✕</button>
                        </div>
                        {p.source_call_ids && p.source_call_ids.length > 0 && (
                          <div className="pl-1 space-y-1">
                            {p.source_call_ids.map((id: string) => (
                              <Fragment key={id}>
                                <SourceCallPlayer callId={id} />
                              </Fragment>
                            ))}
                          </div>
                        )}
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </>
          )}
        </div>
      )}
    </div>
  );
}

// ── Systems list page ─────────────────────────────────────────────────────────

export default function SystemsPage() {
  const { isAdmin, isOperator, loading: authLoading } = useAuth();
  const router = useRouter();
  const { systems, loading } = useSystems();

  useEffect(() => {
    if (!authLoading && !isAdmin && !isOperator) router.replace("/dashboard");
  }, [authLoading, isAdmin, isOperator, router]);

  if (authLoading || (!isAdmin && !isOperator)) return null;
  const [editing, setEditing]             = useState<SystemRecord | null | "new">(null);
  const [editIsDuplicate, setEditIsDuplicate] = useState(false);

  async function handleDelete(id: string) {
    if (!confirm("Delete this system?")) return;
    await c2api.deleteSystem(id);
  }

  function openEdit(s: SystemRecord) {
    setEditing(s);
    setEditIsDuplicate(false);
  }

  function openDuplicate(s: SystemRecord) {
    setEditing({ ...s, name: `Copy of ${s.name}` });
    setEditIsDuplicate(true);
  }

  function closeEdit() {
    setEditing(null);
    setEditIsDuplicate(false);
  }

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <h1 className="text-xl font-bold text-white font-mono">Systems</h1>
        <button
          onClick={() => { setEditing("new"); setEditIsDuplicate(false); }}
          className="bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2 rounded-lg text-sm font-mono transition-colors"
        >
          + New System
        </button>
      </div>

      {editing && (
        <SystemForm
          initial={editing === "new" ? undefined : editing}
          createOnly={editIsDuplicate}
          onSave={closeEdit}
          onCancel={closeEdit}
        />
      )}

      {loading ? (
        <p className="text-gray-600 text-sm font-mono">Loading…</p>
      ) : systems.length === 0 ? (
        <p className="text-gray-600 text-sm font-mono">No systems defined yet.</p>
      ) : (
        <div className="space-y-3">
          {systems.map((s) => {
            const tgCount = Array.isArray(s.config.talkgroups)
              ? (s.config.talkgroups as unknown[]).length
              : null;
            const ccCount = Array.isArray(s.config.control_channels)
              ? (s.config.control_channels as unknown[]).length
              : null;
            return (
              <div key={s.system_id} className="bg-gray-900 border border-gray-800 rounded-lg p-4 font-mono">
                <div className="flex items-start justify-between">
                  <div>
                    <p className="text-white font-semibold">{s.name}</p>
                    <p className="text-xs text-gray-500">{s.system_id}</p>
                    {(s.config.nac || ccCount !== null || tgCount !== null) && (
                      <p className="text-xs text-gray-600 mt-0.5 space-x-2">
                        {!!s.config.nac && <span>NAC {String(s.config.nac)}</span>}
                        {ccCount !== null && <span>· {ccCount} CC</span>}
                        {tgCount !== null && <span>· {tgCount} TGs</span>}
                      </p>
                    )}
                  </div>
                  <span className="text-xs bg-gray-800 border border-gray-700 text-gray-400 px-2 py-0.5 rounded">
                    {s.type}
                  </span>
                </div>
                <div className="mt-3 flex gap-3">
                  <button
                    onClick={() => openEdit(s)}
                    className="text-xs text-indigo-400 hover:text-indigo-300 transition-colors"
                  >
                    Edit
                  </button>
                  <button
                    onClick={() => openDuplicate(s)}
                    className="text-xs text-gray-500 hover:text-gray-300 transition-colors"
                  >
                    Duplicate
                  </button>
                  <button
                    onClick={() => handleDelete(s.system_id)}
                    className="text-xs text-red-500 hover:text-red-400 transition-colors"
                  >
                    Delete
                  </button>
                </div>
                <PreferredTokenPanel systemId={s.system_id} initialTokenId={s.preferred_token_id} />
                <AiFlagsPanel systemId={s.system_id} initial={(s as unknown as { ai_flags?: SystemAiFlags }).ai_flags ?? {}} />
                <VocabularyPanel systemId={s.system_id} />
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}
