// Exceptions Inbox — Stage 5.A. Route: /{slug}/exceptions-inbox.
//
// A prioritized inbox for the exception signals the evaluator produces (fetched
// via cfo-synthesis surface_type="exceptions_inbox"). Each item shows severity,
// dollar impact, diagnosed driver, a recommended action, and a drill-into. The
// user can mark an item resolved or dismissed (with an optional note); that
// disposition persists to exception_signal_state, keyed on
// (company_id, signal_id, signal_period_end). Resolved/dismissed items grey out
// but stay listed under their filter — the audit history is preserved.
//
// Reads ONLY the synthesis service output for the narrative (no engine logic,
// no catalog reads here) + exception_signal_state for disposition. Universal
// across archetypes; only the synthesized content varies.

(function () {
  const R = window.React;
  if (!R) return;
  const { useState, useEffect, useCallback } = R;
  const db = () => window.supabaseClient;

  const SEV_RANK = { high: 3, medium: 2, low: 1, info: 0 };
  const SEV_COLOR = { high: "#c0392b", medium: "#b7791f", low: "#2f6fed", info: "#6b7785" };
  const SEV_EMOJI = { high: "🔴", medium: "🟡", low: "🟢", info: "⚪" };
  const fmtAsOf = (d) => {
    if (!d) return null;
    const dt = new Date(String(d).length <= 10 ? d + "T00:00:00" : d);
    return isNaN(dt.getTime()) ? String(d) : dt.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" });
  };

  function money(v) {
    if (v == null) return null;
    const f = window.PerduraFormat;
    if (f && typeof f.money === "function") return f.money(v, { compact: true });
    if (typeof window.fmtUSD === "function") return window.fmtUSD(v, { compact: true });
    return "$" + Math.round(v).toLocaleString();
  }

  // Disposition key — must match the unique key on exception_signal_state.
  const keyOf = (signalId, periodEnd) => `${signalId}::${periodEnd || ""}`;

  function ExceptionsInboxPage({ scopedCompanyId, companyProfile, setPage, setDrillKey }) {
    const tenantId = scopedCompanyId || (companyProfile && companyProfile.id) || null;
    const [phase, setPhase] = useState("loading");      // loading | done | error | empty
    const [items, setItems] = useState([]);             // synthesized exception items
    const [meta, setMeta] = useState({});               // { signal_count, model, retried, period_start, period_end }
    const [errorMsg, setErrorMsg] = useState("");
    const [stateMap, setStateMap] = useState({});        // keyOf → { status, resolution_notes, ... }
    const [statusFilter, setStatusFilter] = useState("active");
    const [sevFilter, setSevFilter] = useState("all");
    const [noteDraft, setNoteDraft] = useState({});      // keyOf → note text (open editor)
    const [busyKey, setBusyKey] = useState(null);

    const load = useCallback(async () => {
      if (!tenantId) { setPhase("error"); setErrorMsg("No tenant in context."); return; }
      setPhase("loading");
      // 1. Synthesized exceptions (narrative + severity + signal_id + period).
      const res = await window.PerduraSynthesis.fetch(tenantId, "exceptions_inbox", {});
      if (res && res.error) { setPhase("error"); setErrorMsg(res.error); return; }
      const list = Array.isArray(res.prioritized) ? res.prioritized : [];
      setMeta({
        signal_count: res.signal_count, model: res.model, retried: res.retried,
        period_start: res.period_start || null, period_end: res.period_end || null,
      });
      // 2. Disposition rows for this tenant (overlay; full history preserved).
      let sm = {};
      try {
        const { data } = await db().from("exception_signal_state")
          .select("signal_id,signal_period_end,status,resolution_notes,resolved_at,dismissed_at")
          .eq("company_id", tenantId);
        for (const r of (data || [])) sm[keyOf(r.signal_id, r.signal_period_end)] = r;
      } catch (_e) { /* table not deployed yet → everything reads as active */ }
      setStateMap(sm);
      setItems(list);
      setPhase(list.length ? "done" : "empty");
    }, [tenantId]);

    useEffect(() => { load(); }, [load]);

    // Persist a disposition (resolved / dismissed / active) for an item.
    async function setDisposition(item, status, note) {
      const sid = item.signal_id || (item.source_signal_ids || [])[0];
      if (!sid) return;
      const pEnd = item.period_end || meta.period_end || null;
      const pStart = item.period_start || meta.period_start || null;
      const k = keyOf(sid, pEnd);
      setBusyKey(k);
      let userId = null;
      try { const { data } = await db().auth.getUser(); userId = data && data.user ? data.user.id : null; } catch (_e) { /* ignore */ }
      const now = new Date().toISOString();
      const row = {
        company_id: tenantId, signal_id: sid, signal_period_start: pStart, signal_period_end: pEnd,
        status, resolution_notes: note || null, updated_at: now,
        resolved_at: status === "resolved" ? now : null,
        resolved_by: status === "resolved" ? userId : null,
        dismissed_at: status === "dismissed" ? now : null,
        dismissed_by: status === "dismissed" ? userId : null,
      };
      try {
        const { error } = await db().from("exception_signal_state")
          .upsert(row, { onConflict: "company_id,signal_id,signal_period_end" });
        if (error) throw new Error(error.message);
        setStateMap((m) => ({ ...m, [k]: row }));
        setNoteDraft((d) => { const n = { ...d }; delete n[k]; return n; });
      } catch (e) {
        setErrorMsg("Could not save: " + ((e && e.message) || e));
      }
      setBusyKey(null);
    }

    function dispositionOf(item) {
      const sid = item.signal_id || (item.source_signal_ids || [])[0];
      const pEnd = item.period_end || meta.period_end || null;
      const st = stateMap[keyOf(sid, pEnd)];
      return st ? st.status : "active";
    }

    // Filter + rank for display.
    const visible = items
      .map((it) => ({ it, status: dispositionOf(it), sev: it.severity || "info" }))
      .filter((x) => statusFilter === "all" ? true : x.status === statusFilter)
      .filter((x) => sevFilter === "all" ? true : x.sev === sevFilter)
      .sort((a, b) =>
        (SEV_RANK[b.sev] || 0) - (SEV_RANK[a.sev] || 0)
        || Math.abs(b.it.estimated_dollar_impact || 0) - Math.abs(a.it.estimated_dollar_impact || 0));

    const counts = items.reduce((acc, it) => { const s = dispositionOf(it); acc[s] = (acc[s] || 0) + 1; return acc; }, {});

    const pill = (active, label, onClick, badge) => (
      <button type="button" onClick={onClick}
        className={"pc-pill" + (active ? " is-active" : "")}
        style={{
          padding: "5px 12px", borderRadius: 999, fontSize: 12.5, fontWeight: 600, cursor: "pointer",
          border: active ? "1px solid var(--accent, #2f6fed)" : "1px solid var(--border, #d8dee6)",
          background: active ? "var(--accent-soft, #eef6ff)" : "var(--surface, #fff)",
          color: active ? "var(--accent, #2f6fed)" : "var(--text-2)", marginRight: 8, marginBottom: 8,
        }}>
        {label}{badge != null ? ` · ${badge}` : ""}
      </button>
    );

    return (
      <div className="pc-page" data-page="exceptions">
        {window.PageExplainer
          ? R.createElement(window.PageExplainer, {
            id: "exceptions",
            title: "Exceptions Inbox",
            body: R.createElement(R.Fragment, null,
              "Anomalies and risks in your live numbers, ranked by severity and dollar impact, ",
              "explained by Perdura's CFO intelligence — every figure traces to a real signal. ",
              "Review each, then mark it resolved or dismiss it; your decisions are kept for the record."),
          })
          : <h1 style={{ fontSize: 22, fontWeight: 800, marginBottom: 4 }}>Exceptions Inbox</h1>}

        {/* Filters */}
        <div style={{ margin: "12px 0 4px", display: "flex", flexWrap: "wrap", alignItems: "center" }}>
          <span style={{ fontSize: 11.5, fontWeight: 700, textTransform: "uppercase", color: "var(--text-3)", marginRight: 10 }}>Status</span>
          {pill(statusFilter === "active", "Active", () => setStatusFilter("active"), counts.active || 0)}
          {pill(statusFilter === "resolved", "Resolved", () => setStatusFilter("resolved"), counts.resolved || 0)}
          {pill(statusFilter === "dismissed", "Dismissed", () => setStatusFilter("dismissed"), counts.dismissed || 0)}
          {pill(statusFilter === "all", "All", () => setStatusFilter("all"), items.length)}
        </div>
        <div style={{ margin: "0 0 14px", display: "flex", flexWrap: "wrap", alignItems: "center" }}>
          <span style={{ fontSize: 11.5, fontWeight: 700, textTransform: "uppercase", color: "var(--text-3)", marginRight: 10 }}>Severity</span>
          {pill(sevFilter === "all", "All", () => setSevFilter("all"))}
          {pill(sevFilter === "high", "High", () => setSevFilter("high"))}
          {pill(sevFilter === "medium", "Medium", () => setSevFilter("medium"))}
          {pill(sevFilter === "low", "Low", () => setSevFilter("low"))}
        </div>

        {errorMsg && phase !== "error" && (
          <div style={{ color: "var(--danger, #c0392b)", fontSize: 13, marginBottom: 10 }}>{errorMsg}</div>
        )}

        {phase === "loading" && (window.SignalLoader
          ? R.createElement(window.SignalLoader, { label: "Scanning your financials for exceptions…" })
          : <div style={{ color: "var(--text-3)", padding: 24 }}>Scanning your financials for exceptions…</div>)}

        {phase === "error" && (window.IntelEmptyState
          ? R.createElement(window.IntelEmptyState, { title: "Couldn't load exceptions", detail: errorMsg, tone: "error" })
          : <div style={{ color: "var(--danger)", padding: 24 }}>{errorMsg}</div>)}

        {phase === "empty" && (window.IntelEmptyState
          ? R.createElement(window.IntelEmptyState, {
            title: meta.signal_count ? "No active exceptions" : "Awaiting your financial data",
            detail: meta.signal_count
              ? "We reviewed your receivables, payables, spend, and margins and found nothing this period that rises to an exception. We'll surface items here the moment something does."
              : "Once your general ledger is connected and a period of data lands, Perdura will surface anomalies and risks here.",
          })
          : <div style={{ color: "var(--text-3)", padding: 24 }}>No active exceptions.</div>)}

        {phase === "done" && (
          <div>
            {visible.length === 0 && (
              <div style={{ color: "var(--text-3)", fontSize: 13.5, padding: "16px 0" }}>
                No {statusFilter !== "all" ? statusFilter : ""} exceptions{sevFilter !== "all" ? ` at ${sevFilter} severity` : ""} to show.
              </div>
            )}
            {visible.map((x, i) => {
              const it = x.it; const status = x.status; const sev = x.sev;
              const sid = it.signal_id || (it.source_signal_ids || [])[0];
              const pEnd = it.period_end || meta.period_end || null;
              const k = keyOf(sid, pEnd);
              const st = stateMap[k];
              const greyed = status !== "active";
              const impact = money(it.estimated_dollar_impact);
              const noteOpen = Object.prototype.hasOwnProperty.call(noteDraft, k);
              return (
                <div key={k + ":" + i} style={{
                  border: "1px solid var(--border, #e3e8ef)", borderRadius: 12, padding: 16, marginBottom: 12,
                  background: "var(--surface, #fff)", opacity: greyed ? 0.62 : 1,
                }}>
                  <div style={{ display: "flex", justifyContent: "space-between", gap: 16, alignItems: "flex-start" }}>
                    <div style={{ flex: 1 }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
                        <span style={{
                          fontSize: 10.5, fontWeight: 800, textTransform: "uppercase", letterSpacing: 0.4,
                          color: SEV_COLOR[sev] || SEV_COLOR.info, background: (SEV_COLOR[sev] || SEV_COLOR.info) + "1a",
                          padding: "2px 8px", borderRadius: 999,
                        }}>{(SEV_EMOJI[sev] || SEV_EMOJI.info)} {sev}</span>
                        {status !== "active" && (
                          <span style={{ fontSize: 10.5, fontWeight: 700, textTransform: "uppercase", color: "var(--text-3)", background: "var(--surface-2, #eef1f5)", padding: "2px 8px", borderRadius: 999 }}>{status}</span>
                        )}
                        {pEnd && (
                          <span style={{ fontSize: 10.5, color: "var(--text-3)", marginLeft: "auto" }}>as of {fmtAsOf(pEnd)}</span>
                        )}
                      </div>
                      <div style={{ fontSize: 15.5, fontWeight: 700, color: "var(--text-1)", lineHeight: 1.35 }}>{it.headline}</div>
                    </div>
                    {impact && (
                      <div style={{ textAlign: "right", flex: "0 0 auto" }}>
                        <div style={{ fontSize: 19, fontWeight: 800, color: "var(--text-1)", fontFamily: "ui-monospace,monospace" }}>{impact}</div>
                        <div style={{ fontSize: 10.5, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: 0.3 }}>impact</div>
                      </div>
                    )}
                  </div>
                  {it.supporting_detail && <p style={{ fontSize: 13.5, color: "var(--text-2)", lineHeight: 1.55, margin: "8px 0 0" }}>{it.supporting_detail}</p>}
                  {it.diagnosed_driver && (
                    <p style={{ fontSize: 13, color: "var(--text-2)", lineHeight: 1.5, margin: "8px 0 0" }}>
                      <strong style={{ color: "var(--text-1)" }}>Driver:</strong> {it.diagnosed_driver}
                    </p>
                  )}
                  {it.recommended_action && (
                    <p style={{ fontSize: 13, color: "var(--text-2)", lineHeight: 1.5, margin: "8px 0 0" }}>
                      <strong style={{ color: "var(--text-1)" }}>Recommended action:</strong> {it.recommended_action}
                    </p>
                  )}
                  {st && st.resolution_notes && (
                    <p style={{ fontSize: 12.5, color: "var(--text-3)", fontStyle: "italic", margin: "8px 0 0" }}>Note: {st.resolution_notes}</p>
                  )}

                  {/* Actions */}
                  <div style={{ display: "flex", gap: 8, marginTop: 12, flexWrap: "wrap", alignItems: "center" }}>
                    {it.drill_into && (
                      <button className="pc-btn pc-btn-ghost" style={{ fontSize: 12.5 }}
                        onClick={() => window.PerduraSynthesis.navigateDrill(it.drill_into, setPage, setDrillKey)}>
                        Drill into detail →
                      </button>
                    )}
                    {status !== "active" ? (
                      <button className="pc-btn pc-btn-ghost" style={{ fontSize: 12.5 }} disabled={busyKey === k}
                        onClick={() => setDisposition(it, "active", null)}>Reopen</button>
                    ) : (
                      <>
                        <button className="pc-btn pc-btn-ghost" style={{ fontSize: 12.5 }} disabled={busyKey === k}
                          onClick={() => setNoteDraft((d) => ({ ...d, [k]: d[k] || "" }))}>
                          {noteOpen ? "Add a note…" : "Mark resolved / dismiss"}
                        </button>
                      </>
                    )}
                  </div>
                  {noteOpen && status === "active" && (
                    <div style={{ marginTop: 10 }}>
                      <textarea placeholder="Optional note (what you did, or why you're dismissing this)…"
                        value={noteDraft[k]} onChange={(e) => setNoteDraft((d) => ({ ...d, [k]: e.target.value }))}
                        style={{ width: "100%", minHeight: 52, padding: "8px 10px", borderRadius: 8, border: "1px solid var(--border, #d8dee6)", fontSize: 13, boxSizing: "border-box", resize: "vertical" }} />
                      <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
                        <button className="pc-btn pc-btn-primary" style={{ fontSize: 12.5 }} disabled={busyKey === k}
                          onClick={() => setDisposition(it, "resolved", noteDraft[k])}>Mark resolved</button>
                        <button className="pc-btn pc-btn-ghost" style={{ fontSize: 12.5 }} disabled={busyKey === k}
                          onClick={() => setDisposition(it, "dismissed", noteDraft[k])}>Dismiss</button>
                        <button className="pc-btn pc-btn-ghost" style={{ fontSize: 12.5 }}
                          onClick={() => setNoteDraft((d) => { const n = { ...d }; delete n[k]; return n; })}>Cancel</button>
                      </div>
                    </div>
                  )}
                </div>
              );
            })}
            <div style={{ fontSize: 11.5, color: "var(--text-3)", marginTop: 8 }}>
              Based on {meta.signal_count != null ? meta.signal_count : "—"} signal(s){meta.model ? ` · ${meta.model}` : ""}{meta.retried ? " · refined once" : ""}.
            </div>
          </div>
        )}
      </div>
    );
  }

  window.ExceptionsInboxPage = ExceptionsInboxPage;
})();
