/* Eno.Health Console — Documents: Live pipeline board + enhanced history */
const P = window.ENO;

/* ======================================================================== */
/* LIVE PIPELINE  (client-side simulation of the processing pipeline)        */
/* ======================================================================== */
const SAMPLE_FILES = ["cbc-panel.pdf", "lipid-profile.pdf", "metabolic.pdf", "hba1c.pdf", "thyroid.pdf",
  "liver-fn.pdf", "blood-gas.pdf", "coag-pt-inr.pdf", "wgs-report.pdf", "gene-panel.json", "ecg-strip.png", "intake.pdf"];
let LIVE_SEQ = 1;
function makeLiveDoc(bornAt) {
  const lab = P.LABS[Math.floor(Math.random() * P.LABS.length)];
  const t = P.TENANTS[Math.floor(Math.random() * P.TENANTS.length)];
  const panelKeys = (lab.panels && lab.panels.length) ? lab.panels : P.TEST_PANELS.map((p) => p.key);
  const panel = panelKeys[Math.floor(Math.random() * panelKeys.length)];
  const willFail = lab.status === "unsupported" ? Math.random() < 0.16 : Math.random() < 0.035;
  let failAt = null;
  if (willFail) failAt = [1, 3, 3, 9][Math.floor(Math.random() * 4)];
  const willReview = lab.status !== "supported" && Math.random() < 0.45;
  return { uid: "L" + (LIVE_SEQ++), id: "doc_lv" + Math.random().toString(36).slice(2, 8),
    file: SAMPLE_FILES[Math.floor(Math.random() * SAMPLE_FILES.length)], tenant: t.id, lab: lab.id, labStatus: lab.status,
    panel, stage: 0, enteredAt: bornAt, bornAt, state: "active", justMoved: true, failAt, willReview, review: false };
}
function seedLive() {
  const now = Date.now(); const arr = [];
  for (let i = 0; i < 22; i++) {
    const d = makeLiveDoc(now - (3000 + Math.random() * 40000));
    d.stage = Math.floor(Math.random() * P.PIPE_LEN);
    d.enteredAt = now - Math.random() * 5000;
    if (d.failAt != null && d.stage >= d.failAt) d.failAt = null;
    if (d.stage >= 4 && d.willReview) d.review = true;
    arr.push(d);
  }
  return arr;
}

function LivePipelineView({ onOpenDoc }) {
  const docsRef = useRef(seedLive());
  const [docs, setDocs] = useState(docsRef.current);
  const [log, setLog] = useState([]);
  const [paused, setPaused] = useState(false);
  const [sel, setSel] = useState(null);
  const stats = useRef({ published: [], failures: [], latencies: [] });

  useEffect(() => {
    if (paused) return;
    const iv = setInterval(() => {
      const now = Date.now();
      const events = [];
      let next = docsRef.current.map((d) => ({ ...d, justMoved: false }));
      next.forEach((d) => {
        if (d.state !== "active") return;
        if (Math.random() < 0.5) {
          const from = d.stage;
          d.stage = Math.min(P.PIPE_LEN - 1, d.stage + 1);
          d.justMoved = true; d.enteredAt = now;
          if (d.failAt != null && d.stage === d.failAt) {
            d.state = "failed"; d.failedAt = now; stats.current.failures.push(now);
            events.unshift({ k: "fail", id: d.id, file: d.file, stage: d.stage, at: now });
          } else {
            if (d.stage >= 4 && d.willReview) d.review = true;
            events.unshift({ k: "move", id: d.id, file: d.file, from, stage: d.stage, at: now, review: d.review });
          }
        }
      });
      next.forEach((d) => {
        if (d.state === "active" && d.stage === P.PIPE_LEN - 1 && now - d.enteredAt > 1400) {
          d.state = "done"; d.doneAt = now;
          stats.current.published.push(now); stats.current.latencies.push(now - d.bornAt);
          events.unshift({ k: "done", id: d.id, file: d.file, stage: d.stage, at: now });
        }
      });
      next = next.filter((d) => {
        if (d.state === "done") return now - d.doneAt < 450;
        if (d.state === "failed") return now - d.failedAt < 2200;
        return true;
      });
      const active = next.filter((d) => d.state === "active").length;
      if (active < 28) { const n = 1 + (Math.random() < 0.5 ? 1 : 0); for (let i = 0; i < n; i++) next.push(makeLiveDoc(now)); }
      docsRef.current = next; setDocs(next);
      if (events.length) setLog((l) => [...events, ...l].slice(0, 9));
    }, 1150);
    return () => clearInterval(iv);
  }, [paused]);

  const now = Date.now();
  stats.current.published = stats.current.published.filter((t) => now - t < 60000);
  stats.current.failures = stats.current.failures.filter((t) => now - t < 60000);
  const active = docs.filter((d) => d.state === "active");
  const perStage = P.PIPELINE.map((_, i) => docs.filter((d) => d.stage === i && d.state === "active").length);
  const deepest = perStage.reduce((m, c, i) => (c > m.c ? { c, i } : m), { c: 0, i: 0 });
  const lat = stats.current.latencies.slice(-12);
  const avgLat = lat.length ? (lat.reduce((a, b) => a + b, 0) / lat.length / 1000) : 0;
  const reviewable = docs.filter((d) => d.stage >= 4 && d.state === "active");
  const reviewPct = reviewable.length ? Math.round(100 * reviewable.filter((d) => d.review).length / reviewable.length) : 0;

  return h("div", { className: "view" },
    h("div", { className: "ref-banner" }, h(Icon, { name: "activity", size: 16, className: "dim" }),
      h("div", null, "Realtime view of documents flowing through the processing pipeline. ",
        "Cards advance ", h("strong", null, "Ingest → Publish"), " as each stage completes; unsupported-lab documents fail or route to review more often. Click any card to inspect it.")),
    h(LiveMetrics, { throughput: stats.current.published.length, inflight: active.length, avgLat, reviewPct,
      failures: stats.current.failures.length, deepest, paused, onToggle: () => setPaused((p) => !p) }),
    h(LiveRows, { docs, onSelect: setSel }),
    h(LiveDocDrawer, { doc: sel, onClose: () => setSel(null) }));
}

function LiveMetrics({ throughput, inflight, avgLat, reviewPct, failures, deepest, paused, onToggle }) {
  const tiles = [
    { label: "Throughput", value: throughput, unit: "/min", icon: "activity", tone: "ok" },
    { label: "In-flight", value: inflight, icon: "runs" },
    { label: "Avg latency", value: avgLat ? avgLat.toFixed(1) : "—", unit: avgLat ? "s" : "", icon: "clock" },
    { label: "Manual review", value: reviewPct, unit: "%", icon: "alert", tone: reviewPct > 25 ? "warn" : undefined },
    { label: "Failures", value: failures, unit: "/min", icon: "x", tone: failures ? "bad" : "ok" },
  ];
  return h("div", { className: "pipe-metrics-row" },
    h("div", { className: "pipe-metrics" }, tiles.map((t) => h("div", { className: "pmetric", key: t.label },
      h("div", { className: "pmetric-top" }, h("span", { className: "pmetric-label" }, t.label), h(Icon, { name: t.icon, size: 14, className: "dim" })),
      h("div", { className: "pmetric-val " + (t.tone ? "tone-ink-" + t.tone : "") }, t.value, t.unit && h("span", { className: "pmetric-unit" }, t.unit))))),
    h("div", { className: "pipe-live-ctl" },
      h("span", { className: "live-dot" + (paused ? " paused" : "") }), h("span", { className: "live-word" }, paused ? "Paused" : "Live"),
      h(Button, { variant: "ghost", size: "sm", icon: paused ? "play" : "pause", onClick: onToggle }, paused ? "Resume" : "Pause")));
}

function PipelineDiagram({ perStage, docs }) {
  const max = Math.max(1, ...perStage);
  const failPer = P.PIPELINE.map((_, i) => docs.filter((d) => d.stage === i && d.state === "failed").length);
  return h(Card, { pad: false, className: "pipe-diagram-card" },
    h("div", { className: "pipe-diagram" },
      P.PIPELINE.map((s, i) => h(React.Fragment, { key: s.key },
        h("div", { className: "pnode" + (perStage[i] ? " pnode-on" : "") + (failPer[i] ? " pnode-fail" : "") },
          h("div", { className: "pnode-count" }, perStage[i]),
          h("div", { className: "pnode-bar" }, h("span", { style: { height: Math.round((perStage[i] / max) * 100) + "%" } })),
          h("div", { className: "pnode-label" }, s.short),
          failPer[i] ? h("div", { className: "pnode-failbadge", title: failPer[i] + " failing" }, failPer[i]) : null),
        i < P.PIPELINE.length - 1 ? h("div", { className: "pnode-arrow" }, h(Icon, { name: "arrowRight", size: 14 })) : null))));
}

const CONF_DOT = (status) => status === "supported" ? "ok" : status === "partial" ? "warn" : "bad";
function LiveRows({ docs, onSelect }) {
  const rows = docs.filter((d) => d.state !== "done"); // ready docs leave the live view
  return h(Card, { pad: false, className: "liverows-card", title: "Live documents", subtitle: "In processing now · completed documents leave the view" },
    h("div", { className: "liverows" },
      rows.length === 0 && h("div", { className: "sse-wait" }, h(Icon, { name: "spinner", size: 16, className: "spin" }), "Waiting for documents…"),
      rows.map((d) => h(LiveRow, { key: d.uid, d, onSelect }))));
}

function LiveRow({ d, onSelect }) {
  const pct = Math.round(((d.stage + 1) / P.PIPE_LEN) * 100);
  const stage = P.PIPELINE[d.stage];
  return h("button", { className: "lrow" + (d.justMoved ? " lrow-moved" : "") + (d.state === "failed" ? " lrow-failed" : "") + (d.review ? " lrow-review" : ""), onClick: () => onSelect(d) },
    h("div", { className: "lrow-id" },
      h("span", { className: "lrow-file" }, d.file),
      h("span", { className: "lrow-lab" },
        h("span", { className: "svc-dot svc-" + (CONF_DOT(d.labStatus) === "ok" ? "up" : CONF_DOT(d.labStatus) === "warn" ? "stale" : "down") }),
        P.labName(d.lab), " · ", P.tenantName(d.tenant))),
    h("div", { className: "lrow-prog" },
      h("div", { className: "lrow-segs" }, P.PIPELINE.map((s, i) =>
        h("span", { key: i, title: s.label, className: "lseg " + (i < d.stage ? "lseg-done" : i === d.stage ? (d.state === "failed" ? "lseg-fail" : "lseg-active") : "lseg-pending") }))),
      h("div", { className: "lrow-stagelbl" }, d.state === "failed" ? "Failed at " + stage.short : stage.label)),
    h("div", { className: "lrow-right" },
      d.state === "failed" ? h(Pill, { tone: "bad" }, "failed")
        : d.review ? h(Pill, { tone: "warn", soft: true }, "review")
        : h("span", { className: "lrow-pct mono" }, pct, "%"),
      h(Icon, { name: "chevronR", size: 14, className: "row-arrow" })));
}

function TransitionLog({ log }) {
  return h(Card, { pad: false, className: "translog-card", title: "Live transitions", subtitle: "Most recent stage changes" },
    h("div", { className: "translog" },
      log.length === 0 && h("div", { className: "sse-wait" }, h(Icon, { name: "spinner", size: 15, className: "spin" }), "Waiting for transitions…"),
      log.map((e) => h("div", { className: "trow trow-" + e.k, key: e.id + e.at },
        h("span", { className: "trow-icon" }, h(Icon, { name: e.k === "fail" ? "x" : e.k === "done" ? "check" : "arrowRight", size: 13 })),
        h(Mono, { className: "trow-file" }, e.file),
        h("span", { className: "trow-stage" },
          e.k === "done" ? "published" : e.k === "fail" ? "failed at " + P.PIPELINE[e.stage].short
            : P.PIPELINE[e.stage].short),
        e.review && e.k === "move" ? h("span", { className: "kcard-tag warn" }, "review") : null,
        h("span", { className: "trow-time mono dim" }, "just now")))));
}

function LiveDocDrawer({ doc, onClose }) {
  if (!doc) return h(Drawer, { open: false, onClose });
  const lab = P.LABS.find((l) => l.id === doc.lab) || {};
  const elapsed = ((Date.now() - doc.bornAt) / 1000).toFixed(1);
  const reachedFail = doc.state === "failed" ? doc.stage : -1;
  return h(Drawer, { open: true, onClose, width: 560, title: doc.file,
    sub: h(React.Fragment, null, h(Mono, { copy: true }, doc.id),
      doc.state === "failed" ? h(Pill, { tone: "bad" }, "failed") : doc.state === "done" ? h(Pill, { tone: "ok" }, "published") : h(Pill, { tone: "info" }, "in-flight")) },
    h("div", { className: "refbox" },
      h("div", { className: "ref-banner" }, h(Icon, { name: "clock", size: 15, className: "dim" }),
        h("div", null, "In-flight document — the full immutable audit trail becomes available in ", h("strong", null, "History"), " once it reaches Publish.")),
      h("div", { className: "meta-grid" },
        h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Tenant"), h("span", { className: "meta-v" }, P.tenantName(doc.tenant))),
        h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Lab / author"), h("span", { className: "meta-v" }, lab.name + " ", h("span", { className: "tag tag-" + (CONF_DOT(doc.labStatus) === "ok" ? "" : "accent") }, doc.labStatus))),
        h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Test type"), h("span", { className: "meta-v" }, doc.panel ? P.panelName(doc.panel) : "—")),
        h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Elapsed"), h("span", { className: "meta-v mono" }, elapsed + "s"))),
      h("div", { className: "refsec" }, h("p", { className: "refsec-title" }, "Pipeline progress"),
        h("div", { className: "vstage" }, P.PIPELINE.map((s, i) => {
          const st = reachedFail === i ? "failed" : i < doc.stage ? "done" : i === doc.stage ? (doc.state === "done" ? "done" : "active") : "pending";
          return h("div", { className: "vstage-row", key: s.key },
            h("span", { className: "vstage-mark vstage-" + st }, h(Icon, { name: st === "failed" ? "x" : st === "done" ? "check" : st === "active" ? "dot" : "dot", size: 12 })),
            h("span", { className: "vstage-label" }, s.label),
            h("span", { className: "vstage-svc mono dim" }, s.service),
            i < P.PIPELINE.length - 1 ? h("span", { className: "vstage-line" }) : null);
        })))));
}

/* ======================================================================== */
/* HISTORY  (enhanced query over ingested documents)                         */
/* ======================================================================== */
const DATE_RANGES = [
  { value: "all", label: "Any time", ms: Infinity },
  { value: "1h", label: "Last hour", ms: 3600e3 },
  { value: "24h", label: "Last 24 hours", ms: 24 * 3600e3 },
  { value: "7d", label: "Last 7 days", ms: 7 * 24 * 3600e3 },
  { value: "30d", label: "Last 30 days", ms: 30 * 24 * 3600e3 },
];

function DocumentHistory({ onOpenDoc }) {
  const [q, setQ] = useState("");
  const [status, setStatus] = useState("all");
  const [tenant, setTenant] = useState("all");
  const [lab, setLab] = useState("all");
  const [type, setType] = useState("all");
  const [bm, setBm] = useState("all");
  const [stage, setStage] = useState("all");
  const [conf, setConf] = useState("all");
  const [range, setRange] = useState("all");

  const statusOpts = [{ value: "all", label: "All statuses" }, ...Object.keys(P.STATUS).map((k) => ({ value: k, label: P.STATUS[k].label }))];
  const typeOpts = [{ value: "all", label: "All types" },
    ...P.TEST_PANELS.map((p) => ({ value: "p:" + p.key, label: "Panel · " + p.name })),
    ...P.DOC_TYPES.filter((t) => t.key !== "lab").map((t) => ({ value: "t:" + t.key, label: t.label }))];
  const stageOpts = [{ value: "all", label: "Any stage reached" }, ...P.PIPELINE.map((s, i) => ({ value: String(i), label: (i + 1) + ". " + s.label })), { value: "halted", label: "⚠ Halted / failed only" }];
  const confOpts = [{ value: "all", label: "Any confidence" }, { value: "review", label: "Requires review (<85%)" }, { value: "high", label: "High (≥95%)" }, { value: "mid", label: "Medium (85–95%)" }, { value: "na", label: "No score (failed)" }];

  const rangeMs = (DATE_RANGES.find((r) => r.value === range) || {}).ms || Infinity;
  const rows = P.DOCUMENTS.filter((d) => {
    if (status !== "all" && d.statusKey !== status) return false;
    if (tenant !== "all" && d.tenant !== tenant) return false;
    if (lab !== "all" && d.lab !== lab) return false;
    if (type !== "all") {
      if (type.startsWith("p:") && d.panel !== type.slice(2)) return false;
      if (type.startsWith("t:") && d.docType !== type.slice(2)) return false;
    }
    if (bm !== "all" && !(d.biomarkers || []).includes(bm)) return false;
    if (stage !== "all") {
      if (stage === "halted") { if (!d.pipeHalted) return false; }
      else if (d.pipeReached !== Number(stage)) return false;
    }
    if (conf !== "all") {
      const c = d.extractionConfidence;
      if (conf === "review" && !d.requiresReview) return false;
      if (conf === "na" && c != null) return false;
      if (conf === "high" && !(c != null && c >= 95)) return false;
      if (conf === "mid" && !(c != null && c >= 85 && c < 95)) return false;
    }
    if (rangeMs !== Infinity && (P.NOW - d.createdAt.getTime()) > rangeMs) return false;
    if (q && !(d.id + d.file + d.patient + d.correlationId + P.labName(d.lab)).toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });

  const reset = () => { setStatus("all"); setTenant("all"); setLab("all"); setType("all"); setBm("all"); setStage("all"); setConf("all"); setRange("all"); setQ(""); };
  const active = [status, tenant, lab, type, bm, stage, conf, range].filter((v) => v !== "all").length + (q ? 1 : 0);
  const head = ["Document", "Tenant", "Lab / author", "Type", "Status", "Stage reached", "Confidence", "Age"];

  return h("div", { className: "view" },
    h(Card, { pad: false },
      h("div", { className: "toolbar" },
        h("div", { className: "search" }, h(Icon, { name: "search", size: 16 }),
          h("input", { className: "search-input", placeholder: "Search ID, file, patient, correlation, lab…", value: q, onChange: (e) => setQ(e.target.value) })),
        h("span", { className: "toolbar-count" }, rows.length, " of ", P.DOCUMENTS.length),
        active > 0 && h(Button, { variant: "ghost", size: "sm", icon: "x", onClick: reset }, "Clear " + active)),
      h("div", { className: "filterbar" },
        h(Select, { value: status, onChange: setStatus, options: statusOpts }),
        h(Select, { value: tenant, onChange: setTenant, options: [{ value: "all", label: "All tenants" }, ...P.TENANTS.map((t) => ({ value: t.id, label: t.name }))] }),
        h(Select, { value: lab, onChange: setLab, options: [{ value: "all", label: "All labs" }, ...P.LABS.map((l) => ({ value: l.id, label: l.name }))] }),
        h(Select, { value: type, onChange: setType, options: typeOpts }),
        h(Select, { value: bm, onChange: setBm, options: [{ value: "all", label: "Any biomarker" }, ...P.BIOMARKERS.map((b) => ({ value: b.id, label: b.name }))] }),
        h(Select, { value: stage, onChange: setStage, options: stageOpts }),
        h(Select, { value: conf, onChange: setConf, options: confOpts }),
        h(Select, { value: range, onChange: setRange, options: DATE_RANGES.map((r) => ({ value: r.value, label: r.label })) })),
      h("div", { className: "table-wrap" },
        h("table", { className: "table" },
          h("thead", null, h("tr", null, head.map((c, i) => h("th", { key: i, className: i === 7 ? "ta-r" : "" }, c)))),
          h("tbody", null,
            rows.map((d) => h(HistRow, { key: d.id, d, onClick: () => onOpenDoc(d) })),
            !rows.length && h("tr", null, h("td", { colSpan: 8 }, h(Empty, { title: "No documents match", sub: "Try clearing some filters." }))))))));
}

function HistRow({ d, onClick }) {
  const c = d.extractionConfidence;
  const confTone = c == null ? "neutral" : c >= 95 ? "ok" : c >= 85 ? "info" : "warn";
  return h("tr", { className: "row-click", onClick },
    h("td", null, h("div", { className: "cell-doc" }, h("span", { className: "cell-file" }, d.file), h(Mono, { className: "cell-id" }, d.id))),
    h("td", null, h("span", { className: "cell-tenant" }, P.tenantName(d.tenant))),
    h("td", null, P.labName(d.lab)),
    h("td", null, d.panel ? h("span", { className: "tag tag-accent" }, P.panelName(d.panel)) : h("span", { className: "tag" }, (P.DOC_TYPES.find((t) => t.key === d.docType) || {}).label || d.docType)),
    h("td", null, h(StatusPill, { status: d.status })),
    h("td", null, h(StageReached, { d })),
    h("td", null, c == null ? h("span", { className: "dim" }, "—")
      : h("span", { className: "conf-cell-sm" }, h(Pill, { tone: confTone, soft: true }, c.toFixed(1) + "%"), d.requiresReview ? h("span", { className: "kcard-tag warn" }, "review") : null)),
    h("td", { className: "ta-r dim" }, fmtAgo(d.createdAt)));
}

function StageReached({ d }) {
  const pct = Math.round(((d.pipeReached + 1) / P.PIPE_LEN) * 100);
  const tone = d.pipeHalted ? "bad" : pct === 100 ? "ok" : "info";
  return h("div", { className: "stagereached" },
    h("div", { className: "sr-bar" }, h("span", { style: { width: pct + "%", background: "var(--" + tone + ")" } })),
    h("span", { className: "sr-label" + (d.pipeHalted ? " bad-ink" : "") }, d.pipeHalted ? "halted · " : "", P.PIPELINE[d.pipeReached].short));
}

Object.assign(window, { LivePipelineView, DocumentHistory });
