/* App root — shell (rail + topbar), case workspace router, routing state. */

const NAV = [
  { key: "dashboard", label: "Dossiers", icon: "dashboard", count: 9 },
  { key: "review", label: "Review center", icon: "inbox", count: 5, alert: true },
  { key: "bronnen", label: "Bronnen & regels", icon: "database" },
  { key: "templates", label: "Templates", icon: "layers" },
];

function Rail({ nav, onNav }) {
  return (
    <div className="rail">
      <div className="rail-brand">
        <Logo size={32} />
        <div className="wm"><b>VVE Finance</b><span>Financieringsdossier</span></div>
      </div>
      <div className="rail-section">Werkruimte</div>
      <div className="rail-nav">
        {NAV.map((n) => (
          <button key={n.key} className={"rail-item" + (nav === n.key ? " active" : "")} onClick={() => onNav(n.key)}>
            <Icon name={n.icon} size={18} />{n.label}
            {n.count != null && <span className={"count" + (n.alert ? " alert" : "")}>{n.count}</span>}
          </button>
        ))}
      </div>
      <div className="rail-section">Beheer</div>
      <div className="rail-nav">
        <button className="rail-item" onClick={() => onNav("settings")}><Icon name="shield" size={18} />Audit & compliance</button>
        <button className="rail-item" onClick={() => onNav("settings")}><Icon name="settings" size={18} />Instellingen</button>
      </div>
      <div className="rail-spacer"></div>
      <div className="rail-user">
        <div className="avatar photo"><img src="assets/jeroen-koster.png" alt="Jeroen Koster" /></div>
        <div className="who"><b>Jeroen Koster</b><span>Financieel adviseur</span></div>
      </div>
    </div>
  );
}

function MobileNav({ nav, onNav }) {
  const items = [
    ...NAV,
    { key: "settings", label: "Audit", icon: "shield" },
    { key: "settings", label: "Instellingen", icon: "settings" },
  ];
  return (
    <div className="mobile-nav" aria-label="Hoofdnavigatie">
      {items.map((n, index) => (
        <button key={n.key + "-" + index} className={"mobile-nav-item" + (nav === n.key ? " active" : "")} onClick={() => onNav(n.key)}>
          <Icon name={n.icon} size={15} />
          <span>{n.label}</span>
          {n.count != null && <span className={"count" + (n.alert ? " alert" : "")}>{n.count}</span>}
        </button>
      ))}
    </div>
  );
}

function Topbar({ crumb, search, onSearch, onBell, storageStatus, backendStatus }) {
  return (
    <div className="topbar">
      <div className="crumb">{crumb}</div>
      <div className="topbar-spacer"></div>
      <button className="demo-pill" onClick={() => window.openIntro && window.openIntro()}>
        <span className="dp-dot"></span>Demo · voorbeelddata
      </button>
      <div className="storage-pill" title="Lokale browseropslag voor uploads en extracties">
        <Icon name="database" size={13} />Opslag · {storageStatus || "actief"}
      </div>
      <div className="storage-pill backend" title="Cloudflare KV synchronisatie voor case metadata, extracties en approvals. Bronbestanden blijven lokaal totdat R2 als VVE_FILES is gekoppeld.">
        <Icon name="cloud" size={13} />Backend · {backendStatus || "actief"}
      </div>
      <div className="search">
        <Icon name="search" size={15} />
        <input placeholder="Zoek VvE, case-ID of gemeente…" value={search} onChange={(e) => onSearch(e.target.value)} />
      </div>
      <button className="icon-btn" onClick={onBell} aria-label="Meldingen"><Icon name="bell" size={17} /></button>
    </div>
  );
}

function DocumentPreview({ preview }) {
  if (!preview) return null;
  if (preview.kind === "pdf" && preview.url) {
    return (
      <div className="doc-preview">
        <iframe className="preview-frame" src={preview.url} title="PDF preview"></iframe>
      </div>
    );
  }
  if (preview.kind === "sheet") {
    const sheet = (preview.sheets || [])[0] || { name: "Sheet", rows: [] };
    return (
      <div className="doc-preview">
        <div className="preview-label">{sheet.name} · eerste rijen</div>
        <div className="sheet-wrap">
          <table className="sheet-preview">
            <tbody>
              {sheet.rows.map((row, i) => (
                <tr key={i}>
                  {row.map((cell, j) => <td key={j}>{cell || " "}</td>)}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    );
  }
  if (preview.kind === "text") {
    return (
      <div className="doc-preview">
        <pre className="text-preview">{preview.text || "Geen tekst gevonden."}</pre>
      </div>
    );
  }
  return null;
}

function ExtractedFieldsPreview({ fields }) {
  if (!fields || !fields.length) return null;
  return (
    <div className="field-preview">
      <div className="preview-label">Geextraheerde velden</div>
      {fields.map(([k, v, c]) => (
        <div className="exrow" key={k}>
          <span className="ex-k">{k}</span>
          <span className="ex-v">{v}<Conf value={c} /></span>
        </div>
      ))}
    </div>
  );
}

function NoticeModal({ data, onClose }) {
  return (
    <div className="scrim" onMouseDown={(e) => e.target === e.currentTarget && onClose()}>
      <div className={"modal" + (data.preview || data.wide ? " wide" : "")}>
        <div className="mhead">
          <div>
            <div className="mh-eyebrow">{data.eyebrow || "Actie"}</div>
            <h2>{data.title}</h2>
          </div>
          <button className="mclose" onClick={onClose}><Icon name="xCircle" size={18} /></button>
        </div>
        <div className="mbody">
          {data.body && <p className="page-sub" style={{ margin: "0 0 16px" }}>{data.body}</p>}
          {data.items && (
            <div className="initlist">
              {data.items.map((item) => (
                <div className="initrow" key={item.title || item}>
                  <div className="ir-ic"><Icon name={item.icon || "checkCircle"} size={16} /></div>
                  <div><div className="ir-t">{item.title || item}</div>{item.sub && <div className="ir-s">{item.sub}</div>}</div>
                </div>
              ))}
            </div>
          )}
          <ExtractedFieldsPreview fields={data.fields} />
          <DocumentPreview preview={data.preview} />
        </div>
        <div className="mfoot">
          <span className="tiny muted">{data.foot || "Demo-actie uitgevoerd"}</span>
          <button className="btn primary" onClick={onClose}>Sluiten</button>
        </div>
      </div>
    </div>
  );
}

function NotificationsModal({ onClose, onOpenCase, onNav }) {
  const notifications = [
    { title: "Singelhof pakket klaar", sub: "Warmtefonds handmatige indiening kan worden voorbereid.", icon: "package", action: () => onOpenCase("C-2026-099") },
    { title: "ALV-besluit Parkzicht ontbreekt", sub: "Gate G7 blokkeert export tot besluitvorming is gekoppeld.", icon: "flag", action: () => onOpenCase("C-2026-020") },
    { title: "Broncheck Warmtefonds uitgevoerd", sub: "Voorwaarden, rente en downloads zijn vandaag gecontroleerd.", icon: "database", action: () => onNav("bronnen") },
  ];
  return (
    <div className="scrim" onMouseDown={(e) => e.target === e.currentTarget && onClose()}>
      <div className="modal">
        <div className="mhead">
          <div><div className="mh-eyebrow">Meldingen</div><h2>Acties voor Jeroen</h2></div>
          <button className="mclose" onClick={onClose}><Icon name="xCircle" size={18} /></button>
        </div>
        <div className="mbody">
          <div className="initlist">
            {notifications.map((n) => (
              <div className="initrow click" key={n.title} onClick={() => { onClose(); n.action(); }}>
                <div className="ir-ic"><Icon name={n.icon} size={16} /></div>
                <div><div className="ir-t">{n.title}</div><div className="ir-s">{n.sub}</div></div>
              </div>
            ))}
          </div>
        </div>
        <div className="mfoot">
          <span className="tiny muted">Klik op een dossiermelding om direct te openen</span>
          <button className="btn primary" onClick={onClose}>Gelezen</button>
        </div>
      </div>
    </div>
  );
}

function PortfolioReviewPage({ onOpenCase }) {
  const rows = window.VVE.CASES.filter((c) => c.reviews > 0 || c.blockers > 0).map((c) => ({
    ...c,
    top: c.id === "C-2026-099" ? "Singelhof: controleer exportpakket en privacygate" : c.vve + ": open reviewpunten in dossier",
  }));
  return (
    <div className="page wide">
      <div className="page-head">
        <div><div className="eyebrow">Review center</div><h1 className="page-title">Alle open controles</h1>
          <p className="page-sub">Portefeuillebreed overzicht van blockers, lage-confidence extracties en handmatige gates. Elke rij opent het betreffende dossier.</p></div>
      </div>
      <div className="card">
        {rows.map((r) => (
          <div className="lrow click" key={r.id} onClick={() => onOpenCase(r.id)}>
            <div className={"sdisc " + (r.blockers ? "err" : "warn")}><Icon name={r.blockers ? "alert" : "flag"} size={16} /></div>
          <div className="lr-main">
              <div className="lr-title">{r.top}<Badge tone={r.blockers ? "err" : "warn"} sm>{r.blockers} blocker · {r.reviews} review</Badge></div>
              <div className="metaline"><span>{r.vve}</span><span>{r.gemeente}</span><span className="mono">{r.id}</span></div>
            </div>
            <button className="btn ghost sm" onClick={(e) => { e.stopPropagation(); onOpenCase(r.id); }}>Open dossier<Icon name="arrowRight" size={14} /></button>
          </div>
        ))}
      </div>
    </div>
  );
}

function TemplatesPage() {
  const templates = [
    ["ALV-agenda Warmtefonds", "Besluitvorming, lening, maatregelen en servicekostenimpact", "legal"],
    ["Ledenbrief financieringsaanvraag", "Uitlegbaar voor eigenaars, met concept-waarschuwingen", "concept"],
    ["Indieningsmemo Warmtefonds", "Route, bronregels, extracties en resterende handmatige controles", "ready"],
    ["Documentverzoek VvE/beheerder", "Checklist per route, uploadlink en deadline", "ready"],
  ];
  return (
    <div className="page wide">
      <div className="page-head">
        <div><div className="eyebrow">Templates</div><h1 className="page-title">Documenttemplates</h1>
          <p className="page-sub">Templates zijn klikbaar: review toont wat het systeem straks genereert en welke menselijke controle nog nodig is.</p></div>
      </div>
      <div className="grid-2 even">
        {templates.map(([title, sub, status]) => (
          <div className="card" key={title}>
            <div className="card-head"><h3><Icon name={status === "legal" ? "shield" : "fileText"} size={16} />{title}</h3><Badge tone={status === "legal" ? "warn" : "ok"} sm>{status === "legal" ? "review" : "bruikbaar"}</Badge></div>
            <div className="card-body">
              <p className="muted" style={{ marginTop: 0 }}>{sub}</p>
              <button className="btn outline sm" onClick={() => window.openNotice && window.openNotice({
                eyebrow: "Template review",
                title,
                body: "Template opent als preview in de demo. In de MVP wordt dit een bewerkbare generator met bronverwijzingen en goedkeuringslog.",
                items: [
                  { icon: "fileText", title: "Conceptinhoud", sub },
                  { icon: "shield", title: "Menselijke check", sub: status === "legal" ? "Juridische/financiële review verplicht" : "Adviseur kan tekst accepteren of aanpassen" },
                ],
              })}><Icon name="eye" size={14} />Review</button>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function SettingsPage() {
  const [auditOn, setAuditOn] = React.useState(true);
  const [privacyOn, setPrivacyOn] = React.useState(true);
  return (
    <div className="page wide">
      <div className="page-head">
        <div><div className="eyebrow">Beheer</div><h1 className="page-title">Audit & compliance</h1>
          <p className="page-sub">Demo-instellingen voor auditlog, privacygate en bronverversing. Elke wijziging wordt zichtbaar vastgelegd.</p></div>
      </div>
      <div className="grid-2">
        <div className="card">
          <div className="card-head"><h3><Icon name="shield" size={16} />Controle-instellingen</h3></div>
          <div className="doc">
            <div className="dn"><b>Append-only auditlog</b><div className="df">Alle bronchecks, extracties en menselijke besluiten blijven bewaard</div></div>
            <button className={"btn sm " + (auditOn ? "primary" : "outline")} onClick={() => setAuditOn(!auditOn)}>{auditOn ? "Actief" : "Uit"}</button>
          </div>
          <div className="doc">
            <div className="dn"><b>Privacygate voor export</b><div className="df">Export vereist akkoord voordat het pakket mag worden gegenereerd</div></div>
            <button className={"btn sm " + (privacyOn ? "primary" : "outline")} onClick={() => setPrivacyOn(!privacyOn)}>{privacyOn ? "Actief" : "Uit"}</button>
          </div>
        </div>
        <div className="card">
          <div className="card-head"><h3><Icon name="history" size={16} />Beheerlog</h3></div>
          <div className="card-body audit">
            <div className="ai"><span className="adot em"></span><div><div className="atxt">Auditlog {auditOn ? "actief" : "uitgeschakeld in demo"}</div><div className="atime">deze sessie</div></div></div>
            <div className="ai"><span className="adot em"></span><div><div className="atxt">Privacygate {privacyOn ? "actief" : "uitgeschakeld in demo"}</div><div className="atime">deze sessie</div></div></div>
            <div className="ai"><span className="adot"></span><div><div className="atxt">Cloudflare Pages demo draait op vve-finance.pages.dev</div><div className="atime">deployment actief</div></div></div>
          </div>
        </div>
      </div>
    </div>
  );
}

function SourcesAndRules() {
  const { WARMTEFONDS_SOURCES, WARMTEFONDS_RULES, WARMTEFONDS_DOCUMENT_OUTPUTS } = window.VVE;
  const [refreshing, setRefreshing] = React.useState(false);
  const [approving, setApproving] = React.useState(false);
  const [refreshData, setRefreshData] = React.useState(null);
  const [approvalData, setApprovalData] = React.useState(null);
  React.useEffect(() => {
    let live = true;
    if (window.VVE_STORAGE) {
      window.VVE_STORAGE.loadSourceRefresh().then((data) => {
        if (live && data) setRefreshData(data);
      }).catch(() => {});
    }
    fetch("/api/sources/approval", { cache: "no-store" })
      .then((response) => response.ok ? response.json() : null)
      .then((data) => { if (live && data) setApprovalData(data); })
      .catch(() => {});
    return () => { live = false; };
  }, []);
  const sourceRows = refreshData && Array.isArray(refreshData.results) ? refreshData.results : WARMTEFONDS_SOURCES;
  const downloadRows = refreshData && Array.isArray(refreshData.downloads) ? refreshData.downloads : [];
  const ruleRows = refreshData && Array.isArray(refreshData.rules) ? refreshData.rules : WARMTEFONDS_RULES;
  const diffRows = refreshData && Array.isArray(refreshData.semanticDiffs) ? refreshData.semanticDiffs : [];
  const currentApproval = (refreshData && refreshData.approval) || (approvalData && approvalData.current) || null;
  const approvalStatus = (refreshData && refreshData.approvalStatus) || (approvalData && approvalData.approvalStatus) || "needs_approval";
  const approved = sourceRows.filter((s) => s.status === "approved").length;
  const checkedLabel = refreshData && refreshData.checkedAt
    ? new Date(refreshData.checkedAt).toLocaleString("nl-NL", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })
    : "2026-06-03";
  const refreshSources = async () => {
    if (refreshing) return;
    setRefreshing(true);
    try {
      const response = await fetch("/api/sources/refresh", { cache: "no-store" });
      if (!response.ok) throw new Error("Source refresh API gaf HTTP " + response.status);
      const data = await response.json();
      setRefreshData(data);
      setApprovalData((prev) => data.approval ? { ...(prev || {}), current: data.approval, approvalStatus: data.approvalStatus } : prev);
      if (window.VVE_STORAGE) window.VVE_STORAGE.saveSourceRefresh(data).catch(() => {});
    } catch (error) {
      setRefreshData({
        ok: false,
        checkedAt: new Date().toISOString(),
        error: error.message || "Bronnen verversen mislukt",
        results: WARMTEFONDS_SOURCES.map((s) => ({ ...s, status: "review", freshness: "stale", error: error.message || "API niet beschikbaar" })),
      });
    } finally {
      setRefreshing(false);
    }
  };
  const approveSources = async () => {
    if (approving || !refreshData) return;
    setApproving(true);
    try {
      const response = await fetch("/api/sources/approval", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ approvedBy: "Jeroen Koster", note: "Bronversie akkoord voor Warmtefonds demo-handoff." }),
      });
      if (!response.ok) throw new Error("Bronversie goedkeuren gaf HTTP " + response.status);
      const data = await response.json();
      setApprovalData(data);
      const next = { ...refreshData, approvalStatus: "approved", approval: data.current };
      setRefreshData(next);
      if (window.VVE_STORAGE) window.VVE_STORAGE.saveSourceRefresh(next).catch(() => {});
    } catch (error) {
      window.openNotice && window.openNotice({
        eyebrow: "Bronnen",
        title: "Bronversie goedkeuren mislukt",
        body: error.message || "Controleer de source approval API.",
      });
    } finally {
      setApproving(false);
    }
  };
  return (
    <div className="page wide">
      <div className="page-head">
        <div>
          <div className="eyebrow">SourceRegistry</div>
          <h1 className="page-title">Bronnen & regels</h1>
          <p className="page-sub">Warmtefonds is hier zowel bronpakket als financieringsroute. Regels worden versieerbaar opgeslagen, dagelijks of wekelijks opnieuw gecontroleerd en pas gebruikt nadat Jeroen de rule-versie accepteert.</p>
        </div>
        <button className="btn outline sm" disabled={refreshing} onClick={refreshSources}>
          {refreshing ? <span className="spin"></span> : <Icon name="refresh" size={15} />}{refreshing ? "Bronnen checken..." : "Bronnen verversen"}
        </button>
      </div>

      <div className="grid-2">
        <div className="stack">
          <div className="banner ink">
            <Icon name="database" size={19} style={{ flex: "0 0 auto", marginTop: 1, color: "var(--tge-emerald)" }} />
            <div>
              <div className="bt">warmtefonds.vve_energiebespaarlening · laatste check {checkedLabel}</div>
              <div className="bd">De tool controleert de officiele Warmtefonds bron-URL's via de Pages API, hasht formulierdownloads en vergelijkt de semantische rule snapshot. AI mag samenvatten en documenten voorbereiden; Jeroen blijft eigenaar van interpretatie, rentecontrole en indiening.</div>
              {refreshData && <div className="metaline" style={{ marginTop: 8 }}>
                <span><Icon name="history" size={12} />registry v{refreshData.registryVersion || 1}</span>
                <span><Icon name="fileText" size={12} />{downloadRows.length} downloads</span>
                <span><Icon name="shield" size={12} />{ruleRows.length} rules</span>
                <span><Icon name={approvalStatus === "approved" ? "check" : "flag"} size={12} />{approvalStatus === "approved" ? "goedgekeurd" : "approval nodig"}</span>
              </div>}
              {refreshData && refreshData.error && <div className="tiny" style={{ marginTop: 8, color: "var(--st-warn-fg)" }}>Laatste broncheck kon niet volledig worden uitgevoerd: {refreshData.error}</div>}
            </div>
          </div>

          <div className="card">
            <div className="card-head">
              <h3><Icon name="link" size={16} />Warmtefonds bronnen</h3>
              <span className="meta">{approved}/{sourceRows.length} approved · laatste check {checkedLabel}</span>
            </div>
            {sourceRows.map((s) => (
              <div className="lrow" key={s.url}>
                <div className={"sdisc " + (s.freshness === "ok" ? "ok" : "warn")}>
                  <Icon name={s.type === "Rente" ? "coins" : s.type === "Formulieren" ? "fileText" : "shield"} size={16} />
                </div>
                <div className="lr-main">
                  <div className="lr-title">{s.title}<Badge tone={s.freshness === "ok" ? "ok" : "warn"} sm dot={s.freshness === "ok"}>{s.status}</Badge></div>
                  <div className="metaline">
                    <span><Icon name="database" size={12} />{s.type}</span>
                    <span><Icon name="calendar" size={12} />gecheckt {s.checkedAt ? new Date(s.checkedAt).toLocaleString("nl-NL", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }) : s.checked}</span>
                    <span><Icon name="refresh" size={12} />{s.cadence}</span>
                    {s.version && <span><Icon name="history" size={12} />versie {s.version}{s.changed ? " · gewijzigd" : ""}</span>}
                    {s.httpStatus && <span><Icon name="checkCircle" size={12} />HTTP {s.httpStatus}</span>}
                    {s.error && <span style={{ color: "var(--st-warn-fg)" }}><Icon name="flag" size={12} />{s.error}</span>}
                  </div>
                  {s.pageTitle && <div className="lr-sub">{s.pageTitle}</div>}
                  <a className="source-url" href={s.url} target="_blank" rel="noreferrer">{s.url}</a>
                </div>
              </div>
            ))}
          </div>

          <div className="card">
            <div className="card-head">
              <h3><Icon name="fileText" size={16} />Formulieren & downloads</h3>
              <span className="meta">{downloadRows.length ? `${downloadRows.filter((d) => d.freshness === "ok").length}/${downloadRows.length} gehasht` : "ververs bronnen"}</span>
            </div>
            {downloadRows.length ? downloadRows.map((d) => (
              <div className="lrow" key={d.url}>
                <div className={"sdisc " + (d.freshness === "ok" ? "ok" : "warn")}>
                  <Icon name={d.freshness === "ok" ? "fileCheck" : "flag"} size={16} />
                </div>
                <div className="lr-main">
                  <div className="lr-title">{d.title}<Badge tone={d.freshness === "ok" ? "ok" : "warn"} sm>{d.status || "review"}</Badge></div>
                  <div className="metaline">
                    <span><Icon name="history" size={12} />versie {d.version || 1}{d.changed ? " · gewijzigd" : ""}</span>
                    {d.httpStatus && <span><Icon name="checkCircle" size={12} />HTTP {d.httpStatus}</span>}
                    {d.contentType && <span><Icon name="fileText" size={12} />{d.contentType.split(";")[0]}</span>}
                    {d.contentHash && <span className="mono">hash {String(d.contentHash).slice(0, 10)}</span>}
                    {d.error && <span style={{ color: "var(--st-warn-fg)" }}><Icon name="flag" size={12} />{d.error}</span>}
                  </div>
                  <a className="source-url" href={d.url} target="_blank" rel="noreferrer">{d.url}</a>
                </div>
              </div>
            )) : (
              <div className="card-body muted">Klik `Bronnen verversen` om Warmtefonds downloads en formulierhashes op te halen.</div>
            )}
          </div>
        </div>

        <div className="stack">
          <div className={"banner " + (approvalStatus === "approved" ? "info" : "warn")}>
            <Icon name={approvalStatus === "approved" ? "checkCircle" : "flag"} size={18} style={{ flex: "0 0 auto", marginTop: 1 }} />
            <div>
              <div className="bt">{approvalStatus === "approved" ? "Bronversie goedgekeurd" : "Bronversie wacht op Jeroen"}</div>
              <div className="bd">{currentApproval ? `Registry v${currentApproval.registryVersion} goedgekeurd door ${currentApproval.approvedBy} op ${new Date(currentApproval.approvedAt).toLocaleString("nl-NL", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })}.` : "Ververs de bronnen en accepteer daarna de rule-versie voordat deze als vertrouwde basis voor het pakket geldt."}</div>
              {refreshData && <button className={"btn sm " + (approvalStatus === "approved" ? "outline" : "primary")} style={{ marginTop: 10 }} disabled={approving || !refreshData} onClick={approveSources}>
                {approving ? <span className="spin"></span> : <Icon name="check" size={14} />}{approvalStatus === "approved" ? "Herbevestig bronversie" : "Accepteer bronversie"}
              </button>}
            </div>
          </div>

          <div className="card">
            <div className="card-head"><h3><Icon name="shield" size={16} />Warmtefonds rule snapshot</h3><Badge tone="warn" sm>human approval</Badge></div>
            {ruleRows.map((r) => (
              <div className="doc" key={r.label}>
                <div className={"sdisc " + r.tone}><Icon name={r.tone === "ok" ? "check" : r.tone === "warn" ? "flag" : "alert"} size={16} /></div>
                <div className="dn"><b>{r.label}</b><div className="df">{r.value}</div></div>
                <Badge tone={r.tone} sm>{r.tone === "ok" ? "Pass" : r.tone === "warn" ? "Review" : "Blocker"}</Badge>
              </div>
            ))}
          </div>

          <div className="card">
            <div className="card-head"><h3><Icon name="history" size={16} />Semantische wijziginglog</h3><span className="meta">{diffRows.length || 0} regels</span></div>
            {diffRows.length ? diffRows.map((diff, index) => (
              <div className="doc" key={diff.type + index}>
                <div className={"sdisc " + (diff.status === "unchanged" ? "ok" : diff.status === "initial" ? "neutral" : "warn")}>
                  <Icon name={diff.status === "unchanged" ? "check" : "history"} size={16} />
                </div>
                <div className="dn"><b>{diff.title}</b><div className="df">{diff.body}</div></div>
                <Badge tone={diff.status === "unchanged" ? "ok" : "warn"} sm>{diff.status}</Badge>
              </div>
            )) : (
              <div className="card-body muted">Na `Bronnen verversen` toont dit paneel of pagina's, downloads of rule snapshots inhoudelijk gewijzigd zijn.</div>
            )}
          </div>

          <div className="card">
            <div className="card-head"><h3><Icon name="package" size={16} />Documentoutput</h3><span className="meta">system creates</span></div>
            <div className="card-body doc-output-grid">
              {WARMTEFONDS_DOCUMENT_OUTPUTS.map((d) => (
                <div className="doc-output" key={d}><Icon name="fileText" size={14} />{d}</div>
              ))}
            </div>
          </div>

          <div className="banner info">
            <Icon name="history" size={18} style={{ flex: "0 0 auto", marginTop: 1 }} />
            <div>
              <div className="bt">Auditbaar pad naar export</div>
              <div className="bd">Elke bronversie, extractie en menselijke goedkeuring komt in het approval-log. Dat log gaat mee in het Warmtefonds pakket zodat de adviseur kan aantonen waarop de aanvraag is gebaseerd.</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

function CaseWorkspace({ caseId, onBack, uploads }) {
  const { CASES } = window.VVE;
  const c = CASES.find((x) => x.id === caseId);
  if (window.VVE.setActiveCaseData) window.VVE.setActiveCaseData(caseId, uploads || []);
  const [view, setView] = React.useState("overzicht");
  const [stage, setStage] = React.useState(c.stage);

  const goto = (i) => { setStage(i); setView("stage"); window.__scrollTop && window.__scrollTop(); };

  const screens = [
    <IntakeWizard onGoto={goto} c={c} />, <Routekaart />, <Dataroom />, <ReviewCenter />,
    <Scenario />, <AlvPack />, <ExportCenter />, <Afgerond />,
  ];

  return (
    <div className="page wide">
      <CaseHeader c={c} onDataroom={() => goto(2)} onExport={() => goto(6)} />
      <Stepper stage={c.stage} active={stage} view={view}
               onOverview={() => setView("overzicht")} onPick={goto} />
      {view === "overzicht" ? <Overzicht c={c} onGoto={goto} /> : screens[stage]}
    </div>
  );
}

function App() {
  const [route, setRoute] = React.useState({ page: "dashboard" });
  const [modal, setModal] = React.useState(null);
  const [caseUploads, setCaseUploads] = React.useState({});
  const [storageStatus, setStorageStatus] = React.useState("laden");
  const [backendStatus, setBackendStatus] = React.useState("controleren");
  const [search, setSearch] = React.useState("");
  const [intro, setIntro] = React.useState(() => {
    try { return localStorage.getItem("vve_intro_seen") !== "1"; } catch (e) { return true; }
  });
  const scrollRef = React.useRef(null);
  React.useEffect(() => {
    let live = true;
    (async () => {
      if (!window.VVE_STORAGE) {
        setStorageStatus("niet beschikbaar");
        return;
      }
      try {
        const entries = await Promise.all((window.VVE.CASES || []).map(async (c) => [c.id, await window.VVE_STORAGE.loadCaseUploads(c.id)]));
        if (!live) return;
        const restored = {};
        entries.forEach(([id, uploads]) => { if (uploads && uploads.length) restored[id] = uploads; });
        setCaseUploads(restored);
        setStorageStatus(Object.keys(restored).length ? "hersteld" : "actief");
      } catch (error) {
        console.warn("VVE storage load failed", error);
        if (live) setStorageStatus("niet beschikbaar");
      }
    })();
    return () => { live = false; };
  }, []);
  React.useEffect(() => {
    const onRemoteStatus = (event) => {
      if (event && event.detail && event.detail.status) setBackendStatus(event.detail.status);
    };
    window.addEventListener("vve-remote-status", onRemoteStatus);
    if (window.VVE_STORAGE && window.VVE_STORAGE.checkBackend) {
      window.VVE_STORAGE.checkBackend().catch(() => setBackendStatus("niet bereikbaar"));
    }
    return () => window.removeEventListener("vve-remote-status", onRemoteStatus);
  }, []);
  React.useEffect(() => { window.__scrollTop = () => { if (scrollRef.current) scrollRef.current.scrollTop = 0; }; }, []);
  React.useEffect(() => { if (scrollRef.current) scrollRef.current.scrollTop = 0; }, [route]);
  React.useEffect(() => { window.__activeCaseId = route.page === "case" ? route.id : null; }, [route]);
  React.useEffect(() => {
    window.openNewCase = () => setModal({ type: "newcase" });
    window.openUpload = (opts = {}) => setModal({ type: "upload", caseId: window.__activeCaseId, ...opts });
    window.openIntro = () => setIntro(true);
    window.openNotice = (data) => setModal({ type: "notice", data });
  }, []);
  const closeIntro = () => { setIntro(false); try { localStorage.setItem("vve_intro_seen", "1"); } catch (e) {} };

  const openCase = (id) => setRoute({ page: "case", id });
  const goDash = () => setRoute({ page: "dashboard" });
  const onNav = (key) => { if (key === "dashboard") goDash(); else setRoute({ page: key }); };
  const handleSearch = (value) => {
    setSearch(value);
    if (value.trim() && route.page !== "dashboard") setRoute({ page: "dashboard" });
  };

  let content, crumb, navKey = route.page;
  if (route.page === "dashboard") {
    crumb = <span className="here">Dossiers</span>;
    content = <Dashboard onOpen={openCase} search={search} />;
  } else if (route.page === "case") {
    const c = window.VVE.CASES.find((x) => x.id === route.id);
    navKey = "dashboard";
    crumb = (<>
      <button className="link" onClick={goDash}>Dossiers</button>
      <Icon name="chevronRight" size={14} className="sep" />
      <span className="here">{c.vve}</span>
    </>);
    content = <CaseWorkspace caseId={route.id} onBack={goDash} uploads={caseUploads[route.id] || []} />;
  } else if (route.page === "review") {
    crumb = <span className="here">Review center</span>;
    content = <PortfolioReviewPage onOpenCase={openCase} />;
  } else if (route.page === "bronnen") {
    crumb = <span className="here">Bronnen & regels</span>;
    content = <SourcesAndRules />;
  } else if (route.page === "templates") {
    crumb = <span className="here">Templates</span>;
    content = <TemplatesPage />;
  } else {
    crumb = <span className="here">Beheer</span>;
    content = <SettingsPage />;
  }

  return (
    <div className="app">
      <Rail nav={navKey} onNav={onNav} />
      <div className="main">
        <Topbar crumb={crumb} search={search} onSearch={handleSearch} onBell={() => setModal({ type: "notifications" })} storageStatus={storageStatus} backendStatus={backendStatus} />
        <MobileNav nav={navKey} onNav={onNav} />
        <div className="scroll" ref={scrollRef}>{content}</div>
      </div>
      {modal && modal.type === "newcase" &&
        <NewCaseModal onClose={() => setModal(null)}
          onComplete={() => { setModal(null); openCase("C-2026-099"); }} />}
      {modal && modal.type === "upload" &&
        <UploadWizard onClose={() => setModal(null)}
          onLinked={(docs) => {
            const caseId = modal.caseId || window.__activeCaseId || "C-2026-020";
            setCaseUploads((prev) => {
              const nextByFile = new Map((prev[caseId] || []).map((doc) => [doc.file, doc]));
              docs.forEach((doc) => nextByFile.set(doc.file, doc));
              const nextUploads = Array.from(nextByFile.values());
              if (window.VVE_STORAGE) {
                window.VVE_STORAGE.saveCaseUploads(caseId, nextUploads)
                  .then(() => setStorageStatus("opgeslagen"))
                  .catch((error) => {
                    console.warn("VVE storage save failed", error);
                    setStorageStatus("niet beschikbaar");
                  });
                (async () => {
                  await Promise.all([
                    window.VVE_STORAGE.syncUploads(caseId, nextUploads),
                    window.VVE_STORAGE.syncExtractions(caseId, nextUploads),
                  ]);
                  const fileStoreResult = window.VVE_STORAGE.syncSourceFiles
                    ? await window.VVE_STORAGE.syncSourceFiles(caseId, nextUploads)
                    : { fileStore: "not_configured", fileCount: 0 };
                  const extractionJob = window.VVE_STORAGE.startExtractionJob
                    ? await window.VVE_STORAGE.startExtractionJob(caseId, "upload_linked")
                    : null;
                  const legalValidation = window.VVE_STORAGE.syncLegalValidation
                    ? await window.VVE_STORAGE.syncLegalValidation(caseId, nextUploads)
                    : null;
                  await window.VVE_STORAGE.syncCaseState(caseId, {
                    vve: (window.VVE.CASES.find((c) => c.id === caseId) || {}).vve || caseId,
                    syncedAt: new Date().toISOString(),
                    browserSessionOnlyFiles: fileStoreResult.fileStore !== "cloudflare_r2",
                    fileStore: fileStoreResult.fileStore || "not_configured",
                    sourceFileCount: nextUploads.filter((upload) => upload.originalFile).length,
                    durableFileCount: fileStoreResult.fileStore === "cloudflare_r2" ? (fileStoreResult.fileCount || 0) : 0,
                    latestExtractionJobId: extractionJob && extractionJob.job ? extractionJob.job.jobId : null,
                    jobRunner: extractionJob ? extractionJob.jobRunner : null,
                    legalValidationSummary: legalValidation && legalValidation.validation ? legalValidation.validation.summary : null,
                  });
                  if (fileStoreResult.fileStore !== "cloudflare_r2") {
                    setBackendStatus("gesynchroniseerd · files lokaal");
                  }
                })().catch((error) => {
                  console.warn("VVE backend sync failed", error);
                  setBackendStatus("fout");
                });
              }
              return { ...prev, [caseId]: nextUploads };
            });
          }} />}
      {modal && modal.type === "notifications" &&
        <NotificationsModal onClose={() => setModal(null)} onOpenCase={openCase} onNav={onNav} />}
      {modal && modal.type === "notice" &&
        <NoticeModal data={modal.data} onClose={() => setModal(null)} />}
      {intro && <IntroOverlay onClose={closeIntro}
        onTour={() => { closeIntro(); openCase("C-2026-099"); }} />}
    </div>
  );
}

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