/* global React, Icon, PROJECTS, SECTORS, sectorById, sectorLabel, projectName, projectCountries */
const { useState: useStateA, useMemo: useMemoA, useRef: useRefA, useEffect: useEffectA } = React;

// Helper: current quarter for the period picker default.
// Returns { q: 1-4, year, label: "Q2 2026" }.
function currentQuarterDash() {
  const d = new Date();
  const q = Math.floor(d.getMonth() / 3) + 1;
  return { q, year: d.getFullYear(), label: "Q" + q + " " + d.getFullYear() };
}

// Generate the list of selectable periods: last 6 quarters + full years.
// The dropdown stays compact and shows realistic options.
function periodOptionsDash() {
  const now = currentQuarterDash();
  const opts = [];
  // Last 6 quarters (current + 5 previous)
  for (let i = 0; i < 6; i++) {
    let q = now.q - i, y = now.year;
    while (q <= 0) { q += 4; y -= 1; }
    opts.push({ kind: "quarter", q, year: y, label: "Q" + q + " " + y });
  }
  // Current and previous full year
  opts.push({ kind: "year", q: null, year: now.year,     label: lang_text("Année " + now.year, now.year + " full year") });
  opts.push({ kind: "year", q: null, year: now.year - 1, label: lang_text("Année " + (now.year - 1), (now.year - 1) + " full year") });
  return opts;
}
function lang_text(fr, en) { return (typeof window !== "undefined" && window.__lang === "en") ? en : fr; }

// ==================== DASHBOARD ====================
function Dashboard({ t, lang, onOpen, isSuperAdmin, actingOrgId, myOrgId, allOrgs }) {
  // Make current lang available to module helpers above (cheap hack so the
  // periodOptions list can localise without prop-drilling).
  if (typeof window !== "undefined") window.__lang = lang;
  // When super-admin acts on a non-home org, scope the dashboard to that
  // org's projects + programmes. Non-super-admins get RLS-filtered to their
  // own org naturally, so this is a no-op for them.
  const acting = !!(isSuperAdmin && actingOrgId && actingOrgId !== myOrgId);
  const { projects: ALL_PROJECTS, loading, error, realtime } = window.melr.useProjects();
  const { programmes: PROGRAMMES_DASH } = window.melr.usePrograms(acting ? actingOrgId : undefined);
  const { currency } = window.melr.useCurrency();
  const LiveBadge = window.melr.LiveBadge;
  const PROJECTS = acting
    ? (ALL_PROJECTS || []).filter((p) => p.organizationId === actingOrgId)
    : (ALL_PROJECTS || []);
  // Header action state ──────────────────────────────────────────────────
  // Open/close menus for Period and Export. Each is a small dropdown that
  // closes on outside-click (handled via a useEffect below).
  const [periodOpen, setPeriodOpen] = useStateA(false);
  const [exportOpen, setExportOpen] = useStateA(false);
  const [period, setPeriod] = useStateA(currentQuarterDash());
  const [createOpen, setCreateOpen] = useStateA(false);
  // Sector-chip-row pulse animation: when "Filtrer" is clicked we smooth-
  // scroll to the chips and add a CSS class for 1.2s to pulse-highlight.
  const sectorRowRef = useRefA(null);
  const [pulseChips, setPulseChips] = useStateA(false);

  // Close any open dropdown when clicking outside it.
  useEffectA(() => {
    if (!periodOpen && !exportOpen) return;
    const onDown = (e) => {
      if (!e.target.closest("[data-dropdown-host]")) {
        setPeriodOpen(false);
        setExportOpen(false);
      }
    };
    document.addEventListener("mousedown", onDown);
    return () => document.removeEventListener("mousedown", onDown);
  }, [periodOpen, exportOpen]);

  // "Filtrer" button: smooth scroll to the sector-chip filter row + pulse.
  const onFilterClick = () => {
    if (!sectorRowRef.current) return;
    sectorRowRef.current.scrollIntoView({ behavior: "smooth", block: "center" });
    setPulseChips(true);
    setTimeout(() => setPulseChips(false), 1400);
  };

  // "Exporter → CSV": uses the shared exportCSV helper with a flat columns map.
  const onExportCSV = () => {
    setExportOpen(false);
    const rows = PROJECTS.map((p) => ({
      code: p.id,
      name: projectName(p, lang),
      sector: (() => { const s = sectorById(p.sector); return s ? (lang === "en" ? s.en : s.fr) : ""; })(),
      countries: projectCountries(p, lang),
      lead: p.lead,
      budget_native: p.budget,
      currency: p.nativeCurrency || "EUR",
      disbursed_native: p.disbursed,
      progress: Math.round((p.progress || 0)),
      phase: lang === "en" ? p.phaseEn : p.phaseFr,
      risk: p.risk,
      indicators: p.indic,
      sites: p.sites,
    }));
    const date = new Date().toISOString().slice(0, 10);
    if (window.melr.exportCSV) {
      window.melr.exportCSV("dashboard-projects-" + date + ".csv", rows, [
        { key: "code",            label: "Code" },
        { key: "name",            label: lang === "fr" ? "Projet" : "Project" },
        { key: "sector",          label: lang === "fr" ? "Secteur" : "Sector" },
        { key: "countries",       label: lang === "fr" ? "Pays" : "Countries" },
        { key: "lead",            label: lang === "fr" ? "Responsable" : "Lead" },
        { key: "budget_native",   label: lang === "fr" ? "Budget" : "Budget" },
        { key: "currency",        label: lang === "fr" ? "Devise" : "Currency" },
        { key: "disbursed_native",label: lang === "fr" ? "Décaissé" : "Disbursed" },
        { key: "progress",        label: lang === "fr" ? "Avancement %" : "Progress %" },
        { key: "phase",           label: lang === "fr" ? "Phase" : "Phase" },
        { key: "risk",            label: lang === "fr" ? "Risque" : "Risk" },
        { key: "indicators",      label: lang === "fr" ? "Indicateurs" : "Indicators" },
        { key: "sites",           label: "Sites" },
      ]);
    }
  };

  // "Exporter → Excel": same data, .xlsx via the global XLSX (SheetJS) lib.
  const onExportXLSX = () => {
    setExportOpen(false);
    if (!window.XLSX) {
      alert(lang === "fr" ? "Bibliothèque XLSX non chargée — utilisez l'export CSV à la place."
                          : "XLSX library not loaded — please use the CSV export instead.");
      return;
    }
    const data = PROJECTS.map((p) => {
      const s = sectorById(p.sector);
      return {
        Code: p.id,
        [lang === "fr" ? "Projet" : "Project"]: projectName(p, lang),
        [lang === "fr" ? "Secteur" : "Sector"]: s ? (lang === "en" ? s.en : s.fr) : "",
        [lang === "fr" ? "Pays" : "Countries"]: projectCountries(p, lang),
        [lang === "fr" ? "Responsable" : "Lead"]: p.lead,
        [lang === "fr" ? "Budget" : "Budget"]: p.budget,
        [lang === "fr" ? "Devise" : "Currency"]: p.nativeCurrency || "EUR",
        [lang === "fr" ? "Décaissé" : "Disbursed"]: p.disbursed,
        [lang === "fr" ? "Avancement %" : "Progress %"]: Math.round(p.progress || 0),
        [lang === "fr" ? "Phase" : "Phase"]: lang === "en" ? p.phaseEn : p.phaseFr,
        [lang === "fr" ? "Risque" : "Risk"]: p.risk,
        [lang === "fr" ? "Indicateurs" : "Indicators"]: p.indic,
        Sites: p.sites,
      };
    });
    const ws = window.XLSX.utils.json_to_sheet(data);
    const wb = window.XLSX.utils.book_new();
    window.XLSX.utils.book_append_sheet(wb, ws, lang === "fr" ? "Projets" : "Projects");
    const date = new Date().toISOString().slice(0, 10);
    window.XLSX.writeFile(wb, "dashboard-projects-" + date + ".xlsx");
  };

  // "Exporter → PDF": triggers the browser's print dialog. Users pick
  // "Save as PDF" as the destination. We toggle a body class so the
  // existing @media print rules in styles.css switch from the
  // report-only path (default) to the page-as-PDF path (Dashboard et al.).
  // afterprint fires whether the user printed or cancelled, so the class
  // is always removed cleanly.
  const onExportPDF = () => {
    setExportOpen(false);
    if (!window.print) return;
    document.body.classList.add("print-page-mode");
    const cleanup = () => {
      document.body.classList.remove("print-page-mode");
      window.removeEventListener("afterprint", cleanup);
    };
    window.addEventListener("afterprint", cleanup);
    // Fallback timer in case afterprint doesn't fire (some older browsers).
    setTimeout(() => {
      if (document.body.classList.contains("print-page-mode")) cleanup();
    }, 60000);
    window.print();
  };

  // fmtM takes a project (or any obj with a nativeCurrency field) and the amount field name.
  const fmtM = (v, src) => window.melr.formatAmount(v, src || "EUR", currency, lang);
  // Aggregate multiple projects' budgets (each in their own native ccy)
  // into EUR millions, then convert once to the display currency.
  const fmtAgg = (projs, field) => {
    const inEur = (projs || []).reduce((s, p) => s + window.melr.convertToEur(p[field] || 0, p.nativeCurrency || "EUR"), 0);
    return window.melr.formatMoney(inEur, currency, lang);
  };

  if (loading) {
    return <div className="page"><div className="page-body" style={{ padding: 40 }}>{lang === "fr" ? "Chargement des projets…" : "Loading projects…"}</div></div>;
  }
  if (error) {
    return <div className="page"><div className="page-body" style={{ padding: 40, color: "#b91c1c" }}>{(lang === "fr" ? "Erreur base de données : " : "Database error: ") + error}</div></div>;
  }

  const kpis = [
    { label: t("dash.kpi.projects"), value: PROJECTS.length, delta: "+3", dir: "up", spark: [3, 5, 6, 7, 8, 9, 10, 11, 12, 12] },
    { label: t("dash.kpi.indicators"), value: "219", delta: "+11", dir: "up", spark: [120, 130, 145, 150, 162, 168, 173, 178, 200, 219] },
    { label: t("dash.kpi.disbursed"), value: fmtAgg(PROJECTS, "disbursed"), delta: "51%", dir: "flat", spark: [10, 14, 18, 22, 26, 28, 31, 33, 36, 38] },
    { label: t("dash.kpi.beneficiaries"), value: "1.84M", delta: "+8.4%", dir: "up", spark: [1.4, 1.5, 1.55, 1.62, 1.7, 1.74, 1.78, 1.80, 1.82, 1.84] },
  ];

  // Mix portfolio status from PROJECTS
  const dashProjects = PROJECTS.slice(0, 7).map((p) => ({
    id: p.id, name: projectName(p, lang), lead: p.lead.split(" ").map((s) => s[0]).slice(0, 2).join(". "),
    country: projectCountries(p, lang), status: p.risk === "ok" ? "ok" : p.risk === "warn" ? "warn" : "risk",
    progress: p.progress / 100, indicators: p.indic, sites: p.sites, sector: p.sector, raw: p,
  }));

  const activity = [
    { who: "Aïssata D.", what: lang === "fr" ? "a soumis le rapport trimestriel pour" : "submitted Q1 report for", obj: "P-241", when: lang === "fr" ? "il y a 12 min" : "12 min ago", icon: <Icon.fileText /> },
    { who: "Modibo K.", what: lang === "fr" ? "a validé les rendements de la campagne sur" : "validated campaign yields on", obj: "P-302", when: lang === "fr" ? "il y a 1 h" : "1 h ago", icon: <Icon.shieldCheck /> },
    { who: "Halima I.", what: lang === "fr" ? "a importé 412 enquêtes nutritionnelles depuis" : "imported 412 nutrition surveys from", obj: "Kobo", when: lang === "fr" ? "il y a 3 h" : "3 h ago", icon: <Icon.upload /> },
    { who: "Fatima O.", what: lang === "fr" ? "a clôturé l'audit fiscal trimestriel sur" : "closed quarterly fiscal audit on", obj: "P-098", when: lang === "fr" ? "hier" : "yesterday", icon: <Icon.flag /> },
    { who: "Salif O.", what: lang === "fr" ? "a marqué une question d'apprentissage" : "flagged a learning question on", obj: "P-156", when: lang === "fr" ? "hier" : "yesterday", icon: <Icon.brain /> },
    { who: "Boubacar T.", what: lang === "fr" ? "a déclenché un calcul VAN/TRI sur" : "ran NPV/IRR computation on", obj: "P-491", when: "2j", icon: <Icon.zap /> },
  ];

  const alerts = [
    { kind: "red", title: lang === "fr" ? "Audit de données en retard" : "Data audit overdue", sub: "P-377 · WASH Tchad — 4 sites", when: lang === "fr" ? "depuis 3 j" : "3d overdue" },
    { kind: "amber", title: lang === "fr" ? "Indicateurs sous la cible" : "Indicators below target", sub: lang === "fr" ? "7 indicateurs · couverture 78%" : "7 indicators · 78% coverage", when: "Q1" },
    { kind: "amber", title: lang === "fr" ? "Rapport bailleur dans 5 j" : "Donor report in 5d", sub: "P-241 · format AFD", when: lang === "fr" ? "20 mai" : "May 20" },
    { kind: "violet", title: lang === "fr" ? "Question d'apprentissage à instruire" : "Learning question to instruct", sub: lang === "fr" ? "Adoption foyers améliorés < 60% en saison sèche ?" : "Improved-stove adoption < 60% in dry season?", when: "P-491" },
  ];

  // Sector breakdown
  const sectorBreakdown = SECTORS.map((s) => {
    const matches = PROJECTS.filter((p) => p.sector === s.id);
    // Convert each project's amounts to EUR first so we can sum across
    // mixed-currency projects safely.
    const toEur = (v, ccy) => window.melr.convertToEur(v, ccy || "EUR");
    return {
      sector: s,
      count: matches.length,
      budget:    matches.reduce((sum, p) => sum + toEur(p.budget,    p.nativeCurrency), 0),
      disbursed: matches.reduce((sum, p) => sum + toEur(p.disbursed, p.nativeCurrency), 0),
      sites:     matches.reduce((sum, p) => sum + (p.sites || 0), 0),
    };
  }).filter((b) => b.count > 0).sort((a, b) => b.budget - a.budget);

  const totalBudget = sectorBreakdown.reduce((s, b) => s + b.budget, 0);

  // Programme breakdown — symmetrical to sectorBreakdown but grouping
  // projects by programme_id. Orphans (no programme) get their own
  // "Sans programme" bucket so the bar visually shows what's unassigned.
  const programmeBreakdown = (PROGRAMMES_DASH || []).map((pg) => {
    const matches = PROJECTS.filter((p) => p.programmeId === pg.id);
    const toEur = (v, ccy) => window.melr.convertToEur(v, ccy || "EUR");
    const pgSector = sectorById(pg.sector_id);
    return {
      programme: pg,
      sector: pgSector,
      count: matches.length,
      budget:    matches.reduce((sum, p) => sum + toEur(p.budget,    p.nativeCurrency), 0),
      disbursed: matches.reduce((sum, p) => sum + toEur(p.disbursed, p.nativeCurrency), 0),
      sites:     matches.reduce((sum, p) => sum + (p.sites || 0), 0),
    };
  }).sort((a, b) => b.budget - a.budget);

  // Orphan bucket (rendered last in the bar, dashed in the legend)
  const orphanProjects = PROJECTS.filter((p) => !p.programmeId);
  const orphanBucket = orphanProjects.length > 0 ? {
    programme: null,
    sector: null,
    count: orphanProjects.length,
    budget:    orphanProjects.reduce((s, p) => s + window.melr.convertToEur(p.budget    || 0, p.nativeCurrency || "EUR"), 0),
    disbursed: orphanProjects.reduce((s, p) => s + window.melr.convertToEur(p.disbursed || 0, p.nativeCurrency || "EUR"), 0),
    sites:     orphanProjects.reduce((s, p) => s + (p.sites || 0), 0),
  } : null;

  const allProgrammeBuckets = orphanBucket
    ? [...programmeBreakdown, orphanBucket]
    : programmeBreakdown;
  const totalBudgetProgrammes = allProgrammeBuckets.reduce((s, b) => s + b.budget, 0);
  const activeProgrammes = programmeBreakdown.filter((b) => b.count > 0).length;

  const statusPill = (s) => ({
    ok: <span className="pill green dot">{lang === "fr" ? "Sur cible" : "On target"}</span>,
    warn: <span className="pill amber dot">{lang === "fr" ? "Attention" : "At risk"}</span>,
    risk: <span className="pill red dot">{lang === "fr" ? "Critique" : "Critical"}</span>,
  })[s];

  return (
    <div className="page">
      <div className="page-header">
        <div className="page-eyebrow">{lang === "fr" ? "PORTEFEUILLE / MULTI-SECTEURS" : "PORTFOLIO / MULTI-SECTOR"}</div>
        <div className="page-header-row">
          <div>
            <h1 className="page-title">
              {(lang === "fr" ? "Vue d'ensemble — " : "Portfolio overview — ") + period.label}
              <LiveBadge on={realtime} lang={lang} />
            </h1>
            <div className="page-sub">
              {lang === "fr"
                ? `${PROJECTS.length} projets actifs · ${(PROGRAMMES_DASH || []).length} programmes · ${sectorBreakdown.length} secteurs · ${fmtM(totalBudget)} engagés · données consolidées au 28 avril 2026`
                : `${PROJECTS.length} active projects · ${(PROGRAMMES_DASH || []).length} programmes · ${sectorBreakdown.length} sectors · ${fmtM(totalBudget)} committed · consolidated to April 28 2026`}
            </div>
          </div>
          <div className="page-header-actions" style={{ position: "relative" }}>
            {/* Period picker (Q1/Q2/.../full year). Opens an inline dropdown. */}
            <div data-dropdown-host style={{ position: "relative" }}>
              <button className="btn sm" onClick={(e) => { e.stopPropagation(); setPeriodOpen((o) => !o); setExportOpen(false); }}>
                <Icon.calendar /> {period.label}
              </button>
              {periodOpen && (
                <div style={{
                  position: "absolute", top: "100%", right: 0, marginTop: 4,
                  minWidth: 160, background: "var(--bg, white)", color: "var(--text)",
                  border: "1px solid var(--line)", borderRadius: 8,
                  boxShadow: "0 6px 16px rgba(0,0,0,.12)", padding: 4, zIndex: 50,
                }}>
                  {periodOptionsDash().map((opt, i) => (
                    <button key={i} className="btn xs ghost"
                      style={{ width: "100%", justifyContent: "flex-start" }}
                      onClick={() => { setPeriod(opt); setPeriodOpen(false); }}>
                      {opt.label}
                    </button>
                  ))}
                </div>
              )}
            </div>
            {/* "Filtrer" → scroll smooth + pulse on the sector-chip row. */}
            <button className="btn sm" onClick={onFilterClick}
              title={lang === "fr" ? "Aller au filtre par secteur" : "Jump to sector filter"}>
              <Icon.filter /> {t("c.filter")}
            </button>
            {/* "Exporter" → CSV / Excel / PDF menu. */}
            <div data-dropdown-host style={{ position: "relative" }}>
              <button className="btn sm" onClick={(e) => { e.stopPropagation(); setExportOpen((o) => !o); setPeriodOpen(false); }}>
                <Icon.download /> {t("c.export")}
              </button>
              {exportOpen && (
                <div style={{
                  position: "absolute", top: "100%", right: 0, marginTop: 4,
                  minWidth: 180, background: "var(--bg, white)", color: "var(--text)",
                  border: "1px solid var(--line)", borderRadius: 8,
                  boxShadow: "0 6px 16px rgba(0,0,0,.12)", padding: 4, zIndex: 50,
                }}>
                  <button className="btn xs ghost" style={{ width: "100%", justifyContent: "flex-start" }} onClick={onExportCSV}>
                    📄 CSV
                  </button>
                  <button className="btn xs ghost" style={{ width: "100%", justifyContent: "flex-start" }} onClick={onExportXLSX}>
                    📊 Excel (.xlsx)
                  </button>
                  <button className="btn xs ghost" style={{ width: "100%", justifyContent: "flex-start" }} onClick={onExportPDF}>
                    🖨 PDF {lang === "fr" ? "(via impression)" : "(via print dialog)"}
                  </button>
                </div>
              )}
            </div>
            {/* "Nouveau" → opens CreateProjectModal (shared, in app.jsx). */}
            <button className="btn sm primary" onClick={() => setCreateOpen(true)}>
              <Icon.plus /> {t("top.new")}
            </button>
          </div>
        </div>
      </div>

      <div className="page-body">
        {/* KPI grid */}
        <div className="grid kpi" style={{ marginBottom: 18 }}>
          {kpis.map((k, i) => (
            <div key={i} className="kpi-cell">
              <div className="kpi-label">{k.label}</div>
              <div className="kpi-value">{k.value}</div>
              <div className={"kpi-delta " + k.dir}>
                {k.dir === "up" && <Icon.arrowUp className="sm" sw="2" />}
                {k.dir === "down" && <Icon.arrowDown className="sm" sw="2" />}
                {k.dir === "flat" && <span className="mono">·</span>}
                {k.delta} <span className="text-faint" style={{ marginLeft: 4 }}>vs Q4</span>
              </div>
              <div className="kpi-spark">
                <Sparkline values={k.spark} dir={k.dir} />
              </div>
            </div>
          ))}
        </div>

        {/* Sector breakdown — also the target of the header "Filtrer" button
            (scroll-into-view + pulse animation to draw the user's eye). */}
        <div ref={sectorRowRef}
             className={"card" + (pulseChips ? " dashboard-filter-pulse" : "")}
             style={{ marginBottom: 16 }}>
          <div className="card-head">
            <div className="card-title">{lang === "fr" ? "Répartition par secteur" : "Sector breakdown"}</div>
            <span className="text-faint" style={{ fontSize: 11.5 }}>{lang === "fr" ? "Budget engagé · 9 secteurs actifs" : "Committed budget · 9 active sectors"}</span>
          </div>
          <div className="card-body">
            {/* Stacked bar */}
            <div className="sector-bar">
              {sectorBreakdown.map((b) => (
                <div key={b.sector.id} className="sector-bar-seg"
                  style={{ width: (b.budget / totalBudget * 100) + "%", background: b.sector.color }}
                  title={`${sectorLabel(b.sector, lang)} · ${fmtM(b.budget)}`}>
                </div>
              ))}
            </div>
            {/* Legend grid */}
            <div className="sector-grid">
              {sectorBreakdown.map((b) => (
                <div key={b.sector.id} className="sector-tile">
                  <span className="sector-dot" style={{ background: b.sector.color }}></span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div className="strong" style={{ fontSize: 12 }}>{sectorLabel(b.sector, lang)}</div>
                    <div className="text-faint" style={{ fontSize: 10.5 }}>
                      {b.count} {lang === "fr" ? "projets" : "projects"} · {b.sites} sites
                    </div>
                  </div>
                  <div style={{ textAlign: "right" }}>
                    <div className="mono strong" style={{ fontSize: 12.5 }}>{fmtM(b.budget)}</div>
                    <div className="text-faint mono" style={{ fontSize: 10.5 }}>{Math.round(b.disbursed / b.budget * 100)}% {lang === "fr" ? "déc." : "disb."}</div>
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* Programme breakdown — mirrors sector breakdown but groups by programme parent */}
        <div className="card" style={{ marginBottom: 16 }}>
          <div className="card-head">
            <div className="card-title">{lang === "fr" ? "Répartition par programme" : "Programme breakdown"}</div>
            <span className="text-faint" style={{ fontSize: 11.5 }}>
              {(PROGRAMMES_DASH || []).length === 0
                ? (lang === "fr" ? "Aucun programme créé pour le moment" : "No programme created yet")
                : (lang === "fr"
                    ? `Budget cumulé · ${activeProgrammes} programme${activeProgrammes > 1 ? "s" : ""} actif${activeProgrammes > 1 ? "s" : ""}`
                    : `Cumulative budget · ${activeProgrammes} active programme${activeProgrammes > 1 ? "s" : ""}`)}
            </span>
            <div style={{ flex: 1 }}></div>
            <button className="btn xs ghost" onClick={() => onOpen && onOpen("programmes-list")}>
              {lang === "fr" ? "Voir tous →" : "See all →"}
            </button>
          </div>
          <div className="card-body">
            {(PROGRAMMES_DASH || []).length === 0 ? (
              <div style={{ padding: "16px 4px", textAlign: "center" }}>
                <div className="text-faint" style={{ fontSize: 12.5, marginBottom: 6 }}>
                  {lang === "fr"
                    ? "Vos projets ne sont pas encore regroupés par programme."
                    : "Your projects are not yet grouped by programme."}
                </div>
                <button className="btn sm" onClick={() => onOpen && onOpen("programmes-list")}>
                  {lang === "fr" ? "Créer un programme →" : "Create a programme →"}
                </button>
              </div>
            ) : (
              <>
                {/* Stacked bar — one segment per programme (+ optional orphan tail) */}
                <div className="sector-bar">
                  {allProgrammeBuckets.map((b, i) => {
                    const isOrphan = !b.programme;
                    const color = isOrphan ? "var(--text-faint)" : (b.sector ? b.sector.color : "var(--accent)");
                    const label = isOrphan
                      ? (lang === "fr" ? "Sans programme" : "No programme")
                      : `${b.programme.code} · ${fmtM(b.budget)}`;
                    return (
                      <div key={isOrphan ? "orphan" : b.programme.id}
                        className="sector-bar-seg"
                        style={{
                          width: (b.budget / totalBudgetProgrammes * 100) + "%",
                          background: color,
                          opacity: isOrphan ? 0.4 : 1,
                          backgroundImage: isOrphan
                            ? "repeating-linear-gradient(45deg, transparent 0 4px, rgba(0,0,0,.1) 4px 8px)"
                            : undefined,
                        }}
                        title={label}>
                      </div>
                    );
                  })}
                </div>
                {/* Legend grid */}
                <div className="sector-grid">
                  {allProgrammeBuckets.map((b) => {
                    const isOrphan = !b.programme;
                    const color = isOrphan ? "var(--text-faint)" : (b.sector ? b.sector.color : "var(--accent)");
                    return (
                      <div key={isOrphan ? "orphan" : b.programme.id}
                        className={isOrphan ? "sector-tile" : "sector-tile row-link"}
                        onClick={isOrphan ? undefined : () => onOpen && onOpen("prog:" + b.programme.id)}
                        style={isOrphan ? { borderStyle: "dashed", opacity: 0.85 } : { cursor: "pointer" }}
                        title={isOrphan ? null : (lang === "fr" ? "Ouvrir le programme" : "Open programme")}>
                        <span className="sector-dot" style={{ background: color }}></span>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div className="strong" style={{ fontSize: 12 }}>
                            {isOrphan
                              ? <span className="text-faint">◇ {lang === "fr" ? "Sans programme" : "No programme"}</span>
                              : <>◇ {b.programme.code} <span className="text-faint" style={{ fontWeight: 400 }}>— {lang === "en" ? (b.programme.name_en || b.programme.name_fr) : b.programme.name_fr}</span></>}
                          </div>
                          <div className="text-faint" style={{ fontSize: 10.5 }}>
                            {b.count} {lang === "fr" ? "projets" : "projects"} · {b.sites} sites
                            {b.sector && <> · {sectorLabel(b.sector, lang)}</>}
                          </div>
                        </div>
                        <div style={{ textAlign: "right" }}>
                          <div className="mono strong" style={{ fontSize: 12.5 }}>{fmtM(b.budget)}</div>
                          <div className="text-faint mono" style={{ fontSize: 10.5 }}>
                            {b.budget > 0 ? Math.round(b.disbursed / b.budget * 100) : 0}% {lang === "fr" ? "déc." : "disb."}
                          </div>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </>
            )}
          </div>
        </div>

        <div className="grid" style={{ gridTemplateColumns: "1.6fr 1fr" }}>
          {/* Portfolio */}
          <div className="card">
            <div className="card-head">
              <div className="card-title">{t("dash.section.portfolio")}</div>
              <span className="pill">{dashProjects.length}</span>
              <div style={{ flex: 1 }}></div>
              <button className="btn sm ghost"><Icon.filter /></button>
              <button className="btn sm ghost" onClick={() => onOpen && onOpen("projects-list")}>{t("c.see_all")}</button>
            </div>
            <div className="card-body flush">
              <table className="tbl">
                <thead>
                  <tr>
                    <th style={{ width: 60 }}>ID</th>
                    <th>{lang === "fr" ? "Projet" : "Project"}</th>
                    <th>{lang === "fr" ? "Secteur" : "Sector"}</th>
                    <th>{t("c.owner")}</th>
                    <th>{t("c.status")}</th>
                    <th style={{ width: 140 }}>{t("c.progress")}</th>
                    <th className="num">Ind.</th>
                  </tr>
                </thead>
                <tbody>
                  {dashProjects.map((p) => {
                    const s = sectorById(p.sector);
                    return (
                      <tr key={p.id} className="row-link" onClick={() => onOpen && onOpen(p.id)}>
                        <td><span className="tag-mono">{p.id}</span></td>
                        <td className="strong">{p.name}</td>
                        <td><span className="sector-chip" style={{ background: s.bg, color: s.color, borderColor: s.color }}>{sectorLabel(s, lang)}</span></td>
                        <td>
                          <div className="row" style={{ gap: 6 }}>
                            <div className="avatar" style={{ width: 18, height: 18, fontSize: 9 }}>
                              {p.raw.lead.split(" ").map((s) => s[0]).join("").slice(0, 2)}
                            </div>
                            {p.lead}
                          </div>
                        </td>
                        <td>{statusPill(p.status)}</td>
                        <td>
                          <div className="row" style={{ gap: 8 }}>
                            <div className={"progress-bar " + (p.status === "ok" ? "green" : p.status === "warn" ? "amber" : "red")} style={{ flex: 1 }}>
                              <div className="fill" style={{ width: (p.progress * 100) + "%" }}></div>
                            </div>
                            <span className="num" style={{ width: 36, fontSize: 11 }}>{Math.round(p.progress * 100)}%</span>
                          </div>
                        </td>
                        <td className="num">{p.indicators}</td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </div>

          {/* Activity */}
          <div className="card">
            <div className="card-head">
              <div className="card-title">{t("dash.section.activity")}</div>
              <button className="btn sm ghost" style={{ marginLeft: "auto" }}>{t("c.see_all")}</button>
            </div>
            <div className="card-body flush">
              {activity.map((a, i) => (
                <div key={i} className="activity">
                  <div className="activity-icon">{a.icon}</div>
                  <div>
                    <div><b style={{ fontWeight: 500 }}>{a.who}</b> <span className="text-muted">{a.what}</span> <span className="tag-mono" style={{ fontSize: 10.5 }}>{a.obj}</span></div>
                  </div>
                  <span className="when">{a.when}</span>
                </div>
              ))}
            </div>
          </div>
        </div>

        <div style={{ height: 16 }}></div>

        <div className="grid" style={{ gridTemplateColumns: "1fr 1fr 1fr" }}>
          {/* Performance */}
          <div className="card" style={{ gridColumn: "span 2" }}>
            <div className="card-head">
              <div className="card-title">{lang === "fr" ? "Performance par secteur" : "Performance by sector"}</div>
              <div className="row" style={{ gap: 4, marginLeft: "auto" }}>
                <span className="pill accent">{lang === "fr" ? "5 sur cible" : "5 on target"}</span>
                <span className="pill amber">{lang === "fr" ? "3 attention" : "3 at risk"}</span>
                <span className="pill red">{lang === "fr" ? "1 critique" : "1 critical"}</span>
              </div>
            </div>
            <div className="card-body">
              <PerfChart lang={lang} sectorBreakdown={sectorBreakdown} />
            </div>
          </div>

          {/* Alerts */}
          <div className="card">
            <div className="card-head">
              <div className="card-title">{t("dash.section.alerts")}</div>
              <span className="pill red">{alerts.filter(a => a.kind === "red").length}</span>
            </div>
            <div className="card-body flush">
              {alerts.map((a, i) => (
                <div key={i} style={{ padding: "10px 14px", borderBottom: "1px solid var(--line-faint)", display: "grid", gridTemplateColumns: "8px 1fr auto", gap: 10, alignItems: "center" }}>
                  <span style={{
                    width: 8, height: 8, borderRadius: 2,
                    background: a.kind === "red" ? "var(--red)" : a.kind === "amber" ? "var(--amber)" : "var(--violet)"
                  }}></span>
                  <div>
                    <div style={{ fontWeight: 500 }}>{a.title}</div>
                    <div className="text-faint" style={{ fontSize: 11.5, marginTop: 2 }}>{a.sub}</div>
                  </div>
                  <span className="text-faint mono" style={{ fontSize: 10.5 }}>{a.when}</span>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
      {/* New-project modal triggered by the header "Nouveau" button.
          CreateProjectModal is defined in app.jsx and exposed via
          window.CreateProjectModal at module-init time, so by the time
          this render runs at runtime it's available. */}
      {createOpen && window.CreateProjectModal && (
        <window.CreateProjectModal
          lang={lang}
          isSuperAdmin={isSuperAdmin}
          allOrgs={allOrgs}
          onClose={() => setCreateOpen(false)}
          onCreated={() => { setCreateOpen(false); /* useProjects realtime refreshes the list */ }}
        />
      )}
    </div>
  );
}

function Sparkline({ values, dir }) {
  const w = 80, h = 28;
  const min = Math.min(...values), max = Math.max(...values);
  const range = max - min || 1;
  const pts = values.map((v, i) => `${(i / (values.length - 1)) * w},${h - ((v - min) / range) * h}`).join(" ");
  const color = dir === "up" ? "var(--green)" : dir === "down" ? "var(--red)" : "var(--accent)";
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`}>
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.4" />
    </svg>
  );
}

function PerfChart({ lang, sectorBreakdown }) {
  // Bar-with-target chart — top 6 sectors
  const data = sectorBreakdown.slice(0, 7).map((b, i) => {
    const seed = (b.sector.id.charCodeAt(0) * 31 + i * 17) % 30;
    const target = 65 + seed;
    const actual = Math.max(20, Math.min(98, target + (seed - 15) * 1.4));
    return { name: sectorLabel(b.sector, lang), actual: Math.round(actual), target: Math.min(95, target), color: b.sector.color };
  });
  const W = 600, H = 200, padL = 30, padB = 38, padT = 16, padR = 10;
  const cw = (W - padL - padR) / data.length;
  const barW = cw * 0.55;
  const yScale = (v) => padT + (1 - v / 100) * (H - padT - padB);
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", height: 220 }}>
      {[0, 25, 50, 75, 100].map((g) => (
        <g key={g}>
          <line x1={padL} y1={yScale(g)} x2={W - padR} y2={yScale(g)} stroke="var(--line-faint)" strokeDasharray="2 3" />
          <text x={padL - 6} y={yScale(g) + 3} textAnchor="end" fontSize="9" fill="var(--text-faint)" fontFamily="var(--font-mono)">{g}</text>
        </g>
      ))}
      {data.map((d, i) => {
        const x = padL + i * cw + (cw - barW) / 2;
        const y = yScale(d.actual);
        const onTarget = d.actual >= d.target;
        return (
          <g key={i}>
            <rect x={x} y={y} width={barW} height={H - padB - y} fill={onTarget ? d.color : "var(--amber)"} rx="2" opacity="0.92" />
            <line x1={x - 3} x2={x + barW + 3} y1={yScale(d.target)} y2={yScale(d.target)} stroke="var(--text)" strokeWidth="1.4" />
            <text x={x + barW / 2} y={H - padB + 14} textAnchor="middle" fontSize="9.5" fill="var(--text-muted)">{d.name.slice(0, 14)}</text>
            <text x={x + barW / 2} y={y - 4} textAnchor="middle" fontSize="10" fill="var(--text)" fontFamily="var(--font-mono)">{d.actual}</text>
          </g>
        );
      })}
      <g transform={`translate(${W - padR - 240}, ${padT - 4})`}>
        <rect x="0" y="0" width="10" height="10" fill="var(--accent)" rx="2" />
        <text x="14" y="9" fontSize="10" fill="var(--text-muted)">{lang === "fr" ? "Réalisé" : "Achieved"}</text>
        <line x1="74" x2="94" y1="5" y2="5" stroke="var(--text)" strokeWidth="1.5" />
        <text x="98" y="9" fontSize="10" fill="var(--text-muted)">{lang === "fr" ? "Cible" : "Target"}</text>
        <rect x="146" y="0" width="10" height="10" fill="var(--amber)" rx="2" />
        <text x="160" y="9" fontSize="10" fill="var(--text-muted)">{lang === "fr" ? "Sous cible" : "Below"}</text>
      </g>
    </svg>
  );
}

window.Dashboard = Dashboard;
