/* global React, Icon, L */
const { useState: useStateB, useEffect: useEffectB, useRef: useRefB, useMemo: useMemoB } = React;

// ==================== BASELINE + MAP ====================
function Baseline({ t, lang, isSuperAdmin, isAdmin, hasPerm }) {
  // Baseline write actions (create site, bulk-import from CSV) are admin
  // actions — gate them on users.manage or super-admin. Non-admin users
  // still see the map, the sites list, the stakeholders/infra/issues
  // panels (all read-only).
  const canManageBaseline = !!isSuperAdmin || !!isAdmin
    || (hasPerm && hasPerm("users.manage"));
  // Hooks must come first (rules of hooks).
  const { data: liveSites, refresh: refreshSites, realtime } = window.melr.useSites();
  const { projects } = window.melr.useProjects();
  const LiveBadge = window.melr.LiveBadge;
  const [createOpen, setCreateOpen] = useStateB(false);
  const [importOpen, setImportOpen] = useStateB(false);
  // Active project for the 4 operational tabs (Acteurs / Infra / Ops / Issues).
  // The 'Sites' tab keeps its cross-project view.
  const [activeProject, setActiveProject] = useStateB("");
  useEffectB(() => {
    if (!activeProject && projects && projects.length > 0) {
      setActiveProject(projects[0].uuid);
    }
  }, [projects]);
  // Map live rows to the legacy "site" shape so the rest of the screen
  // keeps working. Only those with metadata.coords get rendered on the
  // Leaflet map; the others appear in the right-hand list only.
  // Stable reference so the Leaflet effect doesn't loop on each render.
  const liveMapped = useMemoB(() => {
    if (!liveSites || liveSites.length === 0) return [];
    const mapped = liveSites.map((s) => {
      const c = (s.metadata && s.metadata.coords) || {};
      return {
        id: s.code || s.id,
        name: s.name,
        region: s.region || "—",
        country: s.country_iso2 || "—",
        status: s.status || "ok",
        beneficiaries: s.beneficiaries || 0,
        staff: s.staff_count || 0,
        cpn: 0, dtc3: 0,
        lat: typeof c.lat === "number" ? c.lat : null,
        lng: typeof c.lng === "number" ? c.lng : null,
        project: s.projects && s.projects.code,
      };
    });
    console.log("[MELR] Baseline — live sites:", mapped.length,
                "with coords:", mapped.filter((s) => s.lat !== null).length);
    return mapped;
  }, [liveSites]);
  const hasLive = liveMapped.length > 0;

  // Fixture (Sahel SVG) — kept as fallback when no live sites are
  // available, so the demo stays populated.
  const FIXTURE_SITES = [
    { id: "S-01", name: "Tahoua CSI-A", region: "Tahoua", country: "NE", x: 280, y: 200, status: "ok", beneficiaries: 18420, cpn: 0.82, dtc3: 0.71, staff: 24 },
    { id: "S-02", name: "Maradi CSI-B", region: "Maradi", country: "NE", x: 320, y: 240, status: "ok", beneficiaries: 22110, cpn: 0.76, dtc3: 0.68, staff: 31 },
    { id: "S-03", name: "Tillabéri H-1", region: "Tillabéri", country: "NE", x: 220, y: 180, status: "warn", beneficiaries: 9800, cpn: 0.41, dtc3: 0.32, staff: 12 },
    { id: "S-04", name: "Niamey CHR", region: "Niamey", country: "NE", x: 240, y: 220, status: "ok", beneficiaries: 64200, cpn: 0.91, dtc3: 0.85, staff: 84 },
    { id: "S-05", name: "Ouahigouya CMA", region: "Nord", country: "BF", x: 180, y: 260, status: "warn", beneficiaries: 14300, cpn: 0.64, dtc3: 0.52, staff: 18 },
    { id: "S-06", name: "Bobo-Dlsso H-2", region: "Hauts-Bassins", country: "BF", x: 140, y: 290, status: "ok", beneficiaries: 27800, cpn: 0.78, dtc3: 0.74, staff: 41 },
    { id: "S-07", name: "Gao CSPS", region: "Gao", country: "ML", x: 195, y: 165, status: "risk", beneficiaries: 7200, cpn: 0.28, dtc3: 0.18, staff: 8 },
    { id: "S-08", name: "Mopti CR", region: "Mopti", country: "ML", x: 155, y: 220, status: "warn", beneficiaries: 19500, cpn: 0.55, dtc3: 0.48, staff: 22 },
    { id: "S-09", name: "Bamako CHU", region: "Bamako", country: "ML", x: 110, y: 260, status: "ok", beneficiaries: 88000, cpn: 0.94, dtc3: 0.89, staff: 142 },
    { id: "S-10", name: "Kayes CSCom", region: "Kayes", country: "ML", x: 75, y: 245, status: "warn", beneficiaries: 11800, cpn: 0.59, dtc3: 0.46, staff: 14 },
    { id: "S-11", name: "Ségou H-1", region: "Ségou", country: "ML", x: 130, y: 240, status: "ok", beneficiaries: 24100, cpn: 0.81, dtc3: 0.77, staff: 33 },
    { id: "S-12", name: "Dosso CSI", region: "Dosso", country: "NE", x: 260, y: 250, status: "ok", beneficiaries: 13900, cpn: 0.73, dtc3: 0.66, staff: 19 },
  ];
  const sites = hasLive ? liveMapped : FIXTURE_SITES;
  const [selected, setSelected] = useStateB(hasLive ? (liveMapped[0] && liveMapped[0].id) : "S-04");
  const [tab, setTab] = useStateB("sites");
  const sel = sites.find(s => s.id === selected) || sites[0];

  return (
    <div className="page">
      <div className="page-header">
        <div className="page-eyebrow">{lang === "fr" ? "MODULE / SITUATION SANS PROJET" : "MODULE / SITUATION WITHOUT PROJECT"}</div>
        <div className="page-header-row">
          <div>
            <h1 className="page-title">{t("nav.baseline")} <LiveBadge on={realtime} lang={lang} /></h1>
            <div className="page-sub">
              {hasLive
                ? (lang === "fr"
                    ? `${liveMapped.length} sites cartographiés · données de référence · acteurs, infrastructure, comptes d'exploitation`
                    : `${liveMapped.length} sites mapped · reference data · stakeholders, infrastructure, baseline operating accounts`)
                : (lang === "fr"
                    ? "P-241 Sahel · 42 sites cartographiés · données de référence pré-projet · acteurs, infrastructure, comptes d'exploitation actuels"
                    : "P-241 Sahel · 42 sites mapped · pre-project reference data · stakeholders, infrastructure, baseline operating accounts")}
            </div>
          </div>
          <div className="page-header-actions">
            {canManageBaseline && (
              <button className="btn sm" onClick={() => setImportOpen(true)}>
                <Icon.upload /> {lang === "fr" ? "Importer terrain" : "Field import"}
              </button>
            )}
            <button className="btn sm" onClick={() => {
              const date = new Date().toISOString().slice(0, 10);
              window.melr.exportCSV(`sites-${date}.csv`, sites, [
                { key: "id",            label: "Code" },
                { key: "name",          label: lang === "fr" ? "Nom" : "Name" },
                { key: "region",        label: lang === "fr" ? "Région" : "Region" },
                { key: "country",       label: lang === "fr" ? "Pays" : "Country" },
                { key: "lat",           label: "Latitude" },
                { key: "lng",           label: "Longitude" },
                { key: "beneficiaries", label: lang === "fr" ? "Bénéficiaires" : "Beneficiaries" },
                { key: "staff",         label: lang === "fr" ? "Personnel" : "Staff" },
                { key: "status",        label: lang === "fr" ? "Statut" : "Status" },
                { key: "project",       label: lang === "fr" ? "Projet" : "Project" },
              ]);
            }}><Icon.download /> {t("c.export")}</button>
            {canManageBaseline && (
              <button className="btn sm primary" onClick={() => setCreateOpen(true)}><Icon.plus /> {lang === "fr" ? "Nouveau site" : "New site"}</button>
            )}
          </div>
        </div>
        <div className="page-tabs">
          {[
            { k: "sites", l: lang === "fr" ? "Cartographie sites" : "Site mapping", n: 42 },
            { k: "actors", l: lang === "fr" ? "Acteurs" : "Stakeholders", n: null },
            { k: "infra", l: lang === "fr" ? "Infrastructure" : "Infrastructure", n: null },
            { k: "ops", l: lang === "fr" ? "Comptes d'exploitation" : "Operating accounts", n: null },
            { k: "issues", l: lang === "fr" ? "Problèmes identifiés" : "Issues identified", n: null },
          ].map((x) => (
            <div key={x.k} className={"tab" + (x.k === tab ? " active" : "")} onClick={() => setTab(x.k)}>
              {x.l} {x.n != null && <span className="tag-mono" style={{ marginLeft: 4 }}>{x.n}</span>}
            </div>
          ))}
        </div>
      </div>

      <div className="page-body">
        {/* Project selector — only relevant for the 4 operational tabs */}
        {tab !== "sites" && (
          <div className="card" style={{ marginBottom: 16 }}>
            <div className="card-body" style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 14px" }}>
              <span className="text-faint" style={{ fontSize: 11.5, textTransform: "uppercase", letterSpacing: "0.04em" }}>
                {lang === "fr" ? "Projet" : "Project"}
              </span>
              <select value={activeProject} onChange={(e) => setActiveProject(e.target.value)}
                style={{ padding: "6px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, background: "var(--bg, white)", color: "var(--text)", minWidth: 300 }}>
                <option value="">{lang === "fr" ? "— choisir un projet —" : "— pick a project —"}</option>
                {(projects || []).map((p) => (
                  <option key={p.uuid} value={p.uuid}>{p.id} — {lang === "fr" ? p.nameFr : p.nameEn}</option>
                ))}
              </select>
              {!activeProject && (
                <span className="text-faint" style={{ fontSize: 12 }}>
                  {lang === "fr"
                    ? "Sélectionnez un projet pour saisir les données de référence."
                    : "Pick a project to enter baseline data."}
                </span>
              )}
            </div>
          </div>
        )}

        {tab === "sites" && (hasLive
          ? <LeafletBaselineMap sites={sites} selected={selected} setSelected={setSelected} sel={sel} lang={lang} />
          : <BaselineMap sites={sites} selected={selected} setSelected={setSelected} sel={sel} lang={lang} />
        )}
        {tab === "actors" && <BaselineActors lang={lang} projectId={activeProject} />}
        {tab === "infra" && <BaselineInfra lang={lang} projectId={activeProject} />}
        {tab === "ops" && <BaselineOps lang={lang} projectId={activeProject} />}
        {tab === "issues" && <BaselineIssues lang={lang} projectId={activeProject} />}
      </div>
      {createOpen && (
        <CreateSiteModal
          lang={lang}
          onClose={() => setCreateOpen(false)}
          onCreated={refreshSites}
        />
      )}
      {importOpen && (
        <ImportSitesModal
          lang={lang}
          projects={projects || []}
          defaultProject={activeProject}
          onClose={() => setImportOpen(false)}
          onImported={async () => { await refreshSites(); setImportOpen(false); }}
        />
      )}
    </div>
  );
}

// =====================================================================
// ImportSitesModal — paste-or-upload a CSV of sites, preview, commit.
// Expected columns (header line, case-insensitive, any column order):
//   code, name, kind, region, country_iso2, beneficiaries, staff_count,
//   status, lat, lng
// Only `name` is strictly required; everything else is optional. Code
// is auto-generated when blank. Country defaults to SN. Status defaults
// to 'ok'. lat/lng (when both present and numeric) populate
// metadata.coords + the PostGIS location via set_site_location.
// =====================================================================
function ImportSitesModal({ lang, projects, defaultProject, onClose, onImported }) {
  const [projectCode, setProjectCode] = useStateB("");
  const [csvText, setCsvText] = useStateB("");
  const [parsed, setParsed] = useStateB(null);   // { headers, rows, errors }
  const [busy, setBusy] = useStateB(false);
  const [err, setErr] = useStateB(null);
  const [progress, setProgress] = useStateB(null); // { done, total, failures }
  const fileRef = useRefB(null);

  // Resolve the default project's legacy code from its UUID (which is
  // what activeProject holds).
  useEffectB(() => {
    if (projectCode) return;
    if (defaultProject && projects && projects.length > 0) {
      const p = projects.find((x) => x.uuid === defaultProject) || projects[0];
      if (p) setProjectCode(p.id);
    } else if (projects && projects.length > 0) {
      setProjectCode(projects[0].id);
    }
  }, [projects, defaultProject]);

  // Minimal CSV parser: handles quoted fields, escaped quotes ("") and
  // multi-line cells. Doesn't try to support every Excel quirk, but
  // covers the common copy-paste-from-spreadsheet case.
  const parseCSV = (text) => {
    const rows = [];
    let cur = [];
    let buf = "";
    let inQ = false;
    for (let i = 0; i < text.length; i++) {
      const ch = text[i];
      if (inQ) {
        if (ch === '"' && text[i + 1] === '"') { buf += '"'; i++; }
        else if (ch === '"') { inQ = false; }
        else { buf += ch; }
      } else {
        if (ch === '"') { inQ = true; }
        else if (ch === ',' || ch === ';' || ch === '\t') { cur.push(buf); buf = ""; }
        else if (ch === '\n' || ch === '\r') {
          if (buf !== "" || cur.length) { cur.push(buf); rows.push(cur); cur = []; buf = ""; }
          if (ch === '\r' && text[i + 1] === '\n') i++;
        } else { buf += ch; }
      }
    }
    if (buf !== "" || cur.length) { cur.push(buf); rows.push(cur); }
    return rows;
  };

  const tryParse = (text) => {
    setErr(null);
    if (!text || !text.trim()) { setParsed(null); return; }
    const grid = parseCSV(text.trim()).filter((r) => r.length > 0 && r.some((c) => c && c.trim()));
    if (grid.length < 2) { setErr(lang === "fr" ? "Au moins un en-tête + une ligne de données sont requis." : "Need at least a header + one data row."); setParsed(null); return; }
    const headers = grid[0].map((h) => (h || "").trim().toLowerCase());
    const expected = ["code","name","kind","region","country_iso2","beneficiaries","staff_count","status","lat","lng"];
    const missing = ["name"].filter((req) => !headers.includes(req));
    if (missing.length) { setErr((lang === "fr" ? "Colonne(s) manquante(s) : " : "Missing column(s): ") + missing.join(", ")); setParsed(null); return; }
    const idx = Object.fromEntries(expected.map((k) => [k, headers.indexOf(k)]));
    const rows = grid.slice(1).map((r, i) => {
      const get = (k) => idx[k] >= 0 ? (r[idx[k]] || "").trim() : "";
      const lat = parseFloat(get("lat")); const lng = parseFloat(get("lng"));
      return {
        _row: i + 2,
        code:          get("code"),
        name:          get("name"),
        kind:          get("kind"),
        region:        get("region"),
        country_iso2:  (get("country_iso2") || "SN").toUpperCase(),
        beneficiaries: get("beneficiaries") ? Number(get("beneficiaries")) : null,
        staff_count:   get("staff_count") ? Number(get("staff_count")) : null,
        status:        get("status") || "ok",
        coords:        (!isNaN(lat) && !isNaN(lng)) ? { lat, lng } : null,
      };
    });
    const errors = rows.filter((r) => !r.name).map((r) => "Ligne " + r._row + " : nom manquant.");
    setParsed({ headers, rows, errors });
  };

  const onFileChosen = (e) => {
    const file = e.target.files && e.target.files[0];
    e.target.value = "";
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => { setCsvText(ev.target.result); tryParse(ev.target.result); };
    reader.onerror = () => setErr(lang === "fr" ? "Échec lecture fichier." : "File read failed.");
    reader.readAsText(file);
  };

  const onCommit = async () => {
    if (!parsed || !projectCode) return;
    setBusy(true); setErr(null);
    const valid = parsed.rows.filter((r) => r.name);
    setProgress({ done: 0, total: valid.length, failures: [] });
    for (let i = 0; i < valid.length; i++) {
      const r = valid[i];
      try {
        await window.melr.createSite(projectCode, {
          code: r.code || ("S-" + Date.now().toString(36) + "-" + i),
          name: r.name,
          kind: r.kind || null,
          region: r.region || null,
          country_iso2: r.country_iso2,
          beneficiaries: r.beneficiaries,
          staff_count: r.staff_count,
          status: r.status,
          coords: r.coords,
        });
        setProgress((p) => ({ ...p, done: i + 1 }));
      } catch (ex) {
        setProgress((p) => ({ ...p, done: i + 1, failures: [...p.failures, { row: r._row, name: r.name, err: ex.message }] }));
      }
    }
    setBusy(false);
    // Auto-close if nothing failed.
    const failCount = (progress && progress.failures && progress.failures.length) || 0;
    if (failCount === 0) await onImported();
  };

  const Modal = window.Modal;
  const inp = { width: "100%", padding: "6px 8px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 12.5, background: "var(--bg, white)", color: "var(--text)", boxSizing: "border-box", fontFamily: "inherit" };
  const lbl = { display: "block", fontSize: 10.5, color: "var(--text-faint)", marginBottom: 3, textTransform: "uppercase", letterSpacing: "0.04em", marginTop: 10 };

  return (
    <Modal
      title={lang === "fr" ? "Importer des sites" : "Import sites"}
      onClose={busy ? null : onClose}
      size="lg"
      footer={
        <>
          <button className="btn sm" onClick={onClose} disabled={busy}>
            {lang === "fr" ? "Fermer" : "Close"}
          </button>
          <button className="btn sm primary" onClick={onCommit}
            disabled={busy || !parsed || !projectCode || (parsed && parsed.rows.filter((r) => r.name).length === 0)}>
            {busy
              ? (lang === "fr" ? "Import…" : "Importing…")
              : (parsed
                  ? (lang === "fr" ? `Importer ${parsed.rows.filter((r) => r.name).length} sites` : `Import ${parsed.rows.filter((r) => r.name).length} sites`)
                  : (lang === "fr" ? "Importer" : "Import"))}
          </button>
        </>
      }
    >
      <div style={{ fontSize: 12, color: "var(--text-faint)", marginBottom: 4 }}>
        {lang === "fr"
          ? "Collez un CSV ou téléversez un fichier. Colonnes attendues (séparées par virgule, point-virgule ou tabulation, dans n'importe quel ordre) :"
          : "Paste a CSV or upload a file. Expected columns (comma / semicolon / tab separated, any order):"}
        <div className="mono" style={{ fontSize: 11, marginTop: 4 }}>
          name (obligatoire), code, kind, region, country_iso2, beneficiaries, staff_count, status, lat, lng
        </div>
      </div>

      <label style={lbl}>{lang === "fr" ? "Projet cible" : "Target project"} *</label>
      <select style={inp} value={projectCode} onChange={(e) => setProjectCode(e.target.value)}>
        {(projects || []).length === 0 && <option value="">—</option>}
        {(projects || []).map((p) => (
          <option key={p.uuid || p.id} value={p.id}>
            {p.id} — {(lang === "fr" ? p.nameFr : p.nameEn) || p.nameFr}
          </option>
        ))}
      </select>

      <div style={{ display: "flex", gap: 8, marginTop: 10 }}>
        <input ref={fileRef} type="file" accept=".csv,text/csv,text/plain" style={{ display: "none" }} onChange={onFileChosen} />
        <button type="button" className="btn xs" onClick={() => fileRef.current && fileRef.current.click()}>
          <Icon.upload /> {lang === "fr" ? "Téléverser CSV" : "Upload CSV"}
        </button>
        <button type="button" className="btn xs ghost" onClick={() => { setCsvText(""); setParsed(null); setErr(null); }}>
          {lang === "fr" ? "Effacer" : "Clear"}
        </button>
      </div>

      <label style={lbl}>{lang === "fr" ? "Contenu CSV" : "CSV content"}</label>
      <textarea style={{ ...inp, minHeight: 140, fontFamily: "var(--font-mono, monospace)", fontSize: 11.5 }}
        value={csvText} onChange={(e) => { setCsvText(e.target.value); tryParse(e.target.value); }}
        spellCheck={false}
        placeholder={"name,code,region,country_iso2,beneficiaries,status\nCSCom Pikine Est,S-DKR-01,Dakar,SN,12500,ok"} />

      {parsed && (
        <div style={{ marginTop: 10, fontSize: 12, color: "var(--text-faint)" }}>
          {parsed.rows.length} {lang === "fr" ? "lignes parsées" : "rows parsed"} ·{" "}
          <span style={{ color: parsed.errors.length ? "#b91c1c" : "var(--text)" }}>
            {parsed.rows.filter((r) => r.name).length} {lang === "fr" ? "valides" : "valid"}
            {parsed.errors.length > 0 && ` · ${parsed.errors.length} ${lang === "fr" ? "erreurs" : "errors"}`}
          </span>
        </div>
      )}
      {parsed && parsed.errors.length > 0 && (
        <div style={{ marginTop: 6, fontSize: 11.5, color: "#b91c1c" }}>
          {parsed.errors.slice(0, 5).map((m, i) => <div key={i}>· {m}</div>)}
          {parsed.errors.length > 5 && <div>… +{parsed.errors.length - 5}</div>}
        </div>
      )}

      {progress && (
        <div style={{ marginTop: 10 }}>
          <div className="bar"><div className="bar-fill" style={{ width: Math.round((progress.done / progress.total) * 100) + "%" }}></div></div>
          <div style={{ fontSize: 11.5, marginTop: 4 }}>
            {progress.done}/{progress.total} {lang === "fr" ? "traités" : "processed"}
            {progress.failures.length > 0 && (
              <span style={{ marginLeft: 8, color: "#b91c1c" }}>
                · {progress.failures.length} {lang === "fr" ? "échecs" : "failures"}
              </span>
            )}
          </div>
          {progress.failures.length > 0 && (
            <div style={{ marginTop: 6, fontSize: 11, color: "#b91c1c", maxHeight: 80, overflowY: "auto" }}>
              {progress.failures.map((f, i) => <div key={i}>· {lang === "fr" ? "Ligne" : "Row"} {f.row} ({f.name}) : {f.err}</div>)}
            </div>
          )}
        </div>
      )}

      {err && <div style={{ color: "#b91c1c", fontSize: 12, marginTop: 10 }}>{err}</div>}
    </Modal>
  );
}

// ==================== CREATE SITE MODAL ====================
function CreateSiteModal({ lang, onClose, onCreated }) {
  const [code, setCode]         = useStateB("");
  const [name, setName]         = useStateB("");
  const [project, setProject]   = useStateB("P-001");
  const [kind, setKind]         = useStateB("");
  const [region, setRegion]     = useStateB("");
  const [country, setCountry]   = useStateB("SN");
  const [lat, setLat]           = useStateB("");
  const [lng, setLng]           = useStateB("");
  const [benef, setBenef]       = useStateB("");
  const [staff, setStaff]       = useStateB("");
  const [status, setStatus]     = useStateB("ok");
  const [submitting, setSubmitting] = useStateB(false);
  const [err, setErr]           = useStateB(null);

  const submit = async (e) => {
    e.preventDefault();
    setErr(null); setSubmitting(true);
    try {
      const coords = (lat && lng) ? { lat: parseFloat(lat), lng: parseFloat(lng) } : null;
      await window.melr.createSite(project, {
        code:          code.trim(),
        name:          name.trim(),
        kind:          kind.trim(),
        region:        region.trim(),
        country_iso2:  country.trim(),
        beneficiaries: benef === "" ? null : Number(benef),
        staff_count:   staff === "" ? null : Number(staff),
        status,
        coords,
      });
      if (onCreated) await onCreated();
      onClose();
    } catch (e2) {
      setErr(e2.message);
    } finally {
      setSubmitting(false);
    }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13 };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 520, maxWidth: "92vw",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Nouveau site" : "New site"}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Code (ex: S-DKR-06)" : "Code (e.g. S-DKR-06)"}</span>
            <input required value={code} onChange={(e) => setCode(e.target.value)} placeholder="S-DKR-06" style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Projet" : "Project"}</span>
            <select value={project} onChange={(e) => setProject(e.target.value)} style={inp}>
              <option value="P-001">P-001</option>
              <option value="P-002">P-002</option>
            </select>
          </label>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Nom du site" : "Site name"}</span>
          <input required value={name} onChange={(e) => setName(e.target.value)} placeholder={lang === "fr" ? "CSCom de Rufisque" : "Rufisque health center"} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Type" : "Kind"}</span>
            <input value={kind} onChange={(e) => setKind(e.target.value)} placeholder={lang === "fr" ? "Centre santé" : "Health center"} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Région" : "Region"}</span>
            <input value={region} onChange={(e) => setRegion(e.target.value)} placeholder="Dakar" style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Pays (ISO2)" : "Country (ISO2)"}</span>
            <input value={country} onChange={(e) => setCountry(e.target.value)} placeholder="SN" maxLength={2} style={inp} />
          </label>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Latitude (ex: 14.7333)" : "Latitude (e.g. 14.7333)"}</span>
            <input type="number" step="any" value={lat} onChange={(e) => setLat(e.target.value)} placeholder="14.7333" style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Longitude (ex: -17.3667)" : "Longitude (e.g. -17.3667)"}</span>
            <input type="number" step="any" value={lng} onChange={(e) => setLng(e.target.value)} placeholder="-17.3667" style={inp} />
          </label>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Bénéficiaires" : "Beneficiaries"}</span>
            <input type="number" value={benef} onChange={(e) => setBenef(e.target.value)} placeholder="12500" style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Personnel" : "Staff"}</span>
            <input type="number" value={staff} onChange={(e) => setStaff(e.target.value)} placeholder="8" style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Statut" : "Status"}</span>
            <select value={status} onChange={(e) => setStatus(e.target.value)} style={inp}>
              <option value="ok">OK</option>
              <option value="warn">{lang === "fr" ? "Attention" : "Warning"}</option>
              <option value="bad">{lang === "fr" ? "Critique" : "Critical"}</option>
            </select>
          </label>
        </div>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4 }}>
          <button type="button" onClick={onClose} disabled={submitting}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={submitting}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {submitting ? "…" : (lang === "fr" ? "Créer" : "Create")}
          </button>
        </div>
      </form>
    </div>
  );
}

function BaselineMap({ sites, selected, setSelected, sel, lang }) {
  return (
    <>
      <div className="map-wrap">
        <svg viewBox="0 0 480 360" preserveAspectRatio="xMidYMid meet">
          <defs>
            <pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
              <path d="M 20 0 L 0 0 0 20" fill="none" stroke="var(--line-faint)" strokeWidth="0.4" />
            </pattern>
          </defs>
          <rect width="480" height="360" fill="url(#grid)" />
          {/* Stylized regional outlines for Sahel zone */}
          <path className="map-region has-sites" d="M 30 200 Q 20 230 35 270 L 80 290 L 130 285 L 165 295 L 175 270 L 150 230 L 130 215 L 90 200 Z" />
          <path className="map-region has-sites" d="M 130 215 L 150 230 L 175 270 L 215 285 L 235 270 L 230 245 L 200 215 L 165 195 L 145 200 Z" />
          <path className="map-region has-sites" d="M 165 195 L 200 215 L 230 245 L 255 250 L 290 250 L 295 220 L 285 195 L 245 175 L 210 175 L 180 180 Z" />
          <path className="map-region has-sites" d="M 245 175 L 285 195 L 295 220 L 340 230 L 350 200 L 340 175 L 310 155 L 270 155 Z" />
          <path className="map-region" d="M 30 200 L 90 200 L 145 200 L 180 180 L 210 175 L 270 155 L 310 155 L 350 145 L 380 130 L 380 90 L 340 80 L 300 75 L 250 70 L 200 75 L 150 90 L 100 110 L 60 140 L 30 170 Z" />
          <path className="map-region" d="M 30 200 L 30 170 L 30 290 L 80 320 L 165 295 L 130 285 L 80 290 L 35 270 Z" opacity="0.4" />
          <path className="map-region" d="M 175 270 L 215 285 L 235 270 L 255 250 L 290 250 L 340 230 L 380 250 L 380 290 L 340 310 L 280 320 L 220 320 L 180 300 Z" opacity="0.4" />

          {/* Country labels */}
          <text x="60" y="240" fontSize="9" fill="var(--text-faint)" fontFamily="var(--font-mono)" letterSpacing="0.1em">MALI</text>
          <text x="170" y="265" fontSize="9" fill="var(--text-faint)" fontFamily="var(--font-mono)" letterSpacing="0.1em">B. FASO</text>
          <text x="245" y="225" fontSize="9" fill="var(--text-faint)" fontFamily="var(--font-mono)" letterSpacing="0.1em">NIGER</text>
          <text x="320" y="195" fontSize="9" fill="var(--text-faint)" fontFamily="var(--font-mono)" letterSpacing="0.1em">TCHAD</text>

          {/* Pins */}
          {sites.map((s) => {
            const color = s.status === "ok" ? "var(--green)" : s.status === "warn" ? "var(--amber)" : "var(--red)";
            const isSel = s.id === selected;
            const r = isSel ? 7 : 5;
            return (
              <g key={s.id} className={"map-pin" + (isSel ? " selected" : "")} onClick={() => setSelected(s.id)} transform={`translate(${s.x}, ${s.y})`}>
                {isSel && <circle r="14" fill={color} opacity="0.18" />}
                <circle r={r} fill={color} stroke="white" strokeWidth="1.5" />
                {isSel && <text y="-12" textAnchor="middle" fontSize="9" fontWeight="600" fill="var(--text)">{s.name}</text>}
              </g>
            );
          })}
        </svg>

        <div className="map-overlay">
          <div className="row" style={{ gap: 6, fontWeight: 500 }}><Icon.map /> P-241 · Sahel</div>
          <div className="text-faint">42 {lang === "fr" ? "sites · 3 pays · 412k bénéficiaires" : "sites · 3 countries · 412k beneficiaries"}</div>
        </div>

        <div className="map-zoom">
          <button><Icon.plus2 /></button>
          <button><svg className="i" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M5 12h14" /></svg></button>
          <button><Icon.target /></button>
        </div>

        <div className="site-list">
          {sites.map((s) => (
            <div key={s.id} className={"item" + (s.id === selected ? " selected" : "")} onClick={() => setSelected(s.id)}>
              <div className="row" style={{ gap: 6 }}>
                <span style={{
                  width: 6, height: 6, borderRadius: "50%",
                  background: s.status === "ok" ? "var(--green)" : s.status === "warn" ? "var(--amber)" : "var(--red)"
                }}></span>
                <b style={{ fontWeight: 500 }}>{s.name}</b>
              </div>
              <div className="meta">
                <span className="tag-mono">{s.id}</span>
                · {s.region} · {s.country}
              </div>
            </div>
          ))}
        </div>

        <div className="map-legend">
          <div style={{ fontWeight: 500, marginBottom: 4 }}>{lang === "fr" ? "Statut baseline" : "Baseline status"}</div>
          <div className="lg-row"><span className="lg-dot" style={{ background: "var(--green)" }}></span>{lang === "fr" ? "Données complètes" : "Complete"}</div>
          <div className="lg-row"><span className="lg-dot" style={{ background: "var(--amber)" }}></span>{lang === "fr" ? "Partielles · à compléter" : "Partial"}</div>
          <div className="lg-row"><span className="lg-dot" style={{ background: "var(--red)" }}></span>{lang === "fr" ? "Critiques · accès difficile" : "Critical · access"}</div>
        </div>
      </div>

      {sel && (
        <div className="grid cols-2" style={{ marginTop: 16 }}>
          <div className="card">
            <div className="card-head">
              <div className="card-title">{sel.name}</div>
              <span className="tag-mono">{sel.id}</span>
              <span className="pill" style={{ marginLeft: 6 }}>{sel.country}</span>
              <span className={"pill " + (sel.status === "ok" ? "green" : sel.status === "warn" ? "amber" : "red") + " dot"} style={{ marginLeft: "auto" }}>
                {sel.status === "ok" ? (lang === "fr" ? "Complet" : "Complete") : sel.status === "warn" ? (lang === "fr" ? "Partiel" : "Partial") : (lang === "fr" ? "Critique" : "Critical")}
              </span>
            </div>
            <div className="card-body">
              <div className="grid cols-2" style={{ gap: 10 }}>
                <div>
                  <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Bénéficiaires" : "Beneficiaries"}</div>
                  <div className="mono" style={{ fontSize: 18, fontWeight: 500 }}>{sel.beneficiaries.toLocaleString("fr-FR")}</div>
                </div>
                <div>
                  <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Personnel" : "Staff"}</div>
                  <div className="mono" style={{ fontSize: 18, fontWeight: 500 }}>{sel.staff}</div>
                </div>
                <div>
                  <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>CPN ≥4</div>
                  <div className="mono" style={{ fontSize: 18, fontWeight: 500 }}>{(sel.cpn * 100).toFixed(0)}%</div>
                  <div className="progress-bar" style={{ marginTop: 4 }}>
                    <div className="fill" style={{ width: (sel.cpn * 100) + "%" }}></div>
                  </div>
                </div>
                <div>
                  <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>DTC3</div>
                  <div className="mono" style={{ fontSize: 18, fontWeight: 500 }}>{(sel.dtc3 * 100).toFixed(0)}%</div>
                  <div className="progress-bar" style={{ marginTop: 4 }}>
                    <div className="fill" style={{ width: (sel.dtc3 * 100) + "%" }}></div>
                  </div>
                </div>
              </div>
              <div className="divider"></div>
              <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 6 }}>
                {lang === "fr" ? "Infrastructure baseline" : "Baseline infrastructure"}
              </div>
              <div className="row" style={{ gap: 4, flexWrap: "wrap" }}>
                {(lang === "fr"
                  ? ["Bâtiment principal", "Maternité", "Pharmacie", "Laboratoire", "Salle vaccination", "Chaîne du froid"]
                  : ["Main building", "Maternity", "Pharmacy", "Lab", "Vaccination room", "Cold chain"]
                ).map((x, i) => (
                  <span key={i} className="pill" style={{ background: i < 4 ? "var(--green-soft)" : "var(--bg-sunken)", color: i < 4 ? "var(--green)" : "var(--text-muted)", borderColor: i < 4 ? "oklch(from var(--green) calc(l + 0.30) c h)" : "var(--line)" }}>
                    {i < 4 && <Icon.check className="sm" />}
                    {x}
                  </span>
                ))}
              </div>
            </div>
          </div>

          <div className="card">
            <div className="card-head">
              <div className="card-title">{lang === "fr" ? "Compte d'exploitation actuel (annuel)" : "Current operating account (annual)"}</div>
              <span className="tag-mono">FCFA · 1000</span>
            </div>
            <div className="card-body flush">
              <table className="tbl">
                <thead><tr><th>{lang === "fr" ? "Poste" : "Item"}</th><th className="num">2024</th><th className="num">2025</th><th className="num">Δ</th></tr></thead>
                <tbody>
                  {[
                    { l: lang === "fr" ? "Recettes consultations" : "Consultation revenue", a: 18420, b: 19150, type: "in" },
                    { l: lang === "fr" ? "Subventions État" : "Government subsidy", a: 32100, b: 32100, type: "in" },
                    { l: lang === "fr" ? "Recettes pharmacie" : "Pharmacy revenue", a: 12400, b: 13800, type: "in" },
                    { l: lang === "fr" ? "Total recettes" : "Total revenue", a: 62920, b: 65050, total: true },
                    { l: lang === "fr" ? "Salaires" : "Salaries", a: -28800, b: -29400, type: "out" },
                    { l: lang === "fr" ? "Médicaments / consommables" : "Drugs / consumables", a: -16200, b: -17500, type: "out" },
                    { l: lang === "fr" ? "Maintenance & énergie" : "Maintenance & energy", a: -4800, b: -5100, type: "out" },
                    { l: lang === "fr" ? "Total charges" : "Total expenses", a: -49800, b: -52000, total: true },
                    { l: lang === "fr" ? "Résultat d'exploitation" : "Operating result", a: 13120, b: 13050, total: true, bold: true },
                  ].map((r, i) => (
                    <tr key={i} style={r.total ? { background: "var(--bg-sunken)" } : null}>
                      <td className={r.bold ? "strong" : ""}>{r.l}</td>
                      <td className="num">{r.a.toLocaleString("fr-FR")}</td>
                      <td className="num">{r.b.toLocaleString("fr-FR")}</td>
                      <td className={"num " + (r.b > r.a ? "" : "muted")}>{((r.b - r.a) / Math.abs(r.a) * 100).toFixed(1)}%</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

// ==================== LEAFLET-BASED MAP (live database sites) ====================
function LeafletBaselineMap({ sites, selected, setSelected, sel, lang }) {
  const mapRef     = useRefB(null);   // DOM node for the map container
  const leafletRef = useRefB(null);   // Leaflet map instance
  const markersRef = useRefB({});     // siteId -> L.Marker

  const STATUS_COLOR = { ok: "#16a34a", warn: "#d97706", risk: "#dc2626", bad: "#dc2626" };

  // Initialize the map once (after the container is in the DOM).
  useEffectB(() => {
    if (!mapRef.current || leafletRef.current || typeof L === "undefined") {
      console.log("[MELR] LeafletBaselineMap init skipped — mapRef:", !!mapRef.current,
                  "alreadyInit:", !!leafletRef.current, "Leaflet loaded:", typeof L !== "undefined");
      return;
    }
    const map = L.map(mapRef.current, {
      center: [14.7, -15.5],
      zoom: 7,
      zoomControl: true,
      scrollWheelZoom: true,
      worldCopyJump: false,
      // Constrain panning to Africa + a margin so the user can't drag
      // the map off into infinite world copies.
      maxBounds: L.latLngBounds([-40, -25], [40, 55]),
      maxBoundsViscosity: 1.0,
      minZoom: 3,
    });
    L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
      attribution: "© OpenStreetMap",
      maxZoom: 19,
      minZoom: 3,
      noWrap: true, // don't repeat tiles horizontally
      bounds: L.latLngBounds([-90, -180], [90, 180]),
    }).addTo(map);
    leafletRef.current = map;
    console.log("[MELR] LeafletBaselineMap initialized");
    return () => { map.remove(); leafletRef.current = null; };
  }, []);

  // Rebuild markers whenever the site list changes.
  useEffectB(() => {
    const map = leafletRef.current;
    if (!map) { console.log("[MELR] markers effect: map not ready"); return; }
    Object.values(markersRef.current).forEach((m) => map.removeLayer(m));
    markersRef.current = {};
    const valid = sites.filter((s) => typeof s.lat === "number" && typeof s.lng === "number");
    console.log("[MELR] markers effect: adding", valid.length, "markers");
    valid.forEach((s) => {
      const color = STATUS_COLOR[s.status] || "#6b7280";
      const m = L.circleMarker([s.lat, s.lng], {
        radius: 9, color: "#fff", weight: 2, fillColor: color, fillOpacity: 0.95,
      }).addTo(map);
      m.bindTooltip(`<b>${s.name}</b><br>${s.region} · ${s.country}`, { direction: "top", offset: [0, -8] });
      m.on("click", () => setSelected(s.id));
      markersRef.current[s.id] = m;
    });
    if (valid.length) {
      const bounds = L.latLngBounds(valid.map((s) => [s.lat, s.lng]));
      map.fitBounds(bounds, { padding: [40, 40], maxZoom: 11 });
    }
  }, [sites]);

  // Highlight the selected marker.
  useEffectB(() => {
    Object.entries(markersRef.current).forEach(([id, m]) => {
      m.setStyle({ radius: id === selected ? 13 : 9, weight: id === selected ? 3 : 2 });
      if (id === selected) m.bringToFront();
    });
  }, [selected]);

  return (
    <>
      <div className="map-wrap" style={{ position: "relative" }}>
        <div ref={mapRef} style={{ width: "100%", height: 480, borderRadius: 8, overflow: "hidden" }}></div>

        <div className="map-overlay">
          <div className="row" style={{ gap: 6, fontWeight: 500 }}><Icon.map /> {lang === "fr" ? "Sites portefeuille" : "Portfolio sites"}</div>
          <div className="text-faint">{sites.length} {lang === "fr" ? "sites · données live" : "sites · live data"}</div>
        </div>

        <div className="site-list">
          {sites.map((s) => (
            <div key={s.id} className={"item" + (s.id === selected ? " selected" : "")} onClick={() => setSelected(s.id)}>
              <div className="row" style={{ gap: 6 }}>
                <span style={{ width: 6, height: 6, borderRadius: "50%", background: STATUS_COLOR[s.status] || "#6b7280" }}></span>
                <b style={{ fontWeight: 500 }}>{s.name}</b>
              </div>
              <div className="meta">
                <span className="tag-mono">{s.id}</span> · {s.region} · {s.country}
                {s.project && <span className="tag-mono" style={{ marginLeft: 6 }}>{s.project}</span>}
              </div>
            </div>
          ))}
        </div>

        <div className="map-legend">
          <div style={{ fontWeight: 500, marginBottom: 4 }}>{lang === "fr" ? "Statut baseline" : "Baseline status"}</div>
          <div className="lg-row"><span className="lg-dot" style={{ background: STATUS_COLOR.ok }}></span>{lang === "fr" ? "Données complètes" : "Complete"}</div>
          <div className="lg-row"><span className="lg-dot" style={{ background: STATUS_COLOR.warn }}></span>{lang === "fr" ? "Partielles · à compléter" : "Partial"}</div>
          <div className="lg-row"><span className="lg-dot" style={{ background: STATUS_COLOR.bad }}></span>{lang === "fr" ? "Critiques · accès difficile" : "Critical · access"}</div>
        </div>
      </div>

      {sel && (
        <div className="grid cols-2" style={{ marginTop: 16 }}>
          <div className="card">
            <div className="card-head">
              <div className="card-title">{sel.name}</div>
              <span className="tag-mono">{sel.id}</span>
              <span className="pill" style={{ marginLeft: 6 }}>{sel.country}</span>
              <span className={"pill " + (sel.status === "ok" ? "green" : sel.status === "warn" ? "amber" : "red") + " dot"} style={{ marginLeft: "auto" }}>
                {sel.status === "ok" ? (lang === "fr" ? "Complet" : "Complete") : sel.status === "warn" ? (lang === "fr" ? "Partiel" : "Partial") : (lang === "fr" ? "Critique" : "Critical")}
              </span>
            </div>
            <div className="card-body">
              <div className="grid cols-2" style={{ gap: 10 }}>
                <div>
                  <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Bénéficiaires" : "Beneficiaries"}</div>
                  <div className="mono" style={{ fontSize: 18, fontWeight: 500 }}>{(sel.beneficiaries || 0).toLocaleString("fr-FR")}</div>
                </div>
                <div>
                  <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Personnel" : "Staff"}</div>
                  <div className="mono" style={{ fontSize: 18, fontWeight: 500 }}>{sel.staff || 0}</div>
                </div>
                <div>
                  <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Région" : "Region"}</div>
                  <div className="mono" style={{ fontSize: 14 }}>{sel.region}</div>
                </div>
                <div>
                  <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Projet" : "Project"}</div>
                  <div className="mono" style={{ fontSize: 14 }}>{sel.project || "—"}</div>
                </div>
              </div>
            </div>
          </div>
          <div className="card">
            <div className="card-head"><div className="card-title">{lang === "fr" ? "Coordonnées GPS" : "GPS coordinates"}</div></div>
            <div className="card-body" style={{ fontSize: 12 }}>
              <div className="text-faint">{lang === "fr" ? "Latitude" : "Latitude"}</div>
              <div className="mono" style={{ fontSize: 14, marginBottom: 8 }}>{typeof sel.lat === "number" ? sel.lat.toFixed(4) : "—"}</div>
              <div className="text-faint">{lang === "fr" ? "Longitude" : "Longitude"}</div>
              <div className="mono" style={{ fontSize: 14 }}>{typeof sel.lng === "number" ? sel.lng.toFixed(4) : "—"}</div>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

// ──────────────────────────────────────────────────────────────────────────
// ACTEURS (stakeholders) — operational CRUD
// ──────────────────────────────────────────────────────────────────────────
const STAKEHOLDER_TYPES = [
  { v: "supervisory",    fr: "Tutelle",          en: "Supervisory" },
  { v: "implementation", fr: "Mise en œuvre",    en: "Implementation" },
  { v: "beneficiary",    fr: "Bénéficiaire",     en: "Beneficiary" },
  { v: "donor",          fr: "Cofinancement",    en: "Donor" },
  { v: "supplier",       fr: "Fournisseur",      en: "Supplier" },
  { v: "other",          fr: "Autre",            en: "Other" },
];
function stakeholderTypeLabel(v, lang) {
  const t = STAKEHOLDER_TYPES.find((x) => x.v === v);
  return t ? (lang === "fr" ? t.fr : t.en) : v;
}

function BaselineActors({ lang, projectId }) {
  const { data: rows, loading, realtime } = window.melr.useStakeholders(projectId);
  const LiveBadge = window.melr.LiveBadge;
  const [modalOpen, setModalOpen] = useStateB(false);
  const [editing, setEditing]     = useStateB(null);
  const [err, setErr]             = useStateB(null);

  if (!projectId) {
    return <div className="card"><div className="card-body" style={{ padding: 40, textAlign: "center", color: "var(--text-faint)" }}>
      {lang === "fr" ? "Sélectionnez un projet ci-dessus." : "Pick a project above."}
    </div></div>;
  }

  // Map the live shape onto the visual one expected by PowerInterest
  const actors = (rows || []).map((r) => ({
    id: r.id, n: r.name, role: r.role_label || stakeholderTypeLabel(r.type, lang),
    type: r.type, power: r.power || 0, interest: r.interest || 0, notes: r.notes,
  }));

  const onDelete = async (id) => {
    if (!confirm(lang === "fr" ? "Supprimer cet acteur ?" : "Delete this stakeholder?")) return;
    try { await window.melr.stakeholdersCrud.remove(id); }
    catch (e) { setErr(e.message); }
  };

  return (
    <>
      <div className="grid" style={{ gridTemplateColumns: "1.2fr 1fr", gap: 16 }}>
        <div className="card">
          <div className="card-head">
            <div className="card-title">
              {lang === "fr" ? "Cartographie des acteurs" : "Stakeholder mapping"}
              {" "}<LiveBadge on={realtime} lang={lang} />
            </div>
            <span className="pill">{actors.length}</span>
            <button className="btn sm primary" style={{ marginLeft: "auto" }}
              onClick={() => { setEditing(null); setModalOpen(true); }}>
              <Icon.plus /> {lang === "fr" ? "Ajouter" : "Add"}
            </button>
          </div>
          {err && <div style={{ padding: "8px 14px", color: "#b91c1c", fontSize: 12 }}>{err}</div>}
          <div className="card-body flush">
            {loading && <div style={{ padding: 18, color: "var(--text-faint)" }}>{lang === "fr" ? "Chargement…" : "Loading…"}</div>}
            {!loading && actors.length === 0 && (
              <div style={{ padding: 22, textAlign: "center", color: "var(--text-faint)", fontSize: 13 }}>
                {lang === "fr" ? "Aucun acteur enregistré. Cliquez « Ajouter » pour commencer." : "No stakeholder recorded. Click 'Add' to start."}
              </div>
            )}
            {actors.length > 0 && (
              <table className="tbl">
                <thead>
                  <tr>
                    <th>{lang === "fr" ? "Acteur" : "Stakeholder"}</th>
                    <th>{lang === "fr" ? "Rôle" : "Role"}</th>
                    <th className="num">{lang === "fr" ? "Pouvoir" : "Power"}</th>
                    <th className="num">{lang === "fr" ? "Intérêt" : "Interest"}</th>
                    <th>{lang === "fr" ? "Stratégie" : "Strategy"}</th>
                    <th style={{ width: 80 }}></th>
                  </tr>
                </thead>
                <tbody>
                  {actors.map((a) => (
                    <tr key={a.id}>
                      <td className="strong">{a.n}</td>
                      <td className="muted">{a.role}</td>
                      <td className="num">{a.power || "—"}</td>
                      <td className="num">{a.interest || "—"}</td>
                      <td>
                        {a.power >= 4 && a.interest >= 4 && <span className="pill accent">{lang === "fr" ? "Gérer de près" : "Manage closely"}</span>}
                        {a.power >= 4 && a.interest < 4 && <span className="pill amber">{lang === "fr" ? "Satisfaire" : "Keep satisfied"}</span>}
                        {a.power < 4 && a.interest >= 4 && <span className="pill green">{lang === "fr" ? "Informer" : "Keep informed"}</span>}
                        {a.power < 4 && a.interest < 4 && <span className="pill">{lang === "fr" ? "Surveiller" : "Monitor"}</span>}
                      </td>
                      <td>
                        <div style={{ display: "flex", gap: 4 }}>
                          <button className="btn sm ghost" title={lang === "fr" ? "Modifier" : "Edit"}
                            onClick={() => { setEditing(a); setModalOpen(true); }}>
                            <Icon.edit />
                          </button>
                          <button className="btn sm ghost" title={lang === "fr" ? "Supprimer" : "Delete"}
                            onClick={() => onDelete(a.id)}>
                            <Icon.trash />
                          </button>
                        </div>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </div>
        </div>
        <div className="card">
          <div className="card-head">
            <div className="card-title">{lang === "fr" ? "Matrice Pouvoir / Intérêt" : "Power / Interest matrix"}</div>
          </div>
          <div className="card-body">
            <PowerInterest actors={actors} lang={lang} />
          </div>
        </div>
      </div>

      {modalOpen && (
        <StakeholderModal
          lang={lang}
          projectId={projectId}
          editing={editing}
          onClose={() => setModalOpen(false)}
        />
      )}
    </>
  );
}

// Edit / create stakeholder
function StakeholderModal({ lang, projectId, editing, onClose }) {
  const [name, setName]         = useStateB(editing ? editing.n : "");
  const [type, setType]         = useStateB(editing ? editing.type : "other");
  const [role, setRole]         = useStateB(editing ? (editing.role || "") : "");
  const [power, setPower]       = useStateB(editing ? (editing.power || 3) : 3);
  const [interest, setInterest] = useStateB(editing ? (editing.interest || 3) : 3);
  const [notes, setNotes]       = useStateB(editing ? (editing.notes || "") : "");
  const [busy, setBusy]         = useStateB(false);
  const [err, setErr]           = useStateB(null);

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { display: "block", fontSize: 11, color: "var(--text-faint)", marginBottom: 4, textTransform: "uppercase", letterSpacing: "0.04em", marginTop: 10 };

  const onSubmit = async (e) => {
    e.preventDefault();
    if (!name.trim()) return;
    setBusy(true); setErr(null);
    try {
      const payload = {
        name: name.trim(),
        type,
        role_label: role.trim() || null,
        power: parseInt(power, 10) || null,
        interest: parseInt(interest, 10) || null,
        notes: notes.trim() || null,
      };
      if (editing) await window.melr.stakeholdersCrud.update(editing.id, payload);
      else          await window.melr.stakeholdersCrud.create(projectId, payload);
      onClose();
    } catch (e) { setErr(e.message); }
    finally { setBusy(false); }
  };

  return (
    <div onClick={() => !busy && onClose()} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.5)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={onSubmit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 520, maxWidth: "92vw",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)",
      }}>
        <div style={{ fontSize: 17, fontWeight: 600, marginBottom: 4 }}>
          {editing ? (lang === "fr" ? "Modifier l'acteur" : "Edit stakeholder") : (lang === "fr" ? "Nouvel acteur" : "New stakeholder")}
        </div>

        <label style={lbl}>{lang === "fr" ? "Nom" : "Name"} *</label>
        <input autoFocus required style={inp} value={name} onChange={(e) => setName(e.target.value)} />

        <label style={lbl}>{lang === "fr" ? "Type" : "Type"}</label>
        <select style={inp} value={type} onChange={(e) => setType(e.target.value)}>
          {STAKEHOLDER_TYPES.map((s) => <option key={s.v} value={s.v}>{lang === "fr" ? s.fr : s.en}</option>)}
        </select>

        <label style={lbl}>{lang === "fr" ? "Rôle détaillé (optionnel)" : "Detailed role (optional)"}</label>
        <input style={inp} value={role} onChange={(e) => setRole(e.target.value)} />

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <div>
            <label style={lbl}>{lang === "fr" ? "Pouvoir (1-5)" : "Power (1-5)"}</label>
            <input type="number" min={1} max={5} style={inp} value={power} onChange={(e) => setPower(e.target.value)} />
          </div>
          <div>
            <label style={lbl}>{lang === "fr" ? "Intérêt (1-5)" : "Interest (1-5)"}</label>
            <input type="number" min={1} max={5} style={inp} value={interest} onChange={(e) => setInterest(e.target.value)} />
          </div>
        </div>

        <label style={lbl}>{lang === "fr" ? "Notes" : "Notes"}</label>
        <textarea style={{ ...inp, minHeight: 60, resize: "vertical", fontFamily: "inherit" }}
          value={notes} onChange={(e) => setNotes(e.target.value)} />

        {err && <div style={{ color: "#b91c1c", fontSize: 12, marginTop: 10 }}>{err}</div>}

        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (editing ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Ajouter" : "Add"))}
          </button>
        </div>
      </form>
    </div>
  );
}

function PowerInterest({ actors, lang }) {
  const W = 360, H = 280, padL = 36, padR = 16, padT = 16, padB = 30;
  const xs = (v) => padL + ((v - 1) / 4) * (W - padL - padR);
  const ys = (v) => padT + (1 - (v - 1) / 4) * (H - padT - padB);
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%" }}>
      <line x1={padL} y1={(padT + H - padB) / 2 + padT / 2} x2={W - padR} y2={(padT + H - padB) / 2 + padT / 2} stroke="var(--line-strong)" strokeDasharray="3 3" />
      <line x1={(padL + W - padR) / 2} y1={padT} x2={(padL + W - padR) / 2} y2={H - padB} stroke="var(--line-strong)" strokeDasharray="3 3" />
      <text x={padL + 6} y={padT + 12} fontSize="9" fill="var(--text-faint)">{lang === "fr" ? "Satisfaire" : "Keep satisfied"}</text>
      <text x={W - padR - 6} y={padT + 12} fontSize="9" fill="var(--text-faint)" textAnchor="end">{lang === "fr" ? "Gérer de près" : "Manage closely"}</text>
      <text x={padL + 6} y={H - padB - 4} fontSize="9" fill="var(--text-faint)">{lang === "fr" ? "Surveiller" : "Monitor"}</text>
      <text x={W - padR - 6} y={H - padB - 4} fontSize="9" fill="var(--text-faint)" textAnchor="end">{lang === "fr" ? "Informer" : "Keep informed"}</text>
      <text x={padL - 6} y={padT - 4} fontSize="10" fill="var(--text-muted)">↑ {lang === "fr" ? "Pouvoir" : "Power"}</text>
      <text x={W - padR} y={H - 6} fontSize="10" fill="var(--text-muted)" textAnchor="end">{lang === "fr" ? "Intérêt" : "Interest"} →</text>
      {actors.map((a, i) => {
        const cx = xs(a.interest) + (Math.sin(i * 1.7) * 8);
        const cy = ys(a.power) + (Math.cos(i * 1.3) * 8);
        return (
          <g key={i}>
            <circle cx={cx} cy={cy} r="6" fill="var(--accent)" opacity="0.85" />
            <text x={cx + 9} y={cy + 3} fontSize="9.5" fill="var(--text)">{a.n.split(" ").slice(0, 3).join(" ").slice(0, 22)}</text>
          </g>
        );
      })}
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────────────────
// INFRASTRUCTURE (baseline_infrastructure) — operational matrix CRUD
// ──────────────────────────────────────────────────────────────────────────
const INFRA_CONDITIONS = [
  { v: "",        fr: "—",       en: "—",       pill: "" },
  { v: "good",    fr: "Bon",     en: "Good",    pill: "green" },
  { v: "partial", fr: "Partiel", en: "Partial", pill: "amber" },
  { v: "aged",    fr: "Vétuste", en: "Aged",    pill: "amber" },
  { v: "missing", fr: "Absent",  en: "Missing", pill: "red" },
  { v: "na",      fr: "N/A",     en: "N/A",     pill: "" },
];
const DEFAULT_INFRA_CATEGORIES_FR = ["Bâtiment", "Eau", "Électricité", "Pharmacie", "Laboratoire", "Chaîne froid"];
const DEFAULT_INFRA_CATEGORIES_EN = ["Building", "Water", "Electricity", "Pharmacy", "Lab", "Cold chain"];

function BaselineInfra({ lang, projectId }) {
  const { data: rows, loading, realtime } = window.melr.useBaselineInfrastructure(projectId);
  const { data: sites }                   = window.melr.useSites(projectId);
  const LiveBadge = window.melr.LiveBadge;
  const [busyCell, setBusyCell] = useStateB(null);  // "siteId/cat" while saving
  const [err, setErr]           = useStateB(null);

  if (!projectId) {
    return <div className="card"><div className="card-body" style={{ padding: 40, textAlign: "center", color: "var(--text-faint)" }}>
      {lang === "fr" ? "Sélectionnez un projet ci-dessus." : "Pick a project above."}
    </div></div>;
  }

  // Columns = union of (default categories) + categories already in data
  const dataCategories = Array.from(new Set((rows || []).map((r) => r.category).filter(Boolean)));
  const defaults = lang === "fr" ? DEFAULT_INFRA_CATEGORIES_FR : DEFAULT_INFRA_CATEGORIES_EN;
  const categories = Array.from(new Set([...defaults, ...dataCategories]));

  // Build a lookup: { [site_id]: { [category]: row } }
  const lookup = {};
  (rows || []).forEach((r) => {
    if (!lookup[r.site_id]) lookup[r.site_id] = {};
    lookup[r.site_id][r.category] = r;
  });

  const setCondition = async (site, category, condition) => {
    const key = site.id + "/" + category;
    setBusyCell(key); setErr(null);
    try {
      const existing = lookup[site.id] && lookup[site.id][category];
      if (existing) {
        if (!condition && !existing.notes) {
          // Empty out → delete
          await window.melr.baselineInfrastructureCrud.remove(existing.id);
        } else {
          await window.melr.baselineInfrastructureCrud.update(existing.id, { condition: condition || null });
        }
      } else if (condition) {
        await window.melr.baselineInfrastructureCrud.create(projectId, {
          site_id: site.id,
          category,
          condition,
        });
      }
    } catch (e) { setErr(e.message); }
    finally { setBusyCell(null); }
  };

  const promptAddCategory = async () => {
    const name = window.prompt(lang === "fr"
      ? "Nom de la nouvelle catégorie d'infrastructure :"
      : "Name of the new infrastructure category:");
    if (!name || !name.trim()) return;
    // Add by creating a placeholder row for the first site
    if ((sites || []).length === 0) {
      alert(lang === "fr" ? "Ajoutez d'abord un site au projet." : "Add a site to the project first.");
      return;
    }
    try {
      await window.melr.baselineInfrastructureCrud.create(projectId, {
        site_id: sites[0].id, category: name.trim(), condition: null,
      });
    } catch (e) { setErr(e.message); }
  };

  const condCfg = (v) => INFRA_CONDITIONS.find((c) => c.v === (v || "")) || INFRA_CONDITIONS[0];

  return (
    <div className="card">
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Infrastructure baseline · matrice par site" : "Baseline infrastructure · site matrix"}
          {" "}<LiveBadge on={realtime} lang={lang} />
        </div>
        <span className="pill">{rows ? rows.length : 0} {lang === "fr" ? "cellules" : "cells"}</span>
        <button className="btn sm" style={{ marginLeft: "auto" }} onClick={promptAddCategory}>
          <Icon.plus /> {lang === "fr" ? "Catégorie" : "Category"}
        </button>
      </div>
      {err && <div style={{ padding: "8px 14px", color: "#b91c1c", fontSize: 12 }}>{err}</div>}
      <div className="card-body flush" style={{ overflowX: "auto" }}>
        {loading && <div style={{ padding: 18, color: "var(--text-faint)" }}>{lang === "fr" ? "Chargement…" : "Loading…"}</div>}
        {!loading && (sites || []).length === 0 && (
          <div style={{ padding: 22, textAlign: "center", color: "var(--text-faint)", fontSize: 13 }}>
            {lang === "fr" ? "Aucun site rattaché à ce projet. Créez d'abord des sites." : "No site attached to this project. Create sites first."}
          </div>
        )}
        {!loading && (sites || []).length > 0 && (
          <table className="tbl" style={{ minWidth: 800 }}>
            <thead>
              <tr>
                <th style={{ minWidth: 160 }}>{lang === "fr" ? "Site" : "Site"}</th>
                {categories.map((c) => <th key={c} style={{ minWidth: 120 }}>{c}</th>)}
              </tr>
            </thead>
            <tbody>
              {(sites || []).map((s) => (
                <tr key={s.id}>
                  <td>
                    <div className="strong" style={{ fontSize: 12.5 }}>{s.code}</div>
                    <div className="text-faint" style={{ fontSize: 10.5 }}>{s.name}</div>
                  </td>
                  {categories.map((c) => {
                    const existing = (lookup[s.id] && lookup[s.id][c]) || null;
                    const v = existing ? (existing.condition || "") : "";
                    const cfg = condCfg(v);
                    const busy = busyCell === (s.id + "/" + c);
                    return (
                      <td key={c}>
                        <select
                          value={v}
                          disabled={busy}
                          onChange={(e) => setCondition(s, c, e.target.value)}
                          className={cfg.pill ? "infra-cell pill " + cfg.pill + " dot" : "infra-cell"}
                          style={{
                            border: "1px solid var(--line)",
                            background: cfg.pill === "green" ? "#dcfce7"
                                       : cfg.pill === "amber" ? "#fef3c7"
                                       : cfg.pill === "red"   ? "#fee2e2"
                                       : "var(--bg, white)",
                            color: cfg.pill === "green" ? "#15803d"
                                  : cfg.pill === "amber" ? "#a16207"
                                  : cfg.pill === "red"   ? "#b91c1c"
                                  : "var(--text)",
                            padding: "4px 8px", borderRadius: 999, fontSize: 11, fontWeight: 500,
                            cursor: busy ? "wait" : "pointer", minWidth: 90,
                          }}>
                          {INFRA_CONDITIONS.map((cfg2) => (
                            <option key={cfg2.v} value={cfg2.v} style={{ color: "#111", background: "white" }}>
                              {lang === "fr" ? cfg2.fr : cfg2.en}
                            </option>
                          ))}
                        </select>
                      </td>
                    );
                  })}
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
      <div className="text-faint" style={{ fontSize: 11, padding: "8px 14px", borderTop: "1px solid var(--line-faint)" }}>
        {lang === "fr"
          ? "Astuce : modifier une cellule l'enregistre automatiquement. Sélectionnez « — » pour effacer."
          : "Tip: changing a cell auto-saves. Pick '—' to clear."}
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────────────
// COMPTES D'EXPLOITATION (baseline_ops_lines) — operational CRUD per site
// ──────────────────────────────────────────────────────────────────────────
function fmtAmount(v, lang) {
  if (v == null || v === "") return "—";
  const n = Number(v);
  if (!isFinite(n)) return "—";
  return n.toLocaleString(lang === "fr" ? "fr-FR" : "en-US", { maximumFractionDigits: 0 });
}

function BaselineOps({ lang, projectId }) {
  const { data: rows, loading, realtime } = window.melr.useBaselineOpsLines(projectId);
  const { data: sites }                   = window.melr.useSites(projectId);
  const LiveBadge = window.melr.LiveBadge;
  const [siteFilter, setSiteFilter] = useStateB("");  // "" = all sites
  const [modalOpen, setModalOpen]   = useStateB(false);
  const [editing, setEditing]       = useStateB(null);
  const [err, setErr]               = useStateB(null);

  if (!projectId) {
    return <div className="card"><div className="card-body" style={{ padding: 40, textAlign: "center", color: "var(--text-faint)" }}>
      {lang === "fr" ? "Sélectionnez un projet ci-dessus." : "Pick a project above."}
    </div></div>;
  }

  // Filter by site (or all)
  const filtered = (rows || []).filter((r) =>
    siteFilter === "" ? true : (siteFilter === "__none__" ? !r.site_id : r.site_id === siteFilter)
  );

  // Group by year desc, then split income / expense
  const byYear = new Map();
  filtered.forEach((r) => {
    const y = r.year || 0;
    if (!byYear.has(y)) byYear.set(y, []);
    byYear.get(y).push(r);
  });
  const years = Array.from(byYear.keys()).sort((a, b) => b - a);

  const totalIncomeByYear = {};
  const totalExpenseByYear = {};
  filtered.forEach((r) => {
    const y = r.year || 0;
    const amt = Number(r.amount) || 0;
    if (r.is_income) totalIncomeByYear[y] = (totalIncomeByYear[y] || 0) + amt;
    else             totalExpenseByYear[y] = (totalExpenseByYear[y] || 0) + amt;
  });

  const onDelete = async (id) => {
    if (!confirm(lang === "fr" ? "Supprimer cette ligne ?" : "Delete this line?")) return;
    try { await window.melr.baselineOpsLinesCrud.remove(id); }
    catch (e) { setErr(e.message); }
  };

  const siteName = (sid) => {
    if (!sid) return lang === "fr" ? "(projet)" : "(project)";
    const s = (sites || []).find((x) => x.id === sid);
    return s ? (s.code || s.name) : "—";
  };

  return (
    <>
      <div className="card">
        <div className="card-head">
          <div className="card-title">
            {lang === "fr" ? "Comptes d'exploitation baseline" : "Baseline operating accounts"}
            {" "}<LiveBadge on={realtime} lang={lang} />
          </div>
          <span className="pill">{filtered.length} {lang === "fr" ? "lignes" : "lines"}</span>
          <div style={{ flex: 1 }} />
          <span className="text-faint" style={{ fontSize: 11, marginRight: 6 }}>{lang === "fr" ? "Site" : "Site"}</span>
          <select value={siteFilter} onChange={(e) => setSiteFilter(e.target.value)}
            style={{ padding: "6px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 12, background: "var(--bg, white)", color: "var(--text)" }}>
            <option value="">{lang === "fr" ? "Tous sites" : "All sites"}</option>
            <option value="__none__">{lang === "fr" ? "(sans site / projet)" : "(no site / project)"}</option>
            {(sites || []).map((s) => <option key={s.id} value={s.id}>{s.code} — {s.name}</option>)}
          </select>
          <button className="btn sm primary" style={{ marginLeft: 8 }}
            onClick={() => { setEditing(null); setModalOpen(true); }}>
            <Icon.plus /> {lang === "fr" ? "Ajouter une ligne" : "Add line"}
          </button>
        </div>
        {err && <div style={{ padding: "8px 14px", color: "#b91c1c", fontSize: 12 }}>{err}</div>}

        <div className="card-body flush">
          {loading && <div style={{ padding: 18, color: "var(--text-faint)" }}>{lang === "fr" ? "Chargement…" : "Loading…"}</div>}
          {!loading && filtered.length === 0 && (
            <div style={{ padding: 22, textAlign: "center", color: "var(--text-faint)", fontSize: 13 }}>
              {lang === "fr" ? "Aucune ligne. Cliquez « Ajouter une ligne » pour commencer." : "No lines yet. Click 'Add line' to start."}
            </div>
          )}

          {years.map((y) => {
            const ylines = byYear.get(y);
            const income  = ylines.filter((r) => r.is_income);
            const expense = ylines.filter((r) => !r.is_income);
            return (
              <div key={y} style={{ borderBottom: "1px solid var(--line)", padding: "10px 14px" }}>
                <div style={{ fontWeight: 600, fontSize: 13.5, marginBottom: 6, color: "var(--text)" }}>
                  {lang === "fr" ? "Année" : "Year"} {y}
                  <span className="text-faint" style={{ fontWeight: 400, marginLeft: 10, fontSize: 11.5 }}>
                    {lang === "fr" ? "Résultat" : "Result"} :
                    {" "}<strong style={{ color: (totalIncomeByYear[y] || 0) + (totalExpenseByYear[y] || 0) >= 0 ? "#15803d" : "#b91c1c" }}>
                      {fmtAmount((totalIncomeByYear[y] || 0) + (totalExpenseByYear[y] || 0), lang)}
                    </strong>
                  </span>
                </div>
                <table className="tbl">
                  <thead>
                    <tr>
                      <th style={{ width: 90 }}>{lang === "fr" ? "Site" : "Site"}</th>
                      <th>{lang === "fr" ? "Catégorie" : "Category"}</th>
                      <th>{lang === "fr" ? "Libellé" : "Label"}</th>
                      <th>{lang === "fr" ? "Type" : "Type"}</th>
                      <th className="num">{lang === "fr" ? "Montant" : "Amount"}</th>
                      <th style={{ width: 80 }}></th>
                    </tr>
                  </thead>
                  <tbody>
                    {[...income, ...expense].map((r) => (
                      <tr key={r.id}>
                        <td className="tag-mono" style={{ fontSize: 10.5 }}>{siteName(r.site_id)}</td>
                        <td className="muted">{r.category}</td>
                        <td className="strong">{r.label}</td>
                        <td>
                          {r.is_income
                            ? <span className="pill green dot">{lang === "fr" ? "Recette" : "Revenue"}</span>
                            : <span className="pill amber dot">{lang === "fr" ? "Charge" : "Expense"}</span>}
                        </td>
                        <td className="num" style={{ color: r.is_income ? "#15803d" : "#b91c1c", fontWeight: 600 }}>
                          {fmtAmount(r.amount, lang)}
                        </td>
                        <td>
                          <div style={{ display: "flex", gap: 4 }}>
                            <button className="btn sm ghost" title={lang === "fr" ? "Modifier" : "Edit"}
                              onClick={() => { setEditing(r); setModalOpen(true); }}>
                              <Icon.edit />
                            </button>
                            <button className="btn sm ghost" title={lang === "fr" ? "Supprimer" : "Delete"}
                              onClick={() => onDelete(r.id)}>
                              <Icon.trash />
                            </button>
                          </div>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
                <div className="text-faint" style={{ fontSize: 11.5, marginTop: 4 }}>
                  {lang === "fr" ? "Total recettes" : "Total revenue"} : <strong style={{ color: "#15803d" }}>{fmtAmount(totalIncomeByYear[y] || 0, lang)}</strong>
                  {" · "}
                  {lang === "fr" ? "Total charges" : "Total expenses"} : <strong style={{ color: "#b91c1c" }}>{fmtAmount(totalExpenseByYear[y] || 0, lang)}</strong>
                </div>
              </div>
            );
          })}
        </div>
      </div>

      {modalOpen && (
        <OpsLineModal
          lang={lang}
          projectId={projectId}
          sites={sites || []}
          editing={editing}
          onClose={() => setModalOpen(false)}
        />
      )}
    </>
  );
}

// Edit / create operating line
function OpsLineModal({ lang, projectId, sites, editing, onClose }) {
  const [siteId, setSiteId]     = useStateB(editing ? (editing.site_id || "") : "");
  const [category, setCategory] = useStateB(editing ? (editing.category || "") : "");
  const [label, setLabel]       = useStateB(editing ? (editing.label || "") : "");
  const [year, setYear]         = useStateB(editing ? (editing.year || new Date().getFullYear()) : new Date().getFullYear());
  const [amount, setAmount]     = useStateB(editing ? (editing.amount || 0) : 0);
  const [isIncome, setIsIncome] = useStateB(editing ? !!editing.is_income : true);
  const [busy, setBusy]         = useStateB(false);
  const [err, setErr]           = useStateB(null);

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { display: "block", fontSize: 11, color: "var(--text-faint)", marginBottom: 4, textTransform: "uppercase", letterSpacing: "0.04em", marginTop: 10 };

  const onSubmit = async (e) => {
    e.preventDefault();
    if (!category.trim() || !label.trim()) return;
    setBusy(true); setErr(null);
    try {
      // Amount sign: keep absolute value, the is_income flag carries the meaning.
      const amt = Math.abs(parseFloat(amount) || 0);
      const payload = {
        site_id: siteId || null,
        category: category.trim(),
        label: label.trim(),
        year: parseInt(year, 10),
        amount: amt,
        is_income: !!isIncome,
      };
      if (editing) await window.melr.baselineOpsLinesCrud.update(editing.id, payload);
      else          await window.melr.baselineOpsLinesCrud.create(projectId, payload);
      onClose();
    } catch (e) { setErr(e.message); }
    finally { setBusy(false); }
  };

  return (
    <div onClick={() => !busy && onClose()} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.5)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={onSubmit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 540, maxWidth: "92vw",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)",
      }}>
        <div style={{ fontSize: 17, fontWeight: 600, marginBottom: 4 }}>
          {editing ? (lang === "fr" ? "Modifier la ligne" : "Edit line") : (lang === "fr" ? "Nouvelle ligne" : "New line")}
        </div>

        <label style={lbl}>{lang === "fr" ? "Site (optionnel)" : "Site (optional)"}</label>
        <select style={inp} value={siteId} onChange={(e) => setSiteId(e.target.value)}>
          <option value="">{lang === "fr" ? "— agrégé projet —" : "— project-aggregated —"}</option>
          {sites.map((s) => <option key={s.id} value={s.id}>{s.code} — {s.name}</option>)}
        </select>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <div>
            <label style={lbl}>{lang === "fr" ? "Catégorie" : "Category"} *</label>
            <input required style={inp} value={category} onChange={(e) => setCategory(e.target.value)}
              placeholder={lang === "fr" ? "Consultations, Salaires…" : "Consultations, Salaries…"} />
          </div>
          <div>
            <label style={lbl}>{lang === "fr" ? "Type" : "Type"}</label>
            <select style={inp} value={isIncome ? "1" : "0"} onChange={(e) => setIsIncome(e.target.value === "1")}>
              <option value="1">{lang === "fr" ? "Recette" : "Revenue"}</option>
              <option value="0">{lang === "fr" ? "Charge" : "Expense"}</option>
            </select>
          </div>
        </div>

        <label style={lbl}>{lang === "fr" ? "Libellé" : "Label"} *</label>
        <input required style={inp} value={label} onChange={(e) => setLabel(e.target.value)}
          placeholder={lang === "fr" ? "Ex : Subventions État" : "e.g. Government subsidy"} />

        <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 10 }}>
          <div>
            <label style={lbl}>{lang === "fr" ? "Année" : "Year"} *</label>
            <input type="number" required min={2000} max={2099} style={inp}
              value={year} onChange={(e) => setYear(e.target.value)} />
          </div>
          <div>
            <label style={lbl}>{lang === "fr" ? "Montant (positif)" : "Amount (positive)"} *</label>
            <input type="number" required step="any" min={0} style={inp}
              value={amount} onChange={(e) => setAmount(e.target.value)} />
          </div>
        </div>

        {err && <div style={{ color: "#b91c1c", fontSize: 12, marginTop: 10 }}>{err}</div>}

        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (editing ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Ajouter" : "Add"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────────────
// ISSUES (baseline_issues) — operational CRUD
// ──────────────────────────────────────────────────────────────────────────
const ISSUE_SEVERITIES = [
  { v: "high",   fr: "Haute",   en: "High",   pill: "red"   },
  { v: "medium", fr: "Moyenne", en: "Medium", pill: "amber" },
  { v: "low",    fr: "Faible",  en: "Low",    pill: ""      },
];
function severityCfg(v) { return ISSUE_SEVERITIES.find((s) => s.v === v) || ISSUE_SEVERITIES[2]; }

function BaselineIssues({ lang, projectId }) {
  const { data: rows, loading, realtime } = window.melr.useBaselineIssues(projectId);
  const LiveBadge = window.melr.LiveBadge;
  const [modalOpen, setModalOpen] = useStateB(false);
  const [editing, setEditing]     = useStateB(null);
  const [err, setErr]             = useStateB(null);

  if (!projectId) {
    return <div className="card"><div className="card-body" style={{ padding: 40, textAlign: "center", color: "var(--text-faint)" }}>
      {lang === "fr" ? "Sélectionnez un projet ci-dessus." : "Pick a project above."}
    </div></div>;
  }

  // Order: high → medium → low, then by created_at desc
  const sevOrder = { high: 0, medium: 1, low: 2 };
  const sorted = [...(rows || [])].sort((a, b) => {
    const so = (sevOrder[a.severity] || 9) - (sevOrder[b.severity] || 9);
    if (so !== 0) return so;
    return String(b.created_at || "").localeCompare(String(a.created_at || ""));
  });
  const highCount = sorted.filter((i) => i.severity === "high").length;

  const onDelete = async (id) => {
    if (!confirm(lang === "fr" ? "Supprimer ce problème ?" : "Delete this issue?")) return;
    try { await window.melr.baselineIssuesCrud.remove(id); }
    catch (e) { setErr(e.message); }
  };

  return (
    <>
      <div className="card">
        <div className="card-head">
          <div className="card-title">
            {lang === "fr" ? "Problèmes majeurs identifiés" : "Major issues identified"}
            {" "}<LiveBadge on={realtime} lang={lang} />
          </div>
          <span className="pill red">{highCount} {lang === "fr" ? "haute sévérité" : "high severity"}</span>
          <span className="pill" style={{ marginLeft: 6 }}>{sorted.length}</span>
          <button className="btn sm primary" style={{ marginLeft: "auto" }}
            onClick={() => { setEditing(null); setModalOpen(true); }}>
            <Icon.plus /> {lang === "fr" ? "Nouveau problème" : "New issue"}
          </button>
        </div>
        {err && <div style={{ padding: "8px 14px", color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div className="card-body flush">
          {loading && <div style={{ padding: 18, color: "var(--text-faint)" }}>{lang === "fr" ? "Chargement…" : "Loading…"}</div>}
          {!loading && sorted.length === 0 && (
            <div style={{ padding: 22, textAlign: "center", color: "var(--text-faint)", fontSize: 13 }}>
              {lang === "fr" ? "Aucun problème enregistré. Cliquez « Nouveau problème » pour commencer." : "No issue recorded. Click 'New issue' to start."}
            </div>
          )}
          {sorted.map((i) => {
            const cfg = severityCfg(i.severity);
            return (
              <div key={i.id} style={{
                padding: "12px 16px", borderBottom: "1px solid var(--line-faint)",
                display: "grid", gridTemplateColumns: "auto 1fr auto auto", gap: 12, alignItems: "center",
              }}>
                <span className={"pill " + cfg.pill + " dot"}>{lang === "fr" ? cfg.fr : cfg.en}</span>
                <div>
                  <div style={{ fontWeight: 500 }}>{i.title}</div>
                  {i.root_cause && (
                    <div className="text-faint" style={{ fontSize: 11.5, marginTop: 2 }}>
                      {lang === "fr" ? "Cause racine" : "Root cause"} : {i.root_cause}
                    </div>
                  )}
                </div>
                <span className="tag-mono">{i.sites_count || 0} sites</span>
                <div style={{ display: "flex", gap: 4 }}>
                  <button className="btn sm ghost" title={lang === "fr" ? "Modifier" : "Edit"}
                    onClick={() => { setEditing(i); setModalOpen(true); }}>
                    <Icon.edit />
                  </button>
                  <button className="btn sm ghost" title={lang === "fr" ? "Supprimer" : "Delete"}
                    onClick={() => onDelete(i.id)}>
                    <Icon.trash />
                  </button>
                </div>
              </div>
            );
          })}
        </div>
      </div>

      {modalOpen && (
        <IssueModal
          lang={lang}
          projectId={projectId}
          editing={editing}
          onClose={() => setModalOpen(false)}
        />
      )}
    </>
  );
}

// Edit / create baseline issue
function IssueModal({ lang, projectId, editing, onClose }) {
  const [title, setTitle]       = useStateB(editing ? editing.title : "");
  const [severity, setSeverity] = useStateB(editing ? editing.severity : "medium");
  const [rootCause, setRootCause] = useStateB(editing ? (editing.root_cause || "") : "");
  const [sitesCount, setSitesCount] = useStateB(editing ? (editing.sites_count || 0) : 0);
  const [busy, setBusy] = useStateB(false);
  const [err, setErr]   = useStateB(null);

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { display: "block", fontSize: 11, color: "var(--text-faint)", marginBottom: 4, textTransform: "uppercase", letterSpacing: "0.04em", marginTop: 10 };

  const onSubmit = async (e) => {
    e.preventDefault();
    if (!title.trim()) return;
    setBusy(true); setErr(null);
    try {
      const payload = {
        title: title.trim(),
        severity,
        root_cause: rootCause.trim() || null,
        sites_count: parseInt(sitesCount, 10) || 0,
      };
      if (editing) await window.melr.baselineIssuesCrud.update(editing.id, payload);
      else          await window.melr.baselineIssuesCrud.create(projectId, payload);
      onClose();
    } catch (e) { setErr(e.message); }
    finally { setBusy(false); }
  };

  return (
    <div onClick={() => !busy && onClose()} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.5)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={onSubmit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 520, maxWidth: "92vw",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)",
      }}>
        <div style={{ fontSize: 17, fontWeight: 600, marginBottom: 4 }}>
          {editing ? (lang === "fr" ? "Modifier le problème" : "Edit issue") : (lang === "fr" ? "Nouveau problème" : "New issue")}
        </div>

        <label style={lbl}>{lang === "fr" ? "Intitulé" : "Title"} *</label>
        <input autoFocus required style={inp} value={title} onChange={(e) => setTitle(e.target.value)} />

        <label style={lbl}>{lang === "fr" ? "Sévérité" : "Severity"}</label>
        <select style={inp} value={severity} onChange={(e) => setSeverity(e.target.value)}>
          {ISSUE_SEVERITIES.map((s) => <option key={s.v} value={s.v}>{lang === "fr" ? s.fr : s.en}</option>)}
        </select>

        <label style={lbl}>{lang === "fr" ? "Cause racine" : "Root cause"}</label>
        <textarea style={{ ...inp, minHeight: 50, resize: "vertical", fontFamily: "inherit" }}
          value={rootCause} onChange={(e) => setRootCause(e.target.value)} />

        <label style={lbl}>{lang === "fr" ? "Nombre de sites affectés" : "Number of affected sites"}</label>
        <input type="number" min={0} style={inp} value={sitesCount} onChange={(e) => setSitesCount(e.target.value)} />

        {err && <div style={{ color: "#b91c1c", fontSize: 12, marginTop: 10 }}>{err}</div>}

        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (editing ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Ajouter" : "Add"))}
          </button>
        </div>
      </form>
    </div>
  );
}

window.Baseline = Baseline;
