// App shell + routing for Golda operator platform
const { useState: useSA, useEffect: useEA } = React;

function goldaFirst(...values) {
  return values.find((value) => value !== undefined && value !== null && String(value).trim()) || "";
}
function goldaText(value, fallback = "—") {
  const text = String(value ?? "").trim();
  return text || fallback;
}
// Map a milestone/status code to an operator-friendly label so the UI never shows raw, truncated
// enum codes (e.g. "DEPARTED_ORI", "CUSTOMS_CLEA"). Unknown codes fall back to Title Case.
const GOLDA_STAGE_LABELS = {
  intake_received: "Intake received",
  booking_confirmed: "Booking confirmed",
  shipment_booked: "Booked",
  in_transit: "In transit",
  departed_origin: "Departed origin",
  arrived_destination: "Arrived destination",
  customs_clearance: "Customs clearance",
  delivered: "Delivered"
};
function goldaStageLabel(code) {
  const raw = String(code ?? "").trim();
  if (!raw) return "—";
  const key = raw.toLowerCase();
  if (GOLDA_STAGE_LABELS[key]) return GOLDA_STAGE_LABELS[key];
  // Already-short codes (REO, BOOKED, PICKUP, …) stay as-is; otherwise Title-case the words.
  if (/^[A-Z0-9]{2,6}$/.test(raw)) return raw;
  return raw.replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
}
function goldaActionLabel(decision = {}) {
  const text = [decision.confirmLabel, decision.recommendation, decision.body, decision.question, decision.title, decision.stage].filter(Boolean).join(" ").toLowerCase();
  if (decision.confirmLabel) return decision.confirmLabel;
  if (text.includes("no change") || text.includes("no action") || text.includes("nothing required") || text.includes("not required")) return "Mark no action needed";
  if (text.includes("booked") && (text.includes("keep") || text.includes("no change") || text.includes("confirm"))) return "Keep as booked";
  if (text.includes("rate") || text.includes("price") || text.includes("cost")) return "Approve rate change";
  if (text.includes("clarification") || text.includes("ask both") || text.includes("missing") || text.includes("question")) return "Ask for clarification";
  if (text.includes("send") || decision.kind === "Vendor email draft") return "Send suggested email";
  if (text.includes("dismiss") || text.includes("acknowledge")) return "Acknowledge";
  return "Confirm suggested action";
}
function goldaDecisionCopy(decision = {}) {
  const recommended = goldaText(decision.recommendation || decision.body || decision.question || decision.actionRequiredNow, "Review this email and decide the next operational step.");
  const summary = goldaText(decision.summary || decision.emailSummary || decision.title || decision.subject, recommended);
  const confirmLabel = goldaActionLabel({ ...decision, recommendation: recommended });
  const isNoAction = /no change|no action|nothing required|not required/i.test(recommended + " " + confirmLabel);
  const willHappen = goldaText(decision.confirmImpact || decision.willHappen, isNoAction
    ? "Golda will close this prompt as acknowledged and will not send an email or change shipment data."
    : decision.kind === "Vendor email draft"
      ? "Golda will send the proposed vendor email and keep monitoring for the reply."
      : `Golda will record “${confirmLabel}” as the operator decision and continue the shipment workflow.`);
  return {
    summary,
    recommended,
    confirmLabel,
    willHappen,
    openLabel: decision.openLabel || "Open source email",
    guidancePrompt: decision.guidancePrompt || "Tell Golda what the next action should be instead…",
    sourceUrl: decision.sourceUrl || decision.emailUrl || null
  };
}
function goldaOpenSource(decision = {}) {
  const copy = goldaDecisionCopy(decision);
  if (copy.sourceUrl) return window.open(copy.sourceUrl, "_blank", "noopener,noreferrer");
  const query = encodeURIComponent(decision.sho || decision.subject || decision.customer || "");
  if (query) return window.open(`/api/emails/view?shipmentRef=${query}`, "_blank", "noopener,noreferrer");
  return window.open("/api/emails/view?limit=1", "_blank", "noopener,noreferrer");
}
window.GoldaDecisionCopy = goldaDecisionCopy;
window.GoldaOpenSource = goldaOpenSource;

// Light/dark theme toggle. The initial class is set pre-render in index.ts (no flash);
// this just flips it and persists the choice to localStorage["theme"].
function ThemeToggle() {
  const [dark, setDark] = React.useState(() => document.documentElement.classList.contains("dark"));
  function apply(next) {
    document.documentElement.classList.toggle("dark", next);
    try { localStorage.setItem("theme", next ? "dark" : "light"); } catch (e) {}
    setDark(next);
  }
  return (
    <button className="icon-btn" aria-label="Toggle dark mode" aria-pressed={dark}
            title={dark ? "Switch to light mode" : "Switch to dark mode"} onClick={() => apply(!dark)}>
      {dark ? I.sun : I.moon}
    </button>
  );
}

const NAV = [
  { id: "home", label: "Active Engagements", icon: "home", count: 8, group: "Workspace" },
  { id: "decisions", label: "Decision Review", icon: "inbox", count: 4, group: "Workspace" },
  { id: "vendors", label: "Vendor Approvals", icon: "user_check", count: 3, group: "Workspace" },
  { id: "sho", label: "SHO Report Inbox", icon: "report", count: 3, group: "Workspace" },
  { id: "sop", label: "Customer SOP", icon: "rules", count: 0, group: "Configure" },
  { id: "domains", label: "Domain Registry", icon: "globe", count: 1, group: "Configure" },
  { id: "feedback", label: "Feedback Insights", icon: "spark", count: 0, group: "Learn" },
  { id: "conclusions", label: "AI Requirements", icon: "report", count: 0, group: "Learn" }
];

function App() {
  const [liveReady, setLiveReady] = useSA(false);
  const [route, setRoute] = useSA({ name: "home" });
  const [guardFor, setGuardFor] = useSA(null);
  const [search, setSearch] = useSA("");
  const [currentUser, setCurrentUser] = useSA(null);

  useEA(() => {
    // Show the ACTUAL signed-in operator in the sidebar, not a hardcoded persona.
    let alive = true;
    fetch("/api/auth/me", { cache: "no-store" })
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (alive && d && d.user) setCurrentUser(d.user); })
      .catch(() => {});
    return () => { alive = false; };
  }, []);

  useEA(() => {
    const markReady = () => setLiveReady(true);
    window.addEventListener("golda-live-data-ready", markReady);
    if (window.GoldaLoadLiveData) window.GoldaLoadLiveData(); else markReady();
    return () => window.removeEventListener("golda-live-data-ready", markReady);
  }, []);

  const goto = (name, params) => setRoute({ name, ...params });
  window.GoldaGoto = goto; // let any screen navigate (e.g. "Open Decision Review", guard "Open shipment")
  const titles = {
    home: ["Active Engagements", "Threads Golda is monitoring, sorted by risk"],
    shipment: ["Shipment", "Full execution view"],
    decisions: ["Decision Review", "Understand each email, then confirm the exact next action"],
    vendors: ["Vendor Address Approval", "First-use approval for new vendor emails"],
    sho: ["SHO Report Inbox", "Daily ingestion from Unifreight"],
    sop: ["Customer SOP", "Field rules per customer and lane"],
    domains: ["Domain Registry", "Known sender domains and trust"],
    feedback: ["Feedback Insights", "Patterns from your decisions"],
    conclusions: ["AI Requirements", "Client-ready conclusions from the stored email and case analysis"]
  };
  const [title, sub] = titles[route.name] || titles.home;

  return (
    <div className="app">
      <aside className="sidebar">
        <div className="sidebar-brand">
          <div className="brand-mark">G</div>
          <div>
            <div className="brand-name">Golda</div>
          </div>
          <span className="brand-sub">v0.4</span>
        </div>
        {["Workspace","Configure","Learn"].map(g => {
          // In live mode, hide the screens that still show mock/static data; keep the real ones.
          const live = !!(window.GoldaData && window.GoldaData.__live);
          const hidden = live ? new Set(["vendors", "sho", "sop", "domains", "feedback", "conclusions"]) : new Set();
          const items = NAV.filter(n => n.group === g && !hidden.has(n.id));
          if (!items.length) return null; // drop now-empty group headers
          const countOf = (n) => !live ? n.count
            : n.id === "home" ? (window.GoldaData.engagements || []).length
            : n.id === "decisions" ? (window.GoldaData.decisions || []).length
            : n.count;
          return (
          <React.Fragment key={g}>
            <div className="sidebar-section">{g}</div>
            <nav>
              {items.map(n => (
                <button
                  key={n.id}
                  className={`nav-item ${route.name === n.id || (n.id === "home" && route.name === "shipment") ? "active" : ""}`}
                  onClick={() => goto(n.id)}
                >
                  <span className="nav-icon">{I[n.icon]}</span>
                  <span>{n.label}</span>
                  {countOf(n) > 0 && <span className="nav-count">{countOf(n)}</span>}
                </button>
              ))}
            </nav>
          </React.Fragment>
          );
        })}
        <div className="sidebar-footer">
          {(() => {
            const dn = (currentUser && currentUser.displayName) || "Operator";
            const initials = dn.split(/\s+/).filter(Boolean).map(w => w[0]).join("").slice(0, 2).toUpperCase() || "OP";
            const roleLabel = (currentUser && currentUser.roleLabel) || "Total Cargo";
            return (
              <React.Fragment>
                <div className="avatar">{initials}</div>
                <div style={{ minWidth: 0, flex: 1 }}>
                  <div className="user-name">{dn}</div>
                  <div className="user-role">Total Cargo · {roleLabel}</div>
                </div>
              </React.Fragment>
            );
          })()}
          <button className="icon-btn" style={{ color: "var(--sidebar-fg-muted)" }} title="Settings — not enabled in this preview" disabled>⚙</button>
        </div>
      </aside>

      <div className="main">
        <header className="topbar">
          {route.name === "shipment" ? (
            <div className="row" style={{ gap: 6 }}>
              <button className="btn btn-sm btn-ghost" onClick={() => goto("home")}>← Active</button>
              <span style={{ color: "var(--text-tertiary)" }}>/</span>
              <h1 className="mono">{route.sho}</h1>
            </div>
          ) : (
            <div>
              <h1>{title}</h1>
              <div className="topbar-sub">{sub}</div>
            </div>
          )}
          <div className="search">{I.search}<input placeholder="Search SHO, customer, vendor…" value={search} onChange={(ev) => { setSearch(ev.target.value); if (ev.target.value && route.name !== "home") goto("home"); }}/></div>
          <div className="topbar-actions">
            <ThemeToggle/>
            <button className="icon-btn has-badge" title="Notifications — not enabled in this preview" disabled>{I.bell}<span className="dot"/></button>
            <button className="icon-btn" title="Help — not enabled in this preview" disabled>{I.q}</button>
            <button className="btn btn-sm" onClick={async () => { await fetch("/api/auth/logout", { method: "POST" }); location.reload(); }}>Sign out</button>
          </div>
        </header>

        <main className="content">
          {!liveReady && <div className="banner info"><span>ⓘ</span><div>Loading live LCL Control Tower data…</div></div>}
          {liveReady && window.GoldaData && window.GoldaData.__loadError && <div className="banner"><span>⚠</span><div>Couldn’t load live data. Showing an empty board — please refresh, or sign out and back in.</div></div>}
          {route.name === "home" && <Screen_ActiveEngagements search={search} onOpenShipment={sho => goto("shipment", { sho })}/>}
          {route.name === "shipment" && <Screen_Shipment sho={route.sho} onOpenGuard={() => setGuardFor(window.GoldaData.decisions[0])}/>}
          {route.name === "decisions" && <Screen_DecisionReview onOpenGuard={d => setGuardFor(d)}/>}
          {route.name === "vendors" && <Screen_VendorApproval/>}
          {route.name === "sho" && <Screen_SHOInbox/>}
          {route.name === "sop" && <Screen_SOP/>}
          {route.name === "domains" && <Screen_Domains/>}
          {route.name === "feedback" && <Screen_Feedback/>}
          {route.name === "conclusions" && <Screen_AIConclusions/>}
        </main>
      </div>

      {guardFor && <GuardPanel decision={guardFor} onClose={() => setGuardFor(null)}/>}
    </div>
  );
}


// Live Worker data adapter: preserve the design screens while replacing mock engagement data when available.
(function installGoldaLiveAdapter(){
  const originalData = window.GoldaData;
  function first(...values){ return values.find((v) => v !== undefined && v !== null && String(v).trim()) || ""; }
  function pretty(value){ if(!value) return "—"; const d = new Date(value); return Number.isNaN(d.getTime()) ? String(value) : d.toISOString().slice(0, 10); }
  function parseSubject(subject){
    const text = String(subject || "");
    return {
      supplier: (text.match(/S\/([^ C\/]+)/i) || [])[1],
      consignee: (text.match(/C\/([^ P\/]+)/i) || [])[1],
      po: (text.match(/PO\/([^\s\/]+)/i) || [])[1]
    };
  }
  // Decode RFC 2047 MIME encoded-word subjects (e.g. "=?UTF-8?B?...?=") so non-ASCII subjects
  // render readably and their operator file number can be extracted. Falls back to the raw text.
  function decodeMimeWord(subject){
    let text = String(subject || "");
    if (!/=\?[^?]+\?[BQbq]\?/.test(text)) return text;
    text = text.replace(/\?=\s+=\?/g, "?==?"); // RFC 2047: whitespace between adjacent words is ignored
    try {
      return text.replace(/=\?([^?]+)\?([BbQq])\?([^?]*)\?=/g, (m, _charset, enc, data) => {
        let bytes;
        if (/b/i.test(enc)) {
          const bin = atob(data.replace(/\s+/g, ""));
          bytes = Uint8Array.from(bin, (ch) => ch.charCodeAt(0));
        } else {
          const q = data.replace(/_/g, " ").replace(/=([0-9A-Fa-f]{2})/g, (mm, h) => String.fromCharCode(parseInt(h, 16)));
          bytes = Uint8Array.from(q, (ch) => ch.charCodeAt(0));
        }
        try { return new TextDecoder("utf-8").decode(bytes); } catch (e) { return m; }
      });
    } catch (e) { return String(subject || ""); }
  }
  // The operator file number (a 5-digit "6xxxx" key) is the canonical identity for an LCL file.
  function fileKey(value){ return (String(value || "").toUpperCase().match(/\b(6\d{4})\b/) || [])[1] || ""; }
  // Fold a recent-email case into the shipment it belongs to (dedupe): keep the shipment's tracking
  // state, but fill blanks from the email and surface the email's freshness + operator-facing note.
  function mergeCaseInto(shipEng, caseEng){
    if (shipEng.__caseMerged) return;
    shipEng.__caseMerged = true;
    const better = (cur, alt) => ((!cur || cur === "—" || /pending|tbd|unknown/i.test(String(cur))) && alt && !/pending|tbd|unknown/i.test(String(alt))) ? alt : cur;
    shipEng.customer = better(shipEng.customer, caseEng.customer);
    shipEng.supplier = better(shipEng.supplier, caseEng.supplier);
    shipEng.origin = better(shipEng.origin, caseEng.origin);
    shipEng.destination = better(shipEng.destination, caseEng.destination);
    shipEng.etd = better(shipEng.etd, caseEng.etd);
    shipEng.eta = better(shipEng.eta, caseEng.eta);
    shipEng.vessel = better(shipEng.vessel, caseEng.vessel);
    shipEng.incoterms = better(shipEng.incoterms, caseEng.incoterms);
    shipEng.note = caseEng.note;
    shipEng.noteKind = caseEng.noteKind;
    shipEng.lastUpdate = caseEng.lastUpdate; // the email is the freshest activity signal
    shipEng.liveCase = caseEng.liveCase;
    if (caseEng.risk === "Critical") shipEng.risk = "Critical";
    else if (caseEng.risk === "High" && shipEng.risk === "Normal") shipEng.risk = "High";
    if (caseEng.stale) shipEng.stale = true;
  }
  // Derive real field-completeness rows from an already-resolved engagement (no fabricated dates).
  function computeFields(e){
    const src = e.supplier && !/pending|tbd/i.test(e.supplier) ? `Inbound email — ${e.supplier}` : "Inbound email";
    const at = e.lastUpdate && e.lastUpdate !== "—" ? e.lastUpdate : "—";
    const row = (name, value) => {
      const ok = value && value !== "—" && !/pending|tbd/i.test(String(value));
      return { name, state: ok ? "ok" : "missing", source: ok ? src : "Not yet received", at: ok ? at : "—" };
    };
    return [
      row("Booking / SHO number", e.sho),
      row("Customer", e.customer),
      row("Supplier", e.supplier),
      row("Origin port", e.origin),
      row("Destination port", e.destination),
      row("Incoterms", e.incoterms),
      row("Vessel", e.vessel),
      row("ETD", e.etd),
      row("ETA", e.eta)
    ];
  }
  // Map a free-form stage/milestone string onto the fixed LCL execution track index.
  function stageIndex(stage){
    const s = String(stage || "").toUpperCase();
    if (/DELIV|ARRIV|CUSTOM|FINAL/.test(s)) return 5;
    if (/BOARD|DEPART|ATD|SAIL|VESSEL|TRANSIT/.test(s)) return 4;
    if (/BOOK/.test(s)) return 3;
    if (/PICKUP|PICK|DOC/.test(s)) return 2;
    if (/SUPPLY/.test(s)) return 1;
    return 0; // REO / intake / new shipment
  }
  // Real execution-progress steps. Only the current step carries the (real) last-update date; others stay "—".
  function computeSteps(e){
    const names = ["REO", "Supply Date", "PICKUP", "BOOKED", "ON BOARD", "ATD"];
    const idx = stageIndex(e.stage);
    const blocked = e.stageStatus === "blocked" || e.stale;
    // Only show a date where we have real milestone evidence. The departure date (ETD/ATD) maps to
    // ON BOARD; other steps stay "—" rather than showing the last-email date as if it were the
    // milestone date (which made "ON BOARD" disagree with the header ETD).
    const etd = e.etd && e.etd !== "—" ? e.etd : "";
    return names.map((name, i) => ({
      name,
      date: (i === 4 && etd) ? etd : "—",
      state: i < idx ? "done" : i === idx ? (blocked ? "blocked" : "current") : ""
    }));
  }
  function caseToEngagement(item, i){
    const extraction = item.extraction || {};
    const shipment = extraction.shipment || {};
    const subject = decodeMimeWord(item.subject);
    const parsed = parseSubject(subject);
    const fileRef = fileKey(subject);
    const supplier = first(shipment.supplierName, parsed.supplier, "Supplier pending");
    const unlinked = !item.shipmentId;
    // Out-of-scope / irrelevant mail is stored but never a review item — it must not show
    // as a Critical "Unmatched email" on the board (that drowned the genuine work).
    const isOther = item.classification === "other";
    const needsReview = !isOther && (unlinked || item.escalationFlag || item.humanReviewStatus === "pending_review");
    const customer = first(shipment.consigneeName, shipment.customerName, parsed.consignee, isOther ? "Out of scope" : unlinked ? "Unmatched email" : "Customer pending");
    const overdue = item.followUp && ["overdue", "escalated"].includes(item.followUp.status);
    const isDoc = item.classification === "document_issue";
    return {
      // Show the operator SHO / file number (6xxxx) as the primary identifier, not the LCL number.
      sho: first(fileRef, shipment.bookingNumber, item.bookingNumberRef, item.hblNumberRef, item.poNumberRef, item.caseId ? `Case-${String(item.caseId).slice(0, 8)}` : "", `EMAIL-${i + 1}`),
      fileRef: fileRef || null,
      customer,
      agent: first(shipment.agentName, item.followUp?.ownerParty, "Operations"),
      origin: first(shipment.originPort, "—"),
      destination: first(shipment.destinationPort, "—"),
      vessel: first(shipment.vesselName, shipment.vessel, "TBD"),
      etd: first(shipment.etd, "—"),
      eta: first(shipment.eta, "—"),
      stage: isOther ? "IGNORED" : unlinked ? "REVIEW" : item.classification === "new_shipment" ? "REO" : isDoc ? "PICKUP" : "BOOKED",
      stageStatus: overdue || needsReview ? "blocked" : "current",
      risk: overdue || needsReview ? "Critical" : isDoc ? "High" : "Normal",
      automation: item.followUp || needsReview ? "Draft & ask" : "Observe",
      incoterms: first(shipment.incoterm, "—"),
      manager: first(item.followUp?.ownerParty, item.followUp?.expectedFrom, "Operations"),
      manager_email: "ops@totalcargo.co.il",
      supplier,
      supplier_email: first(shipment.supplierEmail, item.fromAddress, ""),
      lastUpdate: pretty(item.receivedAt || item.createdAt),
      stale: !!overdue,
      note: first(item.actionRequiredNow, item.essenceSummary, item.summary, subject, "Review latest email"),
      noteKind: overdue || needsReview ? "danger" : isDoc ? "warn" : "info",
      liveCase: item
    };
  }
  function shipmentToEngagement(item, i){
    const overdue = item.currentFollowUpStatus === "overdue" || item.current_follow_up_status === "overdue";
    return {
      // SHO / operator file number (6xxxx) first; fall back to booking/LCL number only if absent.
      sho: first(item.fileRef, item.bookingNumber, item.hblNumber, item.shipmentNumber, `SHIP-${i + 1}`),
      fileRef: item.fileRef || null,
      shipmentNumber: item.shipmentNumber || null,
      customer: first(item.consigneeName, item.customerName, "Customer pending"),
      agent: first(item.agentName, "Operations"),
      origin: first(item.originPort, "—"),
      destination: first(item.destinationPort, "—"),
      vessel: first(item.vesselName, "TBD"),
      etd: first(item.etd, "—"),
      eta: first(item.eta, "—"),
      stage: goldaStageLabel(first(item.milestoneCode, item.status, "REO")),
      stageStatus: overdue ? "blocked" : "current",
      risk: overdue ? "Critical" : "Normal",
      automation: "Draft & ask",
      incoterms: first(item.metadata?.incoterm, "—"),
      manager: first(item.followUpOwnerParty, item.agentName, "Operations"),
      manager_email: "ops@totalcargo.co.il",
      supplier: first(item.supplierName, "Supplier pending"),
      supplier_email: first(item.supplierEmail, ""),
      lastUpdate: pretty(item.updatedAt || item.createdAt),
      stale: !!overdue,
      note: first(item.followUpSummary, item.followUpNextExpectedUpdate, "Shipment is active in the control tower."),
      noteKind: overdue ? "danger" : "ok",
      liveShipment: item
    };
  }
  function buildLiveData(payload){
    const cases = Array.isArray(payload?.recentCases) ? payload.recentCases : [];
    const shipments = Array.isArray(payload?.publicShipments) ? payload.publicShipments : [];

    // Tracked shipments are the canonical engagements; recent email cases are folded into the
    // shipment they belong to — by the backend shipmentId link (primary) or a shared operator
    // file number (secondary) — so the same physical shipment is never listed twice.
    const shipmentEngagements = shipments.map(shipmentToEngagement);
    const byShipmentId = new Map();
    const byFileNum = new Map();
    shipments.forEach((s, idx) => {
      const e = shipmentEngagements[idx];
      if (s.id) byShipmentId.set(s.id, e);
      const fk = fileKey(e.sho);
      if (fk && !byFileNum.has(fk)) byFileNum.set(fk, e);
    });
    const unique = [...shipmentEngagements];
    cases.forEach((item, idx) => {
      const ce = caseToEngagement(item, idx);
      const linked = (item.shipmentId && byShipmentId.get(item.shipmentId)) || byFileNum.get(fileKey(ce.sho));
      if (linked) { mergeCaseInto(linked, ce); return; } // dedupe: enrich the shipment, don't duplicate it
      unique.push(ce);
    });
    if (!unique.length) return originalData;
    for (const e of unique) {
      e.fields = computeFields(e);   // real per-shipment field completeness (detail screen)
      e.steps = computeSteps(e);     // real execution-progress track (detail screen)
    }
    const critical = unique.filter((e) => ["Critical", "High"].includes(e.risk)).slice(0, 4);
    return {
      ...originalData,
      engagements: unique,
      quickReplies: critical.map((e, i) => {
        const raw = e.liveCase || e.liveShipment || {};
        const noAction = /no change|required right now|no immediate|no action/i.test(e.note || "");
        const recommendation = noAction ? "No operational change is needed right now. Keep monitoring this shipment." : e.note;
        return {
          id: `live-qr-${i}`,
          kind: e.risk === "Critical" ? "urgent" : "warn",
          sho: e.sho,
          customer: e.customer,
          caseId: raw.caseId || raw.id || null,
          emailId: raw.id || null,
          title: e.risk === "Critical" ? `Action required — ${e.stage}` : "Review suggested follow-up",
          summary: first(raw.essenceSummary, raw.summary, raw.summaryText, raw.subject, `${e.customer} email about ${e.stage}`),
          recommendation,
          confirmLabel: goldaActionLabel({ stage: e.stage, recommendation }),
          confirmImpact: noAction ? "Golda will acknowledge this card as reviewed and will not send an email or change shipment data." : "Golda will record your confirmation and continue the recommended follow-up path.",
          sourceUrl: raw.id ? `/api/emails/view?caseId=${encodeURIComponent(raw.caseId || raw.id)}` : null,
          body: recommendation,
          options: [
            { label: noAction ? "Mark no action needed" : goldaActionLabel({ stage: e.stage, recommendation }), primary: true },
            { label: "Open source email", action: "open", primary: false },
            { label: "I'll write next action", action: "write", primary: false }
          ]
        };
      }),
      sho_emails: cases.slice(0, 8).map((item, i) => ({
        id: item.id || `live-email-${i}`,
        dir: item.fromAddress && item.fromAddress.toLowerCase().includes("totalcargo") ? "internal" : "vendor",
        from: first(item.fromAddress, "Inbound email"),
        time: pretty(item.receivedAt || item.createdAt),
        subject: first(decodeMimeWord(item.subject), "LCL update"),
        extract: [["classification", item.classification || "—"], ["action", first(item.actionRequiredNow, item.essenceSummary, "Review")]],
        action: first(item.nextActionAfterThat, "Continue monitoring")
      })),
      // Decision Review cards are built ONLY from real engagements that have email activity —
      // never padded with the mock decisions (which were showing fictional shipments like
      // "SHO-24803 / Magal Security"). Evidence/guards are empty (the monitoring-only backend
      // does not produce drafts or guard checks); the screen hides those sections in live mode.
      decisions: unique.filter((e) => e.liveCase).slice(0, 12).map((e, i) => {
        const raw = e.liveCase || e.liveShipment || {};
        // Severity-aware recommendation: a Critical/High card must never read "no action".
        // Overdue -> name who to chase and for what; other escalations keep the backend's
        // specific guidance (e.note); only genuine Normal no-ops show the monitoring line.
        const escalated = e.risk === "Critical" || e.risk === "High";
        const fu = (e.liveCase && e.liveCase.followUp) || (e.liveShipment && e.liveShipment.followUp) || {};
        const noAction = !escalated && /no change|required right now|no immediate|no action/i.test(e.note || "");
        const recommendation = escalated
          ? (e.stale
              ? `Follow-up is overdue — chase ${first(fu.ownerParty, fu.expectedFrom, e.manager, "the responsible party")} for ${first(fu.expectedAction, "the next expected update")}.`
              : first(e.note, "Review this escalation and confirm the next operational step."))
          : (noAction ? "No operational change is needed right now. Keep monitoring this shipment." : e.note);
        // Real extraction confidence from the matched shipment; null (chip hidden) when unknown —
        // never the fabricated 0.85 that made every card read an identical, wrong 85%.
        const confidence = (e.liveShipment && typeof e.liveShipment.confidence === "number") ? e.liveShipment.confidence
          : (typeof raw.confidence === "number" ? raw.confidence : null);
        return {
          id: `live-dec-${i}`,
          kind: e.automation || "Status update",
          confidence,
          sho: e.sho,
          customer: e.customer,
          caseId: raw.caseId || raw.id || null,
          emailId: raw.id || null,
          risk: e.risk,
          summary: first(raw.essenceSummary, raw.summary, raw.summaryText, raw.subject, `${e.customer} — ${e.stage}`),
          recommendation,
          confirmLabel: goldaActionLabel({ stage: e.stage, recommendation }),
          confirmImpact: noAction ? "Golda will mark this reviewed with no outbound action." : "Golda will record this operator decision and continue monitoring.",
          sourceUrl: raw.caseId ? `/api/emails/view?caseId=${encodeURIComponent(raw.caseId)}` : `/api/emails/view?ref=${encodeURIComponent(e.fileRef || e.sho)}`,
          question: recommendation,
          body: recommendation,
          evidence: [],
          guards: []
        };
      }),
      domains: originalData.domains,
      __live: payload
    };
  }
  window.GoldaLoadLiveData = async function GoldaLoadLiveData(){
    try {
      const response = await fetch('/api/public/evaluation', { cache: 'no-store' });
      if (!response.ok) throw new Error(`public evaluation ${response.status}`);
      const payload = await response.json();
      window.GoldaData = buildLiveData(payload);
      window.dispatchEvent(new CustomEvent('golda-live-data-ready'));
    } catch (error) {
      console.warn('Golda live data load failed; showing empty state (no mock fallback)', error);
      // Do NOT fall back to the fictional design mock in production — show an explicit empty/error
      // state so operators never mistake demo data for real shipments (issue #119).
      window.GoldaData = { ...(window.GoldaData || {}), __live: true, __loadError: true, engagements: [], decisions: [], quickReplies: [] };
      window.dispatchEvent(new CustomEvent('golda-live-data-ready'));
    }
  };
})();

ReactDOM.createRoot(document.getElementById("root")).render(<App/>);
