/* global window */
// ============================================================================
// MELR · Cadre logique complet (donor-agnostic)
// ----------------------------------------------------------------------------
// Generates the *project-specific* logical framework as a .docx file.
//
// Unlike the donor templates (USAID/GAVI/AFD/UE/WB) which use mostly
// boilerplate copy, this export pulls the real cadrage data persisted in
// the project detail "Cadre logique" tab :
//   - project_objectives  (kind: general | specific | result)
//   - project_toc         (level: activity | output | outcome | impact)
//   - project_assumptions (statement, mitigation, status)
//   - indicators          (level, code, name, target, frequency, means_of_verification)
//
// Output : standard 4-column logframe matrix as expected by every classical
// donor (AFD, UE, GIZ, Banque mondiale, USAID DO-IR, etc.) :
//
//   ┌───────────────────────┬─────────────────────────┬────────────────────────┬───────────────────────┐
//   │ Logique d'intervention │ Indicateurs vérifiables │ Sources de vérification │ Hypothèses / Risques  │
//   ├───────────────────────┼─────────────────────────┼────────────────────────┼───────────────────────┤
//   │ Objectif global        │ … indicators Impact     │ … MoV per indicator    │ … hypotheses (lvl 1)  │
//   │ Objectifs spécifiques  │ … indicators Outcome    │ … MoV per indicator    │ … hypotheses (lvl 2)  │
//   │ Résultats attendus     │ … indicators Output     │ … MoV per indicator    │ … hypotheses (lvl 3)  │
//   │ Activités              │ … effort indicators     │ … logs/photos          │ … hypotheses (lvl 4)  │
//   └───────────────────────┴─────────────────────────┴────────────────────────┴───────────────────────┘
//
// One full logframe per project in scope. Empty cells display "À définir"
// so the document remains lisible even when the cadrage is incomplete.
//
// Entry point: window.exportLogframe({ projects, indicators, scope, year,
//                                       scopeLabel, orgName, lang, filename })
// Uses shared helpers via window.melrDonor.
// ============================================================================

(function () {
  if (typeof window === "undefined") return;

  // Couleur primaire neutre (bleu foncé MELR) — neutralité vis-à-vis des
  // bailleurs : ce template est *générique*, pas badgé.
  const PRIMARY = "002F6C";
  const ACCENT  = "EFF6FF";

  function _shared() { return window.melrDonor; }
  function _ok()     { return !!(_shared() && _shared().isReady && _shared().isReady()); }

  // ── Récupération asynchrone des données cadrage ──────────────────────
  // L'écran Reporting passe `projects` + `indicators` mais ne charge pas
  // les tables cadrage (objectives / toc / assumptions). On va les chercher
  // ici en parallèle pour chaque projet du scope. Si Supabase n'est pas
  // disponible (mode démo / offline), on retourne des arrays vides — le
  // template affichera "À définir" partout, mais ne plantera pas.
  async function _fetchCadrage(projectIds) {
    const sb = window.melr && window.melr.supabase;
    if (!sb || !projectIds || projectIds.length === 0) {
      return { objectives: [], toc: [], assumptions: [] };
    }
    try {
      const [objR, tocR, assR] = await Promise.all([
        sb.from("project_objectives")
          .select("id, project_id, kind, label, description, sort_order")
          .in("project_id", projectIds)
          .order("sort_order", { ascending: true }),
        sb.from("project_toc")
          .select("id, project_id, level, label, description, parent_id, assumption, sort_order")
          .in("project_id", projectIds)
          .order("sort_order", { ascending: true }),
        sb.from("project_assumptions")
          .select("id, project_id, statement, mitigation, status, sort_order, last_reviewed")
          .in("project_id", projectIds)
          .order("sort_order", { ascending: true }),
      ]);
      return {
        objectives:   (objR && objR.data)  || [],
        toc:          (tocR && tocR.data)  || [],
        assumptions:  (assR && assR.data)  || [],
      };
    } catch (e) {
      console.warn("[reporting-logframe] _fetchCadrage failed:", e);
      return { objectives: [], toc: [], assumptions: [] };
    }
  }

  // Indicators arrive from the Reporting screen with multiple possible
  // shape variations (project_uuid / projectId / project / project_id).
  // Normalize the project link so we can group them per project below.
  function _indProjectId(ind) {
    return ind.project_uuid || ind.projectId || ind.project_id || ind.project || null;
  }

  // ── Cover ──────────────────────────────────────────────────────────────
  function buildCover({ orgName, scopeLabel, year, lang }) {
    const { Paragraph, TextRun, AlignmentType } = window.docx;
    const L = _shared().L;
    return [
      new Paragraph({ spacing: { before: 1200 }, children: [] }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        children: [new TextRun({ text: L(lang, "Cadre logique", "Logical Framework", "Marco lógico"), bold: true, size: 44, color: PRIMARY })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        spacing: { after: 300 },
        children: [new TextRun({ text: L(lang, "Document de référence projet", "Project reference document", "Documento de referencia del proyecto"), italics: true, size: 22, color: _shared().COLORS.MUTED })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        spacing: { after: 600, before: 800 },
        children: [new TextRun({ text: scopeLabel || L(lang, "Portefeuille", "Portfolio", "Cartera"), bold: true, size: 28 })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        spacing: { after: 200 },
        children: [new TextRun({ text: orgName || "", size: 24, color: _shared().COLORS.MUTED })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        spacing: { before: 600, after: 200 },
        children: [new TextRun({ text: (L(lang, "Exercice", "Fiscal year", "Ejercicio") + " : " + year), size: 22 })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        spacing: { after: 1600 },
        children: [new TextRun({ text: L(lang, "Document généré le ", "Generated on ", "Documento generado el ") + new Date().toLocaleDateString(window.L("fr-FR", "en-US", "es-ES")), size: 20, italics: true, color: _shared().COLORS.MUTED })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        children: [new TextRun({ text: L(lang, "Généré automatiquement par MELR · REFT Africa", "Auto-generated by MELR · REFT Africa", "Generado automáticamente por MELR · REFT Africa"), size: 18, color: _shared().COLORS.MUTED })],
      }),
    ];
  }

  // ── 4-column matrix for ONE project ─────────────────────────────────
  function buildProjectMatrix({ project, indicators, objectives, toc, assumptions, lang }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, PageBreak, Spacer } = s;
    const out = [];
    out.push(PageBreak());

    const projName = (project && (project.nameFr || project.name_fr || project.name || project.id)) || "—";
    out.push(H(1, (window.L("Projet : ", "Project: ", "Proyecto: ")) + projName, { color: PRIMARY }));
    if (project && (project.code || project.id)) {
      out.push(P((window.L("Code : ", "Code: ", "Código: ")) + (project.code || project.id), { run: { italics: true, color: s.COLORS.MUTED, size: 18 } }));
    }
    out.push(Spacer(120));

    // ── Helpers locaux ──────────────────────────────────────────────────
    const projObjectives  = (objectives  || []).filter((o) => o.project_id === project.uuid || o.project_id === project.id);
    const projToc         = (toc         || []).filter((t) => t.project_id === project.uuid || t.project_id === project.id);
    const projAssumptions = (assumptions || []).filter((a) => a.project_id === project.uuid || a.project_id === project.id);

    const indsByLevel = { Impact: [], Outcome: [], Output: [], Activity: [] };
    (indicators || []).forEach((i) => {
      const pid = _indProjectId(i);
      if (pid && pid !== project.uuid && pid !== project.id) return;
      const lvl = (i.level || "Output").trim();
      if (indsByLevel[lvl]) indsByLevel[lvl].push(i);
      else indsByLevel.Output.push(i);
    });

    function indicatorList(arr) {
      if (!arr || arr.length === 0) return s.L(lang, "À définir", "To be defined", "Por definir");
      return arr.slice(0, 5).map((i) => {
        const code = i.id || i.code || "—";
        const nm   = lang === "fr" ? (i.name_fr || i.name || "—") : (i.name_en || i.name || "—");
        const tgt  = (i.target != null && i.target !== "") ? " (cible : " + i.target + (i.unit ? " " + i.unit : "") + ")" : "";
        return "• " + code + " — " + nm + tgt;
      }).join("\n") + (arr.length > 5 ? "\n…(+" + (arr.length - 5) + ")" : "");
    }

    function movList(arr) {
      if (!arr || arr.length === 0) return s.L(lang, "À définir", "To be defined", "Por definir");
      const movs = arr
        .map((i) => i.means_of_verification || i.meansOfVerification || i.source || null)
        .filter(Boolean);
      if (movs.length === 0) return s.L(lang, "Non précisé", "Not specified", "No especificado");
      const uniq = Array.from(new Set(movs));
      return uniq.slice(0, 4).map((m) => "• " + m).join("\n");
    }

    function objText(kind) {
      const matches = projObjectives.filter((o) => o.kind === kind);
      if (matches.length === 0) {
        if (kind === "general")  return s.L(lang, "Objectif global à définir.",      "Overall objective to be defined.", "Objetivo global por definir.");
        if (kind === "specific") return s.L(lang, "Objectifs spécifiques à définir.", "Specific objectives to be defined.", "Objetivos específicos por definir.");
        if (kind === "result")   return s.L(lang, "Résultats attendus à définir.",   "Expected results to be defined.", "Resultados esperados por definir.");
      }
      return matches.map((o, i) => ((matches.length > 1 ? (i + 1) + ". " : "") + (o.label || "—"))).join("\n");
    }

    function activitiesText() {
      const acts = projToc.filter((t) => t.level === "activity");
      if (acts.length === 0) return s.L(lang, "Activités à définir.", "Activities to be defined.", "Actividades por definir.");
      return acts.slice(0, 8).map((a, i) => (i + 1) + ". " + (a.label || "—")).join("\n")
        + (acts.length > 8 ? "\n…(+" + (acts.length - 8) + ")" : "");
    }

    // Hypothèses : on agrège tout dans la dernière colonne, mais on essaie
    // de les répartir par niveau si le toc en a (`assumption` sur project_toc).
    function assumptionsForLevel(tocLevel) {
      const fromToc = projToc
        .filter((t) => t.level === tocLevel && t.assumption && t.assumption.trim())
        .map((t) => "• " + t.assumption.trim());
      if (fromToc.length > 0) return fromToc.slice(0, 4).join("\n");
      // Fallback : hypothèses globales du projet (sans niveau précis)
      const general = projAssumptions
        .filter((a) => a.status !== "failed")
        .slice(0, 3)
        .map((a) => "• " + (a.statement || "—"));
      return general.length > 0 ? general.join("\n") : s.L(lang, "À définir", "To be defined", "Por definir");
    }

    // ── Construction de la matrice 4 colonnes ───────────────────────────
    const hdr = { bold: true, color: "FFFFFF", fill: PRIMARY };
    const matrixHeader = Row([
      Cell(s.L(lang, "Logique d'intervention",     "Intervention logic", "Lógica de intervención"),       { ...hdr, width: 2400 }),
      Cell(s.L(lang, "Indicateurs vérifiables",    "Verifiable indicators", "Indicadores verificables"),    { ...hdr, width: 3000 }),
      Cell(s.L(lang, "Sources de vérification",    "Sources of verification", "Fuentes de verificación"),  { ...hdr, width: 1960 }),
      Cell(s.L(lang, "Hypothèses / Risques",       "Assumptions / Risks", "Hipótesis / Riesgos"),      { ...hdr, width: 2000 }),
    ]);

    // Quatre lignes — une par niveau de la chaîne logique
    const rows = [matrixHeader,
      // ── Objectif global / Impact ──────────────────────────────────────
      Row([
        Cell([
          new window.docx.Paragraph({
            children: [new window.docx.TextRun({ text: s.L(lang, "OBJECTIF GLOBAL", "OVERALL OBJECTIVE", "OBJETIVO GLOBAL"), bold: true, color: PRIMARY, size: 18 })],
            spacing: { after: 60 },
          }),
          new window.docx.Paragraph({
            children: [new window.docx.TextRun({ text: objText("general"), size: 18 })],
          }),
        ], { width: 2400, fill: ACCENT, vAlign: window.docx.VerticalAlign && window.docx.VerticalAlign.TOP }),
        Cell(indicatorList(indsByLevel.Impact),  { width: 3000, size: 18 }),
        Cell(movList(indsByLevel.Impact),        { width: 1960, size: 18 }),
        Cell(assumptionsForLevel("impact"),       { width: 2000, size: 18 }),
      ]),
      // ── Objectifs spécifiques / Outcomes ──────────────────────────────
      Row([
        Cell([
          new window.docx.Paragraph({
            children: [new window.docx.TextRun({ text: s.L(lang, "OBJECTIFS SPÉCIFIQUES", "SPECIFIC OBJECTIVES", "OBJETIVOS ESPECÍFICOS"), bold: true, color: PRIMARY, size: 18 })],
            spacing: { after: 60 },
          }),
          new window.docx.Paragraph({
            children: [new window.docx.TextRun({ text: objText("specific"), size: 18 })],
          }),
        ], { width: 2400, fill: ACCENT }),
        Cell(indicatorList(indsByLevel.Outcome), { width: 3000, size: 18 }),
        Cell(movList(indsByLevel.Outcome),       { width: 1960, size: 18 }),
        Cell(assumptionsForLevel("outcome"),      { width: 2000, size: 18 }),
      ]),
      // ── Résultats attendus / Outputs ──────────────────────────────────
      Row([
        Cell([
          new window.docx.Paragraph({
            children: [new window.docx.TextRun({ text: s.L(lang, "RÉSULTATS ATTENDUS", "EXPECTED RESULTS", "RESULTADOS ESPERADOS"), bold: true, color: PRIMARY, size: 18 })],
            spacing: { after: 60 },
          }),
          new window.docx.Paragraph({
            children: [new window.docx.TextRun({ text: objText("result"), size: 18 })],
          }),
        ], { width: 2400, fill: ACCENT }),
        Cell(indicatorList(indsByLevel.Output),  { width: 3000, size: 18 }),
        Cell(movList(indsByLevel.Output),        { width: 1960, size: 18 }),
        Cell(assumptionsForLevel("output"),       { width: 2000, size: 18 }),
      ]),
      // ── Activités / niveau opérationnel ───────────────────────────────
      Row([
        Cell([
          new window.docx.Paragraph({
            children: [new window.docx.TextRun({ text: s.L(lang, "ACTIVITÉS", "ACTIVITIES", "ACTIVIDADES"), bold: true, color: PRIMARY, size: 18 })],
            spacing: { after: 60 },
          }),
          new window.docx.Paragraph({
            children: [new window.docx.TextRun({ text: activitiesText(), size: 18 })],
          }),
        ], { width: 2400, fill: ACCENT }),
        Cell(indicatorList(indsByLevel.Activity || []), { width: 3000, size: 18 }),
        Cell(s.L(lang,
          "Journaux d'activités MELR (dates, GPS, photos), feuilles de présence, livrables signés.",
          "MELR activity logs (dates, GPS, photos), attendance sheets, signed deliverables.", "Registros de actividades MELR (fechas, GPS, fotos), hojas de asistencia, entregables firmados."), { width: 1960, size: 18 }),
        Cell(assumptionsForLevel("activity"),     { width: 2000, size: 18 }),
      ]),
    ];

    out.push(Tbl(rows, { columnWidths: [2400, 3000, 1960, 2000] }));
    out.push(Spacer(200));

    // ── Hypothèses globales du projet (table récap) ─────────────────────
    if (projAssumptions && projAssumptions.length > 0) {
      out.push(H(2, s.L(lang, "Hypothèses et conditions", "Assumptions and conditions", "Hipótesis y condiciones"), { color: PRIMARY }));
      const aHdr = { bold: true, color: "FFFFFF", fill: PRIMARY };
      const aRows = [Row([
        Cell(s.L(lang, "Hypothèse",         "Assumption", "Hipótesis"),  { ...aHdr, width: 3800 }),
        Cell(s.L(lang, "Mesure d'atténuation","Mitigation", "Medida de mitigación"), { ...aHdr, width: 3800 }),
        Cell(s.L(lang, "Statut",            "Status", "Estado"),      { ...aHdr, width: 1760, align: window.docx.AlignmentType.CENTER }),
      ])];
      const statusLabel = {
        unverified: { fr: "Non vérifiée", en: "Unverified", es: "No verificada", fill: "F3F4F6", color: s.COLORS.MUTED },
        confirmed:  { fr: "Confirmée",    en: "Confirmed",  es: "Confirmada",    fill: "DCFCE7", color: s.COLORS.GREEN },
        at_risk:    { fr: "Menacée",      en: "At risk",    es: "En riesgo",     fill: "FEF3C7", color: s.COLORS.AMBER },
        failed:     { fr: "Invalidée",    en: "Failed",     es: "No lograda",    fill: "FEE2E2", color: s.COLORS.RED },
      };
      projAssumptions.forEach((a) => {
        const st = statusLabel[a.status] || statusLabel.unverified;
        aRows.push(Row([
          Cell(a.statement  || "—",                                  { width: 3800, size: 18 }),
          Cell(a.mitigation || s.L(lang, "—", "—"),                  { width: 3800, size: 18 }),
          Cell((lang === "es" ? (st.es != null ? st.es : st.en) : lang === "fr" ? st.fr : st.en),                        { width: 1760, size: 18, bold: true, align: window.docx.AlignmentType.CENTER, fill: st.fill, color: st.color }),
        ]));
      });
      out.push(Tbl(aRows, { columnWidths: [3800, 3800, 1760] }));
      out.push(Spacer(200));
    }

    // ── Théorie du changement (cascade Activity → Output → Outcome → Impact) ──
    if (projToc && projToc.length > 0) {
      out.push(H(2, s.L(lang, "Théorie du changement (cascade)", "Theory of Change (cascade)", "Teoría del cambio (cascada)"), { color: PRIMARY }));
      out.push(P(s.L(lang,
        "Chaîne causale Activité → Produit (Output) → Effet (Outcome) → Impact, telle que saisie dans le module Cadre logique du projet.",
        "Causal chain Activity → Output → Outcome → Impact, as captured in the project's Logical Framework module.", "Cadena causal Actividad → Producto (Output) → Efecto (Outcome) → Impacto, tal como se registra en el módulo de Marco lógico del proyecto."),
        { run: { italics: true, color: s.COLORS.MUTED, size: 18 } }));
      const tHdr = { bold: true, color: "FFFFFF", fill: PRIMARY };
      const tRows = [Row([
        Cell(s.L(lang, "Niveau",   "Level", "Nivel"),      { ...tHdr, width: 1400 }),
        Cell(s.L(lang, "Énoncé",   "Statement", "Enunciado"),  { ...tHdr, width: 4400 }),
        Cell(s.L(lang, "Hypothèse pour passer au niveau supérieur", "Assumption to move up", "Hipótesis para pasar al nivel superior"), { ...tHdr, width: 3560 }),
      ])];
      const levelLabel = {
        activity: { fr: "Activité",  en: "Activity", es: "Actividad" },
        output:   { fr: "Produit",   en: "Output",   es: "Producto"  },
        outcome:  { fr: "Effet",     en: "Outcome",  es: "Resultado" },
        impact:   { fr: "Impact",    en: "Impact",   es: "Impacto"   },
      };
      // Ordre logique : activity → output → outcome → impact
      const order = ["activity", "output", "outcome", "impact"];
      const sorted = projToc.slice().sort((a, b) => {
        const ai = order.indexOf(a.level), bi = order.indexOf(b.level);
        if (ai !== bi) return ai - bi;
        return (a.sort_order || 0) - (b.sort_order || 0);
      });
      sorted.forEach((t) => {
        const lvl = levelLabel[t.level] || { fr: t.level, en: t.level };
        tRows.push(Row([
          Cell((lang === "es" ? (lvl.es != null ? lvl.es : lvl.en) : lang === "fr" ? lvl.fr : lvl.en), { width: 1400, size: 18, bold: true, fill: ACCENT, color: PRIMARY }),
          Cell((t.label || "—") + (t.description ? "\n" + t.description : ""), { width: 4400, size: 18 }),
          Cell(t.assumption || s.L(lang, "—", "—"),                              { width: 3560, size: 18 }),
        ]));
      });
      out.push(Tbl(tRows, { columnWidths: [1400, 4400, 3560] }));
    }

    return out;
  }

  // ── Coverage note when scope is empty ─────────────────────────────────
  function buildEmptyState({ lang }) {
    const s = _shared();
    const { H, P } = s;
    return [
      H(1, s.L(lang, "Cadre logique", "Logical Framework", "Marco lógico"), { color: PRIMARY }),
      P(s.L(lang,
        "Aucun projet dans le périmètre sélectionné. Choisissez un projet ou un programme depuis la barre de portée du module Reporting, puis relancez l'export.",
        "No project in the selected scope. Pick a project or programme from the scope bar in the Reporting module, then re-run the export.", "Ningún proyecto en el ámbito seleccionado. Elija un proyecto o un programa desde la barra de alcance del módulo de Reporting y, a continuación, vuelva a ejecutar la exportación.")),
    ];
  }

  // ── PIRS-light : fiche par indicateur (annexe) ────────────────────────
  function buildIndicatorsAnnex({ indicators, lang }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, PageBreak } = s;
    const out = [];
    out.push(PageBreak());
    out.push(H(1, s.L(lang, "Annexe — Fiches indicateurs", "Annex — Indicator sheets", "Anexo — Fichas de indicadores"), { color: PRIMARY }));
    out.push(P(s.L(lang,
      "Fiche synthétique de chaque indicateur du périmètre : code, niveau, unité, base, cible, fréquence de mesure et source de vérification (MoV). Pour la fiche PIRS complète, consulter l'export PMP USAID.",
      "Brief sheet per indicator in scope: code, level, unit, baseline, target, measurement frequency, and means of verification (MoV). For the full PIRS sheet, see the USAID PMP export.", "Ficha sintética de cada indicador del ámbito: código, nivel, unidad, base, meta, frecuencia de medición y fuente de verificación (MoV). Para la ficha PIRS completa, consulte la exportación PMP USAID.")));

    if (!indicators || indicators.length === 0) {
      out.push(P(s.L(lang, "Aucun indicateur dans le périmètre.", "No indicator in scope.", "Ningún indicador en el ámbito."), { run: { italics: true, color: s.COLORS.MUTED } }));
      return out;
    }

    const hdr = { bold: true, color: "FFFFFF", fill: PRIMARY };
    const rows = [Row([
      Cell(s.L(lang, "Code",       "Code"),       { ...hdr, width: 900 }),
      Cell(s.L(lang, "Niveau",     "Level", "Nivel"),      { ...hdr, width: 1000 }),
      Cell(s.L(lang, "Indicateur", "Indicator", "Indicador"),  { ...hdr, width: 3000 }),
      Cell(s.L(lang, "Unité",      "Unit", "Unidad"),       { ...hdr, width: 700, align: window.docx.AlignmentType.CENTER }),
      Cell(s.L(lang, "Base",       "Baseline", "Línea de base"),   { ...hdr, width: 800, align: window.docx.AlignmentType.CENTER }),
      Cell(s.L(lang, "Cible",      "Target", "Meta"),     { ...hdr, width: 800, align: window.docx.AlignmentType.CENTER }),
      Cell(s.L(lang, "Fréquence",  "Frequency", "Frecuencia"),  { ...hdr, width: 900 }),
      Cell(s.L(lang, "Source / MoV","Source / MoV"),{ ...hdr, width: 1260 }),
    ])];
    indicators.forEach((i) => {
      const nm = lang === "fr" ? (i.name_fr || i.name || "—") : (i.name_en || i.name || "—");
      const mov = i.means_of_verification || i.meansOfVerification || i.source || "—";
      rows.push(Row([
        Cell(i.id || i.code || "—",                              { width: 900,  size: 18, bold: true }),
        Cell(i.level || "—",                                      { width: 1000, size: 18 }),
        Cell(nm,                                                  { width: 3000, size: 18 }),
        Cell(i.unit || "—",                                       { width: 700,  size: 18, align: window.docx.AlignmentType.CENTER }),
        Cell(i.baseline != null ? String(i.baseline) : "—",       { width: 800,  size: 18, align: window.docx.AlignmentType.CENTER }),
        Cell(i.target   != null ? String(i.target)   : "—",       { width: 800,  size: 18, align: window.docx.AlignmentType.CENTER }),
        Cell(i.frequency || "—",                                  { width: 900,  size: 18 }),
        Cell(mov,                                                 { width: 1260, size: 18 }),
      ]));
    });
    out.push(Tbl(rows, { columnWidths: [900, 1000, 3000, 700, 800, 800, 900, 1260] }));
    return out;
  }

  // ── Entry point ───────────────────────────────────────────────────────
  async function exportLogframe(opts) {
    opts = opts || {};
    const lang   = opts.lang || "fr";
    const projs  = opts.projects   || [];
    const inds   = opts.indicators || [];
    const year   = opts.year || new Date().getFullYear();
    const scopeLabel = opts.scopeLabel || "";
    const orgName    = opts.orgName    || "";
    const filename   = opts.filename   || "Cadre-Logique";

    if (!_ok()) {
      alert(window.L("Module docx non disponible. Rechargez la page et réessayez.", "docx module not available. Reload the page and retry.", "Módulo docx no disponible. Recargue la página e inténtelo de nuevo."));
      return;
    }

    const s = _shared();
    const { Document } = window.docx;

    // Fetch cadrage for every project in scope (in parallel)
    const projectIds = Array.from(new Set(projs.map((p) => p.uuid || p.id).filter(Boolean)));
    const { objectives, toc, assumptions } = await _fetchCadrage(projectIds);

    // Assemble doc body
    let body = buildCover({ orgName, scopeLabel, year, lang });
    if (projs.length === 0) {
      body = body.concat(buildEmptyState({ lang }));
    } else {
      projs.forEach((p) => {
        body = body.concat(buildProjectMatrix({
          project: p, indicators: inds, objectives, toc, assumptions, lang,
        }));
      });
      // Annexe consolidée — indicateurs de tout le périmètre
      body = body.concat(buildIndicatorsAnnex({ indicators: inds, lang }));
    }

    const doc = new Document({
      creator: "MELR · REFT Africa",
      title:   (window.L("Cadre logique", "Logical Framework", "Marco lógico")) + " — " + (scopeLabel || "Portfolio"),
      description: (window.L("Cadre logique généré par MELR", "Logical Framework generated by MELR", "Marco lógico generado por MELR")),
      styles: { default: { document: { run: { font: "Calibri", size: 22 } } } },
      sections: [{ properties: {}, children: body }],
    });

    await s.saveDocx(doc, filename, lang);
  }

  window.exportLogframe = exportLogframe;
})();
