/* global React, Icon, window */
// ============================================================================
// MELR · STAR (FEM/GEF) — tableau de bord & simulateur d'allocation
// ----------------------------------------------------------------------------
// Récupère en direct les indicateurs publics de la Banque mondiale (CPIA/IRAI
// + PIB), combine avec les entrées propres au FEM (GBI, PPI, allocation
// officielle), calcule le CPI et le score pays selon la formule FEM-8
// (public/star-calc.jsx) et offre un simulateur de sensibilité.
//
// Honnêteté : moniteur + simulateur fidèle, PAS le moteur officiel du FEM
// (le GBI et la normalisation finale en dollars sont internes au FEM). La
// décomposition du CPI et la sensibilité sont en revanche exactes.
// ============================================================================

(function () {
  const { useState, useEffect, useMemo } = React;

  const sec = (c, extra) => (window.MelrSection ? window.MelrSection.style(c, extra) : { marginBottom: 14, ...(extra || {}) });
  const secT = (c) => (window.MelrSection ? window.MelrSection.title(c) : { fontSize: 13, fontWeight: 700, marginBottom: 8 });

  // Quelques pays bénéficiaires fréquents (ISO3) pour le datalist — saisie
  // libre possible pour tout autre pays.
  const SUGGESTED = [
    { iso3: "SEN", fr: "Sénégal", en: "Senegal" },
    { iso3: "CIV", fr: "Côte d'Ivoire", en: "Côte d'Ivoire" },
    { iso3: "MLI", fr: "Mali", en: "Mali" },
    { iso3: "BFA", fr: "Burkina Faso", en: "Burkina Faso" },
    { iso3: "GIN", fr: "Guinée", en: "Guinea" },
    { iso3: "BEN", fr: "Bénin", en: "Benin" },
    { iso3: "TGO", fr: "Togo", en: "Togo" },
    { iso3: "NER", fr: "Niger", en: "Niger" },
    { iso3: "GHA", fr: "Ghana", en: "Ghana" },
    { iso3: "CMR", fr: "Cameroun", en: "Cameroon" },
  ];

  const fmt = (v, d) => (v == null || Number.isNaN(Number(v)) ? "—" : Number(v).toFixed(d == null ? 2 : d));
  const fmtMoney = (v) => (v == null || v === "" ? "—" : Number(v).toLocaleString("fr-FR") + " $");
  // Montants projets : exposés en MILLIONS de devise native par melr-data.
  const fmtM = (m) => (m == null || Number.isNaN(Number(m)) ? "—" : Number(m).toLocaleString("fr-FR", { maximumFractionDigits: 1 }) + " M");
  // Agrège engagé (budget) / décaissé / taux d'absorption pour une liste
  // d'UUID de projets. budget/disbursed sont déjà en millions natifs.
  function utilFor(uuids, projByUuid) {
    let budget = 0, disbursed = 0, n = 0;
    (uuids || []).forEach((u) => {
      const p = projByUuid[u];
      if (!p) return;
      budget += Number(p.budget) || 0;
      disbursed += Number(p.disbursed) || 0;
      n++;
    });
    return { budget, disbursed, count: n, rate: budget > 0 ? 100 * disbursed / budget : 0 };
  }
  // Utilisation totale tous domaines focaux confondus.
  function totalUtil(linksByFa, projByUuid) {
    const all = [];
    Object.values(linksByFa || {}).forEach((arr) => (arr || []).forEach((u) => { if (!all.includes(u)) all.push(u); }));
    return utilFor(all, projByUuid);
  }

  function StarScreen({ lang, effectiveOrgId, isSuperAdmin, hasPerm }) {
    const L = (fr, en) => (lang === "fr" ? fr : en);
    const S = window.STAR;
    const canManage = isSuperAdmin
      || (typeof hasPerm === "function" && (hasPerm("users.manage") || hasPerm("evaluations.manage")));

    const [iso3, setIso3] = useState(() => {
      try { return localStorage.getItem("melr.star.iso3") || "SEN"; } catch (_) { return "SEN"; }
    });
    const [cycle, setCycle] = useState("GEF-8");

    // Projets MELR (suivi de l'utilisation, temps réel via le hook realtime).
    const { projects: allProjects } = window.melr.useProjects ? window.melr.useProjects() : { projects: [] };
    const projByUuid = useMemo(() => {
      const m = {};
      (allProjects || []).forEach((p) => { m[p.uuid] = p; });
      return m;
    }, [allProjects]);

    // Snapshots Banque mondiale en base (rapide), + refresh à la demande.
    const { data: snaps, refresh: refreshSnaps } = window.melr.useStarWbSnapshots
      ? window.melr.useStarWbSnapshots({ orgId: effectiveOrgId, iso3 })
      : { data: [], refresh: () => {} };
    const wbByCode = useMemo(() => {
      const m = {};
      (snaps || []).forEach((r) => { m[r.indicator_code] = r; });
      return m;
    }, [snaps]);
    const [wbBusy, setWbBusy] = useState(false);
    const [wbMsg, setWbMsg] = useState(null);
    const doRefreshWb = async () => {
      setWbBusy(true); setWbMsg(null);
      try {
        await window.melr.refreshStarWorldBank(effectiveOrgId, iso3.trim().toUpperCase());
        refreshSnaps && refreshSnaps();
        setWbMsg(L("Données Banque mondiale actualisées.", "World Bank data refreshed."));
      } catch (e) { setWbMsg(L("Erreur : ", "Error: ") + e.message); }
      finally { setWbBusy(false); }
    };

    // Profil FEM (entrées manuelles).
    const [profile, setProfile] = useState(null);
    const [dirty, setDirty] = useState(false);
    const [saveBusy, setSaveBusy] = useState(false);
    const [saveMsg, setSaveMsg] = useState(null);
    useEffect(() => {
      let cancelled = false;
      setProfile(null); setDirty(false); setSaveMsg(null);
      if (!effectiveOrgId || !iso3) return;
      window.melr.fetchStarProfile(effectiveOrgId, iso3.trim().toUpperCase(), cycle)
        .then((p) => { if (!cancelled) setProfile(p || { gbi: {}, ppi: {}, allocation: {} }); })
        .catch(() => { if (!cancelled) setProfile({ gbi: {}, ppi: {}, allocation: {} }); });
      return () => { cancelled = true; };
    }, [effectiveOrgId, iso3, cycle]);
    const patchProfile = (p) => { setProfile({ ...(profile || {}), ...p }); setDirty(true); };
    const linkProject = (faKey, uuid) => {
      const prj = { ...((profile && profile.projects) || {}) };
      const list = (prj[faKey] || []).slice();
      if (!list.includes(uuid)) list.push(uuid);
      prj[faKey] = list;
      patchProfile({ projects: prj });
    };
    const unlinkProject = (faKey, uuid) => {
      const prj = { ...((profile && profile.projects) || {}) };
      prj[faKey] = (prj[faKey] || []).filter((u) => u !== uuid);
      patchProfile({ projects: prj });
    };
    const saveProfile = async () => {
      setSaveBusy(true); setSaveMsg(null);
      try {
        const country = (SUGGESTED.find((s) => s.iso3 === iso3) || {});
        await window.melr.saveStarProfile(effectiveOrgId, iso3.trim().toUpperCase(), cycle, {
          country_name: profile.country_name || (lang === "fr" ? country.fr : country.en) || null,
          gbi: profile.gbi || {}, ppi: profile.ppi || {}, allocation: profile.allocation || {},
          projects: profile.projects || {},
          gdp_index: profile.gdp_index == null || profile.gdp_index === "" ? null : Number(profile.gdp_index),
          notes: profile.notes || null,
        });
        setDirty(false);
        setSaveMsg(L("Profil enregistré.", "Profile saved."));
      } catch (e) { setSaveMsg(L("Erreur : ", "Error: ") + e.message); }
      finally { setSaveBusy(false); }
    };

    // Valeurs combinées pour le calcul.
    const cepia = wbByCode[S.WB.cepia.code] ? wbByCode[S.WB.cepia.code].value : null;
    const bfi   = wbByCode[S.WB.bfi.code]   ? wbByCode[S.WB.bfi.code].value   : null;
    const gdpPc = wbByCode[S.WB.gdpPerCapita.code] ? wbByCode[S.WB.gdpPerCapita.code].value : null;
    const ppiInputs = (profile && profile.ppi) || {};
    const calcInput = useMemo(() => ({
      cepia, bfi,
      pir: ppiInputs.pir, ter: ppiInputs.ter, disb: ppiInputs.disb,
      gbi: profile && profile.gbi ? totalGbi(profile.gbi) : null,
      gdpIndex: profile && profile.gdp_index != null && profile.gdp_index !== "" ? Number(profile.gdp_index) : 1,
    }), [cepia, bfi, ppiInputs.pir, ppiInputs.ter, ppiInputs.disb, profile]);
    const ppw = S.ppiWeightsFor(cycle);          // poids PPI du cycle (pir/ter/disb)
    const cpiR = S.cpi(calcInput, cycle);
    const scoreR = S.countryScore(calcInput, cycle);

    // Simulateur de sensibilité.
    const [simField, setSimField] = useState("cepia");
    const [simValue, setSimValue] = useState("");
    const simFields = [
      { key: "cepia", fr: "CEPIA (environnement)", en: "CEPIA (environment)", cur: cepia },
      { key: "bfi",   fr: "BFI (Cluster D)",        en: "BFI (Cluster D)",       cur: bfi },
      { key: "ppi",   fr: "PPI (portefeuille)",     en: "PPI (portfolio)",       cur: cpiR ? cpiR.inputs.ppi : null },
      { key: "gbi",   fr: "GBI (bénéfices)",        en: "GBI (benefits)",        cur: calcInput.gbi },
      { key: "gdpIndex", fr: "Indice PIB",          en: "GDP index",             cur: calcInput.gdpIndex },
    ];
    const simCur = (simFields.find((f) => f.key === simField) || {}).cur;
    const sim = (simValue !== "" && S.countryScore(calcInput, cycle))
      ? S.sensitivity(calcInput, simField, Number(simValue), cycle) : null;

    const lbl = { fontSize: 11, fontWeight: 600, display: "block", marginBottom: 2 };
    const wbCell = (code) => {
      const r = wbByCode[code];
      return r ? (fmt(r.value) + (r.year ? "  (" + r.year + ")" : "")) : "—";
    };

    return (
      <div className="page" data-screen-label="STAR / FEM">
        <div className="page-header">
          <div className="page-eyebrow">{L("ALLOCATION FEM · STAR", "GEF ALLOCATION · STAR")}</div>
          <div className="page-header-row">
            <div>
              <h1 className="page-title">{L("Allocation FEM (STAR)", "GEF allocation (STAR)")}</h1>
              <div className="page-sub">
                {L("Système Transparent d'Allocation des Ressources — formule FEM-8 : Score = CPI¹·⁰ × GBI⁰·⁸ × PIB⁻⁰·¹⁶. Données CPIA/PIB en direct de la Banque mondiale.",
                   "System for Transparent Allocation of Resources — GEF-8 formula: Score = CPI¹·⁰ × GBI⁰·⁸ × GDP⁻⁰·¹⁶. CPIA/GDP data live from the World Bank.")}
              </div>
            </div>
          </div>
        </div>

        {/* Sélection pays / cycle */}
        <div style={{ display: "flex", gap: 10, alignItems: "end", flexWrap: "wrap", marginBottom: 14 }}>
          <div>
            <label style={lbl}>{L("Pays (ISO3)", "Country (ISO3)")}</label>
            <input className="input" list="star-countries" value={iso3}
              onChange={(e) => { const v = e.target.value.toUpperCase().slice(0, 3); setIso3(v); try { localStorage.setItem("melr.star.iso3", v); } catch (_) {} }}
              style={{ width: 120, textTransform: "uppercase" }} />
            <datalist id="star-countries">
              {SUGGESTED.map((s) => <option key={s.iso3} value={s.iso3}>{lang === "fr" ? s.fr : s.en}</option>)}
            </datalist>
          </div>
          <div>
            <label style={lbl}>{L("Cycle", "Cycle")}</label>
            <select className="input" value={cycle} onChange={(e) => setCycle(e.target.value)}>
              {Object.keys(S.CYCLES).map((k) => <option key={k} value={k}>{k}{S.CYCLES[k].provisional ? " ⚠" : ""}</option>)}
            </select>
            {S.CYCLES[cycle] && S.CYCLES[cycle].provisional && (
              <div className="text-faint" style={{ fontSize: 11, marginTop: 4, color: "#b45309" }}>
                ⚠ {L("Coefficients provisoires (repris de FEM-8) — à confirmer sur GEF/C.71/06.", "Provisional coefficients (carried from GEF-8) — to be confirmed against GEF/C.71/06.")}
              </div>
            )}
          </div>
          <button className="btn sm primary" disabled={wbBusy} onClick={doRefreshWb}>
            {wbBusy ? "…" : <><Icon.refresh /> {L("Actualiser données Banque mondiale", "Refresh World Bank data")}</>}
          </button>
          {wbMsg && <span className="text-faint" style={{ fontSize: 11.5 }}>{wbMsg}</span>}
        </div>

        {/* Score & décomposition (bleu = synthèse/KPI) */}
        <div style={sec("blue")}>
          <div>
            <div style={secT("blue")}>📊 {L("Score pays et décomposition", "Country score and decomposition")}</div>
            <div style={{ display: "flex", gap: 12, flexWrap: "wrap", marginBottom: 8 }}>
              <Kpi label="CPI" value={cpiR ? fmt(cpiR.value, 3) : "—"} hint={L("indice de performance (0,20 PPI + 0,65 CEPIA + 0,15 BFI)", "performance index (0.20 PPI + 0.65 CEPIA + 0.15 BFI)")} />
              <Kpi label={L("Score pays (modèle)", "Country score (model)")} value={scoreR ? fmt(scoreR.score, 2) : "—"} hint={cycle + " · CPI¹·⁰ × GBI⁰·⁸ × PIB" + S.CYCLES[cycle].gdpWeight} />
              <Kpi label={L("Part du CPIA dans le CPI", "CPIA share of CPI")} value="80 %" hint={L("CEPIA 65 % + BFI 15 %", "CEPIA 65% + BFI 15%")} />
            </div>
            {cpiR && (
              <div style={{ display: "flex", height: 14, borderRadius: 7, overflow: "hidden", border: "1px solid var(--line)" }}>
                {[
                  { k: "cepia", c: "#16a34a", v: cpiR.contrib.cepia },
                  { k: "ppi",   c: "#2563eb", v: cpiR.contrib.ppi },
                  { k: "bfi",   c: "#d97706", v: cpiR.contrib.bfi },
                ].map((p) => (
                  <div key={p.k} title={p.k.toUpperCase() + " : " + fmt(p.v, 2)}
                    style={{ width: (100 * p.v / cpiR.value) + "%", background: p.c }} />
                ))}
              </div>
            )}
            {cpiR && (
              <div className="text-faint" style={{ fontSize: 10.5, marginTop: 4, display: "flex", gap: 14, flexWrap: "wrap" }}>
                <span><span style={{ color: "#16a34a", fontWeight: 700 }}>■</span> CEPIA {fmt(cpiR.contrib.cepia, 2)}</span>
                <span><span style={{ color: "#2563eb", fontWeight: 700 }}>■</span> PPI {fmt(cpiR.contrib.ppi, 2)}</span>
                <span><span style={{ color: "#d97706", fontWeight: 700 }}>■</span> BFI {fmt(cpiR.contrib.bfi, 2)}</span>
              </div>
            )}
            {!cpiR && (
              <div className="text-faint" style={{ fontSize: 11.5 }}>
                {L("Actualisez les données Banque mondiale (CEPIA + BFI) et renseignez le PPI ci-dessous pour calculer le CPI.",
                   "Refresh World Bank data (CEPIA + BFI) and enter PPI below to compute the CPI.")}
              </div>
            )}
          </div>
        </div>

        {/* Données Banque mondiale (vert = données) */}
        <div style={sec("green")}>
          <div>
            <div style={secT("green")}>🌍 {L("Données Banque mondiale (CPIA / IRAI — en direct)", "World Bank data (CPIA / IRAI — live)")}</div>
            <table className="tbl" style={{ width: "100%" }}>
              <tbody>
                <tr><td style={{ fontSize: 12 }}>{lang === "fr" ? S.WB.cepia.fr : S.WB.cepia.en}</td>
                    <td style={{ textAlign: "right", fontWeight: 700 }}>{wbCell(S.WB.cepia.code)}</td></tr>
                <tr><td style={{ fontSize: 12 }}>{lang === "fr" ? S.WB.bfi.fr : S.WB.bfi.en}</td>
                    <td style={{ textAlign: "right", fontWeight: 700 }}>{wbCell(S.WB.bfi.code)}</td></tr>
                <tr><td style={{ fontSize: 12 }}>{lang === "fr" ? S.WB.gdpPerCapita.fr : S.WB.gdpPerCapita.en}</td>
                    <td style={{ textAlign: "right", fontWeight: 700 }}>{gdpPc != null ? fmtMoney(Math.round(gdpPc)) : "—"}</td></tr>
              </tbody>
            </table>
            <details style={{ marginTop: 8 }}>
              <summary style={{ fontSize: 11.5, cursor: "pointer", color: "var(--text-muted)" }}>
                {L("Détail du Cluster D (BFI = moyenne des 5 critères)", "Cluster D breakdown (BFI = average of 5 criteria)")}
              </summary>
              <table className="tbl" style={{ width: "100%", marginTop: 6 }}>
                <tbody>
                  {S.CLUSTER_D.map((c) => (
                    <tr key={c.code}><td style={{ fontSize: 11.5 }}>{lang === "fr" ? c.fr : c.en}</td>
                      <td style={{ textAlign: "right", fontSize: 11.5 }}>{wbCell(c.code)}</td></tr>
                  ))}
                </tbody>
              </table>
            </details>
            <div className="text-faint" style={{ fontSize: 10.5, marginTop: 6 }}>
              {L("Source : API ouverte data.worldbank.org (indice IRAI / CPIA, échelle 1–6). Disponible pour les pays IDA ; pour un pays non-IDA le CPIA est confidentiel — saisir alors CEPIA/BFI manuellement.",
                 "Source: open API data.worldbank.org (IRAI / CPIA index, 1–6 scale). Available for IDA countries; for non-IDA countries the CPIA is confidential — enter CEPIA/BFI manually instead.")}
            </div>
          </div>
        </div>

        {/* Entrées propres au FEM (ambre = saisie/paramètres) */}
        <div style={sec("amber")}>
          <div>
            <div style={secT("amber")}>✏️ {L("Entrées propres au FEM", "GEF-specific inputs")}</div>
            {!profile && <div className="text-faint" style={{ fontSize: 11.5 }}>{L("Chargement…", "Loading…")}</div>}
            {profile && (
              <>
                <div style={{ fontSize: 12, fontWeight: 700, margin: "4px 0 6px" }}>{L("PPI — performance du portefeuille (1–6)", "PPI — portfolio performance (1–6)")}</div>
                <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 10 }}>
                  <NumIn lang={lang} disabled={!canManage} label={L("PIR (exécution, " + Math.round(ppw.pir * 100) + " %)", "PIR (implementation, " + Math.round(ppw.pir * 100) + "%)")}
                    value={(profile.ppi || {}).pir} onChange={(v) => patchProfile({ ppi: { ...(profile.ppi || {}), pir: v } })} />
                  <NumIn lang={lang} disabled={!canManage} label={L("TER (achèvement, " + Math.round(ppw.ter * 100) + " %)", "TER (terminal eval, " + Math.round(ppw.ter * 100) + "%)")}
                    value={(profile.ppi || {}).ter} onChange={(v) => patchProfile({ ppi: { ...(profile.ppi || {}), ter: v } })} />
                  {ppw.disb > 0 && (
                    <NumIn lang={lang} disabled={!canManage} label={L("Décaissement (" + Math.round(ppw.disb * 100) + " %)", "Disbursement (" + Math.round(ppw.disb * 100) + "%)")}
                      value={(profile.ppi || {}).disb} onChange={(v) => patchProfile({ ppi: { ...(profile.ppi || {}), disb: v } })} />
                  )}
                  <div style={{ alignSelf: "end", fontSize: 11.5, color: "var(--text-muted)" }}>
                    PPI = {cpiR ? fmt(cpiR.inputs.ppi, 2) : "—"}
                  </div>
                </div>
                <div style={{ fontSize: 12, fontWeight: 700, margin: "4px 0 6px" }}>{L("GBI — indice de bénéfices, par domaine focal", "GBI — benefits index, by focal area")}</div>
                <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 4 }}>
                  {S.FOCAL_AREAS.map((fa) => (
                    <NumIn key={fa.key} lang={lang} disabled={!canManage} label={lang === "fr" ? fa.fr : fa.en}
                      value={(profile.gbi || {})[fa.key]} onChange={(v) => patchProfile({ gbi: { ...(profile.gbi || {}), [fa.key]: v } })} />
                  ))}
                  <NumIn lang={lang} disabled={!canManage} label={L("Indice PIB (1 = neutre)", "GDP index (1 = neutral)")}
                    value={profile.gdp_index} onChange={(v) => patchProfile({ gdp_index: v })} step="0.01" />
                </div>
                <div className="text-faint" style={{ fontSize: 10.5, marginBottom: 10 }}>
                  {L("GBI total (somme domaines) = ", "Total GBI (sum of focal areas) = ")}{fmt(totalGbi(profile.gbi), 2)}
                </div>
                <div style={{ fontSize: 12, fontWeight: 700, margin: "4px 0 6px" }}>{L("Allocation officielle du cycle (US$), par domaine focal", "Official cycle allocation (US$), by focal area")}</div>
                <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 4 }}>
                  {S.FOCAL_AREAS.map((fa) => (
                    <NumIn key={fa.key} lang={lang} disabled={!canManage} wide label={lang === "fr" ? fa.fr : fa.en}
                      value={(profile.allocation || {})[fa.key]} onChange={(v) => patchProfile({ allocation: { ...(profile.allocation || {}), [fa.key]: v } })} />
                  ))}
                  <div style={{ alignSelf: "end", fontSize: 11.5, fontWeight: 700 }}>
                    {L("Total : ", "Total: ")}{fmtMoney(totalGbi(profile.allocation))}
                  </div>
                </div>
                {canManage && (
                  <div style={{ marginTop: 10 }}>
                    <button className="btn sm primary" disabled={saveBusy || !dirty} onClick={saveProfile}>
                      {saveBusy ? "…" : L("Enregistrer le profil", "Save profile")}
                    </button>
                    {dirty && <span style={{ fontSize: 11, color: "#b45309", marginLeft: 8 }}>{L("Non enregistré", "Unsaved")}</span>}
                    {saveMsg && <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>{saveMsg}</span>}
                  </div>
                )}
              </>
            )}
          </div>
        </div>

        {/* Suivi de l'utilisation (cyan = exécution temps réel) */}
        <div style={sec("blue")}>
          <div>
            <div style={secT("blue")}>📈 {L("Suivi de l'utilisation (temps réel)", "Utilization tracking (real time)")}</div>
            <div className="text-faint" style={{ fontSize: 11.5, marginBottom: 8 }}>
              {L("Reliez les projets MELR financés par le FEM à chaque domaine focal : engagé (budget) et décaissé sont agrégés en direct depuis vos projets.",
                 "Link the MELR projects financed by the GEF to each focal area: committed (budget) and disbursed are aggregated live from your projects.")}
            </div>
            {(S.FOCAL_AREAS).map((fa) => {
              const linked = ((profile && profile.projects) || {})[fa.key] || [];
              const u = utilFor(linked, projByUuid);
              const alloc = profile && profile.allocation ? Number((profile.allocation || {})[fa.key]) : null;
              return (
                <div key={fa.key} style={{ padding: "8px 0", borderTop: "1px dashed var(--line)" }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap", marginBottom: 4 }}>
                    <span style={{ fontSize: 12.5, fontWeight: 700, minWidth: 150 }}>{lang === "fr" ? fa.fr : fa.en}</span>
                    <span className="text-faint" style={{ fontSize: 11 }}>
                      {L("Engagé : ", "Committed: ")}<b>{fmtM(u.budget)}</b>
                      {L(" · Décaissé : ", " · Disbursed: ")}<b>{fmtM(u.disbursed)}</b>
                      {alloc ? L(" · Alloué FEM : ", " · GEF allocation: ") + fmtMoney(alloc) : ""}
                    </span>
                    <span style={{ marginLeft: "auto", fontSize: 12, fontWeight: 800, color: u.rate >= 60 ? "#15803d" : u.rate >= 30 ? "#b45309" : "#b91c1c" }}>
                      {u.budget > 0 ? Math.round(u.rate) + " %" : "—"} <span className="text-faint" style={{ fontWeight: 400, fontSize: 10 }}>{L("absorption", "absorption")}</span>
                    </span>
                  </div>
                  <div style={{ height: 8, background: "var(--bg-sunken)", borderRadius: 4, overflow: "hidden" }}>
                    <div style={{ width: Math.min(100, u.rate || 0) + "%", height: 8, background: "#1d4ed8", borderRadius: 4, transition: "width .3s" }} />
                  </div>
                  {/* chips des projets liés + ajout */}
                  <div style={{ display: "flex", gap: 6, flexWrap: "wrap", alignItems: "center", marginTop: 6 }}>
                    {linked.map((uuid) => {
                      const p = projByUuid[uuid];
                      return (
                        <span key={uuid} style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 10.5, background: "var(--bg-sunken)", border: "1px solid var(--line)", borderRadius: 999, padding: "1px 8px" }}>
                          {p ? p.id : uuid.slice(0, 6)}
                          {canManage && (
                            <button onClick={() => unlinkProject(fa.key, uuid)} style={{ border: "none", background: "transparent", cursor: "pointer", color: "var(--text-faint)", padding: 0, fontSize: 12 }} title={L("Retirer", "Remove")}>×</button>
                          )}
                        </span>
                      );
                    })}
                    {canManage && (
                      <select className="input" value="" onChange={(e) => { if (e.target.value) linkProject(fa.key, e.target.value); }}
                        style={{ fontSize: 11, width: 200 }}>
                        <option value="">{L("+ lier un projet…", "+ link a project…")}</option>
                        {(allProjects || []).filter((p) => !linked.includes(p.uuid)).map((p) => (
                          <option key={p.uuid} value={p.uuid}>{p.id} · {lang === "fr" ? p.nameFr : (p.nameEn || p.nameFr)}</option>
                        ))}
                      </select>
                    )}
                  </div>
                </div>
              );
            })}
            <div className="text-faint" style={{ fontSize: 10, marginTop: 6 }}>
              {L("Taux d'absorption = décaissé / engagé (sans conversion de devise). L'allocation FEM est en US$ ; les montants projets sont en devise native.",
                 "Absorption rate = disbursed / committed (no currency conversion). The GEF allocation is in US$; project amounts are in native currency.")}
            </div>
          </div>
        </div>

        {/* Simulateur de sensibilité (violet = analyse) */}
        <div style={sec("purple")}>
          <div>
            <div style={secT("purple")}>🎚 {L("Simulateur de sensibilité", "Sensitivity simulator")}</div>
            <div className="text-faint" style={{ fontSize: 11.5, marginBottom: 8 }}>
              {L("Variation EXACTE du score pays si un facteur change (élasticité invariante d'échelle — indépendante de la normalisation FEM).",
                 "EXACT change in the country score if one driver changes (scale-invariant elasticity — independent of GEF normalization).")}
            </div>
            <div style={{ display: "flex", gap: 10, alignItems: "end", flexWrap: "wrap" }}>
              <div>
                <label style={lbl}>{L("Facteur", "Driver")}</label>
                <select className="input" value={simField} onChange={(e) => { setSimField(e.target.value); setSimValue(""); }}>
                  {simFields.map((f) => <option key={f.key} value={f.key}>{lang === "fr" ? f.fr : f.en}</option>)}
                </select>
              </div>
              <div>
                <label style={lbl}>{L("Valeur actuelle", "Current value")}</label>
                <input className="input" value={simCur == null ? "—" : fmt(simCur, 2)} disabled style={{ width: 90 }} />
              </div>
              <div>
                <label style={lbl}>{L("Nouvelle valeur", "New value")}</label>
                <input type="number" className="input" value={simValue} onChange={(e) => setSimValue(e.target.value)}
                  placeholder={simCur == null ? "" : fmt(simCur, 1)} style={{ width: 100 }} />
              </div>
              {sim && (
                <div style={{ alignSelf: "end", fontSize: 14, fontWeight: 800, color: sim.deltaPct >= 0 ? "#15803d" : "#b91c1c" }}>
                  {sim.deltaPct >= 0 ? "▲ +" : "▼ "}{fmt(sim.deltaPct, 2)} %
                  <span className="text-faint" style={{ fontWeight: 400, fontSize: 11, marginLeft: 6 }}>
                    {L("sur le score", "on the score")}
                  </span>
                </div>
              )}
            </div>
          </div>
        </div>

        <div className="text-faint" style={{ fontSize: 10.5, marginTop: 4 }}>
          {L("Modèle indicatif fondé sur les formules officielles du STAR (FEM-8 : GEF/C.62/04 ; GEF-9 : GEF/C.71/06, PIB -0,21 + indice de décaissement). L'allocation finale en dollars est fixée par le Secrétariat du FEM par reconstitution (GBI et normalisation internes).",
             "Indicative model based on the official STAR formulas (GEF-8: GEF/C.62/04; GEF-9: GEF/C.71/06, GDP -0.21 + disbursement index). The final dollar allocation is set by the GEF Secretariat per replenishment (internal GBI and normalization).")}
        </div>
      </div>
    );
  }

  function totalGbi(obj) {
    if (!obj) return null;
    const vals = Object.values(obj).map((v) => Number(v)).filter((v) => !Number.isNaN(v));
    if (!vals.length) return null;
    return vals.reduce((s, v) => s + v, 0);
  }

  function Kpi({ label, value, hint }) {
    return (
      <div style={{ background: "var(--bg-elev)", border: "1px solid var(--line)", borderRadius: 8, padding: "10px 14px", minWidth: 130, flex: 1 }}>
        <div style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: ".4px", color: "var(--muted)", fontWeight: 600 }}>{label}</div>
        <div style={{ fontSize: 22, fontWeight: 800, marginTop: 2 }}>{value}</div>
        {hint && <div style={{ fontSize: 10, color: "var(--muted)", marginTop: 2 }}>{hint}</div>}
      </div>
    );
  }

  function NumIn({ lang, label, value, onChange, disabled, step, wide }) {
    return (
      <div>
        <label style={{ fontSize: 10.5, fontWeight: 600, display: "block", marginBottom: 2 }}>{label}</label>
        <input type="number" className="input" disabled={disabled} step={step || "0.1"}
          value={value == null ? "" : value}
          onChange={(e) => onChange(e.target.value === "" ? null : Number(e.target.value))}
          style={{ width: wide ? 150 : 90, fontSize: 12, textAlign: "right" }} />
      </div>
    );
  }

  window.StarScreen = StarScreen;

  // ── Carte compacte pour le dashboard principal des programmes ────────────
  // Auto-suffisante : charge le profil du pays par défaut (localStorage),
  // les snapshots Banque mondiale et les projets, puis affiche le score CPI
  // et la jauge d'absorption du portefeuille FEM. Masquée si rien n'est
  // configuré (pour ne pas encombrer les organisations non concernées).
  function StarDashboardCard({ lang, effectiveOrgId, myOrgId }) {
    const L = (fr, en, es) => (lang === "es" ? (es != null ? es : en) : lang === "en" ? en : fr);
    const S = window.STAR;
    const orgId = effectiveOrgId || myOrgId;
    const iso3 = (() => { try { return localStorage.getItem("melr.star.iso3") || "SEN"; } catch (_) { return "SEN"; } })();
    const { data: snaps } = window.melr.useStarWbSnapshots ? window.melr.useStarWbSnapshots({ orgId, iso3 }) : { data: [] };
    const { projects: allProjects } = window.melr.useProjects ? window.melr.useProjects() : { projects: [] };
    const [profile, setProfile] = useState(undefined); // undefined = chargement, null = absent
    useEffect(() => {
      let cancelled = false;
      if (!orgId) { setProfile(null); return; }
      window.melr.fetchStarProfile(orgId, iso3, "GEF-8")
        .then((p) => { if (!cancelled) setProfile(p); })
        .catch(() => { if (!cancelled) setProfile(null); });
      return () => { cancelled = true; };
    }, [orgId, iso3]);

    if (!S || profile === undefined) return null;
    if (!profile) return null; // pas de profil STAR pour cette org → carte masquée

    const wb = {}; (snaps || []).forEach((r) => { wb[r.indicator_code] = r; });
    const cepia = wb[S.WB.cepia.code] ? wb[S.WB.cepia.code].value : null;
    const bfi   = wb[S.WB.bfi.code]   ? wb[S.WB.bfi.code].value   : null;
    const projByUuid = {}; (allProjects || []).forEach((p) => { projByUuid[p.uuid] = p; });
    const totalGbiV = profile.gbi ? Object.values(profile.gbi).map(Number).filter((v) => !Number.isNaN(v)).reduce((s, v) => s + v, 0) : null;
    const cpiR = S.cpi({ cepia, bfi, pir: (profile.ppi || {}).pir, ter: (profile.ppi || {}).ter });
    const u = totalUtil(profile.projects, projByUuid);
    const country = profile.country_name || iso3;

    return (
      <div className="card tint-cyan" style={{ marginBottom: 16 }}>
        <div className="card-head">
          <div className="card-title">{L("Allocation FEM (STAR) — ", "GEF allocation (STAR) — ", "Asignación FMAM (STAR) — ") + country}</div>
          <button className="btn xs ghost" onClick={() => window.__navigate && window.__navigate("star")}>
            {L("Ouvrir", "Open", "Abrir")} →
          </button>
        </div>
        <div className="card-body">
          <div style={{ display: "flex", gap: 14, flexWrap: "wrap", alignItems: "center" }}>
            <Kpi label="CPI" value={cpiR ? fmt(cpiR.value, 2) : "—"} hint={L("performance (80 % CPIA)", "performance (80% CPIA)", "desempeño (80 % CPIA)")} />
            <Kpi label={L("Absorption portefeuille", "Portfolio absorption", "Absorción de cartera")}
              value={u.budget > 0 ? Math.round(u.rate) + " %" : "—"}
              hint={L(u.count + " projet(s) lié(s)", u.count + " linked project(s)", u.count + " proyecto(s)")} />
            <div style={{ flex: 1, minWidth: 160 }}>
              <div className="text-faint" style={{ fontSize: 10.5, marginBottom: 3 }}>
                {L("Engagé ", "Committed ", "Comprometido ")}{fmtM(u.budget)}{L(" · Décaissé ", " · Disbursed ", " · Desembolsado ")}{fmtM(u.disbursed)}
              </div>
              <div style={{ height: 8, background: "var(--bg-sunken)", borderRadius: 4, overflow: "hidden" }}>
                <div style={{ width: Math.min(100, u.rate || 0) + "%", height: 8, background: "#0891b2", borderRadius: 4 }} />
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
  window.StarDashboardCard = StarDashboardCard;
})();
