/* global window */
// ============================================================================
// MELR · Banque africaine de développement (BAD / AfDB) — IPR export
// ----------------------------------------------------------------------------
// Génère un « Implementation Progress and Results Report » (IPR) au format
// .docx. L'IPR est l'instrument de supervision périodique de la BAD ; il
// s'adosse au RLF (Results-Based Logical Framework) et porte les deux
// notations qui commandent le dialogue avec la Banque : IP (progrès de mise
// en œuvre) et DO (réalisation de l'objectif de développement).
//
// Structure générée :
//   A. Données de base du projet
//   B. Progrès vers l'objectif de développement
//   C. Cadre logique axé sur les résultats (RLF) — Impact / Effets / Produits
//   D. Notations de performance (échelle BAD à 4 points)
//   E. Questions de mise en œuvre et actions convenues
//   F. Sauvegardes environnementales et sociales
//   G. Décaissements et passation des marchés
//   H. Leçons apprises
//
// ⚠️ La structure suit le cadre PUBLIC de la BAD (IPR + RLF). Aucun gabarit
// officiel n'était disponible à la construction du module : les libellés et la
// numérotation des sections doivent être confrontés au formulaire en vigueur
// fourni par la Banque avant toute transmission.
//
// Point d'entrée : window.exportAfdbIpr({...})
// Utilise les primitives partagées de window.melrDonor.
// ============================================================================

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

  const AFDB_GREEN = "0A6E4A";   // vert institutionnel BAD
  const AFDB_ORANGE = "C8622C";  // orange d'accentuation

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

  // Échelle de notation BAD : 4 points, 4 étant le meilleur. Les seuils
  // d'atteinte sont publiés dans le document : une note dérivée d'un calcul
  // non explicité se conteste, et l'agent de supervision doit pouvoir la
  // corriger en connaissance de cause.
  function ratingScale(lang) {
    const L = _shared().L;
    return [
      ["4", L(lang, "Très satisfaisant", "Highly satisfactory", "Muy satisfactorio"), "≥ 95 %"],
      ["3", L(lang, "Satisfaisant", "Satisfactory", "Satisfactorio"), "80 – 94 %"],
      ["2", L(lang, "Insatisfaisant", "Unsatisfactory", "Insatisfactorio"), "60 – 79 %"],
      ["1", L(lang, "Très insatisfaisant", "Highly unsatisfactory", "Muy insatisfactorio"), "< 60 %"],
    ];
  }

  // Traduit un % d'atteinte moyen en note BAD sur 4. Rend la notation
  // reproductible plutôt que déclarative : l'agent de supervision peut la
  // corriger, mais elle part d'une base chiffrée issue des indicateurs.
  function ratingFromPct(pct) {
    if (pct == null) return null;
    if (pct >= 95) return 4;
    if (pct >= 80) return 3;
    if (pct >= 60) return 2;
    return 1;
  }

  function avgPct(indicators, year, periods) {
    const s = _shared();
    const vals = [];
    (indicators || []).forEach((ind) => {
      const perf = s.computePerformance(ind, year, periods);
      const last = perf.pct[perf.pct.length - 1];
      if (last != null && isFinite(last)) vals.push(last);
    });
    if (!vals.length) return null;
    return Math.round(vals.reduce((a, b) => a + b, 0) / vals.length);
  }

  // ── Page de garde ──────────────────────────────────────────────────────
  function buildCover({ orgName, scopeLabel, year, lang }) {
    const { Paragraph, TextRun, AlignmentType } = window.docx;
    const s = _shared();
    const L = s.L;
    const C = (text, opts) => new Paragraph({
      alignment: AlignmentType.CENTER,
      spacing: { after: (opts && opts.after) || 200, before: (opts && opts.before) || 0 },
      children: [new TextRun({ text, ...(opts && opts.run ? opts.run : {}) })],
    });
    return [
      new Paragraph({ spacing: { before: 1200 }, children: [] }),
      C("Banque africaine de développement", { run: { bold: true, size: 48, color: AFDB_GREEN }, after: 60 }),
      C("African Development Bank Group", { run: { italics: true, size: 22, color: s.COLORS.MUTED }, after: 500 }),
      C(L(lang, "Rapport sur l'état d'exécution et les résultats (IPR)",
                "Implementation Progress and Results Report (IPR)",
                "Informe de progreso y resultados de la ejecución (IPR)"),
        { run: { bold: true, size: 30, color: AFDB_GREEN }, after: 400 }),
      C(scopeLabel || L(lang, "Projet", "Project", "Proyecto"), { run: { bold: true, size: 28 } }),
      C(orgName || L(lang, "Organe d'exécution", "Implementing agency", "Organismo ejecutor"),
        { run: { size: 24, color: s.COLORS.MUTED } }),
      C(L(lang, "Période de référence", "Reporting period", "Período de referencia") + " : " + year,
        { run: { size: 22 }, before: 700 }),
      C(L(lang, "Document généré le ", "Generated on ", "Documento generado el ") +
        new Date().toLocaleDateString(window.L("fr-FR", "en-US", "es-ES")),
        { run: { size: 20, italics: true, color: s.COLORS.MUTED }, after: 1500 }),
      C(L(lang, "Généré automatiquement par MELR · REFT Africa",
                "Auto-generated by MELR · REFT Africa",
                "Generado automáticamente por MELR · REFT Africa"),
        { run: { size: 18, color: s.COLORS.MUTED } }),
    ];
  }

  // ── A. Données de base ─────────────────────────────────────────────────
  function buildProjectData({ projects, scopeLabel, orgName, year, lang }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, Spacer } = s;
    const out = [];
    const p = (projects && projects[0]) || {};
    out.push(H(1, s.L(lang, "A. Données de base du projet", "A. Basic project data", "A. Datos básicos del proyecto"), { color: AFDB_GREEN }));
    out.push(P(s.L(lang,
      "Cette section identifie le projet et son cadre de financement. Elle constitue l'en-tête de référence de tout échange avec la Banque.",
      "This section identifies the project and its financing framework. It is the reference header for every exchange with the Bank.",
      "Esta sección identifica el proyecto y su marco de financiamiento. Constituye el encabezado de referencia de todo intercambio con el Banco.")));
    const row = (k, v) => Row([
      Cell(k, { width: 3200, bold: true, size: 20, fill: "F2F7F4" }),
      Cell(v || "—", { width: 6160, size: 20 }),
    ]);
    out.push(Tbl([
      row(s.L(lang, "Intitulé du projet", "Project title", "Título del proyecto"), scopeLabel),
      row(s.L(lang, "Organe d'exécution", "Executing agency", "Organismo ejecutor"), orgName),
      row(s.L(lang, "Code / référence", "Code / reference", "Código / referencia"), p.code),
      row(s.L(lang, "Secteur", "Sector", "Sector"), p.sector),
      row(s.L(lang, "Pays", "Country", "País"), p.country),
      row(s.L(lang, "Période de référence", "Reporting period", "Período de referencia"), String(year)),
      row(s.L(lang, "Guichet de financement", "Financing window", "Ventanilla de financiamiento"),
          s.L(lang, "À préciser (BAD / FAD / Fonds fiduciaire)", "To be specified (ADB / ADF / Trust Fund)", "Por precisar (BAD / FAD / Fondo fiduciario)")),
    ], { columnWidths: [3200, 6160] }));
    out.push(Spacer());
    return out;
  }

  // ── B. Progrès vers l'objectif de développement ────────────────────────
  function buildProgress({ indicators, year, lang, periods }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, Spacer } = s;
    const out = [];
    out.push(H(1, s.L(lang, "B. Progrès vers l'objectif de développement",
                            "B. Progress towards the development objective",
                            "B. Avances hacia el objetivo de desarrollo"), { color: AFDB_GREEN }));
    const bag = s.groupByLevel(indicators);
    const avgOutcome = avgPct(bag.Outcome, year, periods);
    const avgOutput = avgPct(bag.Output, year, periods);
    const avgAll = avgPct(indicators, year, periods);
    out.push(P(s.L(lang,
      "Le tableau ci-dessous résume l'avancement mesuré sur les indicateurs suivis dans MELR, par niveau de la chaîne de résultats. Les pourcentages sont calculés sur la dernière période renseignée.",
      "The table below summarises measured progress on the indicators tracked in MELR, by results-chain level. Percentages are computed on the latest reported period.",
      "El cuadro siguiente resume el avance medido en los indicadores seguidos en MELR, por nivel de la cadena de resultados. Los porcentajes se calculan sobre el último período informado.")));
    const pctTxt = (v) => (v == null ? "—" : v + " %");
    out.push(Tbl([
      Row([
        Cell(s.L(lang, "Niveau", "Level", "Nivel"), { width: 3000, bold: true, fill: AFDB_GREEN, color: "FFFFFF" }),
        Cell(s.L(lang, "Indicateurs suivis", "Indicators tracked", "Indicadores seguidos"), { width: 2200, bold: true, fill: AFDB_GREEN, color: "FFFFFF" }),
        Cell(s.L(lang, "Atteinte moyenne", "Average achievement", "Logro medio"), { width: 2200, bold: true, fill: AFDB_GREEN, color: "FFFFFF" }),
        Cell(s.L(lang, "Note BAD", "AfDB rating", "Calificación BAD"), { width: 1960, bold: true, fill: AFDB_GREEN, color: "FFFFFF" }),
      ]),
      Row([
        Cell(s.L(lang, "Effets (Outcomes)", "Outcomes", "Efectos (Outcomes)"), { width: 3000, bold: true }),
        Cell(String(bag.Outcome.length), { width: 2200 }),
        Cell(pctTxt(avgOutcome), { width: 2200 }),
        Cell(ratingFromPct(avgOutcome) == null ? "—" : String(ratingFromPct(avgOutcome)), { width: 1960, bold: true }),
      ]),
      Row([
        Cell(s.L(lang, "Produits (Outputs)", "Outputs", "Productos (Outputs)"), { width: 3000, bold: true }),
        Cell(String(bag.Output.length), { width: 2200 }),
        Cell(pctTxt(avgOutput), { width: 2200 }),
        Cell(ratingFromPct(avgOutput) == null ? "—" : String(ratingFromPct(avgOutput)), { width: 1960, bold: true }),
      ]),
      Row([
        Cell(s.L(lang, "Ensemble du portefeuille", "Whole portfolio", "Conjunto de la cartera"), { width: 3000, bold: true, fill: "F2F7F4" }),
        Cell(String((indicators || []).length), { width: 2200, fill: "F2F7F4" }),
        Cell(pctTxt(avgAll), { width: 2200, fill: "F2F7F4", bold: true }),
        Cell(ratingFromPct(avgAll) == null ? "—" : String(ratingFromPct(avgAll)), { width: 1960, fill: "F2F7F4", bold: true }),
      ]),
    ], { columnWidths: [3000, 2200, 2200, 1960] }));
    out.push(P(s.L(lang,
      "Les notes proposées sont dérivées mécaniquement du taux d'atteinte ; elles constituent une base de discussion et non un jugement définitif de l'agent de supervision.",
      "The proposed ratings are derived mechanically from the achievement rate; they are a basis for discussion, not a final judgement by the supervision officer.",
      "Las calificaciones propuestas se derivan mecánicamente de la tasa de logro; constituyen una base de discusión y no un juicio definitivo del oficial de supervisión."),
      { run: { italics: true, color: s.COLORS.MUTED, size: 18 } }));
    out.push(Spacer());
    return out;
  }

  // ── C. Cadre logique axé sur les résultats (RLF) ───────────────────────
  function buildRlf({ indicators, year, lang, periods }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, Spacer } = s;
    const out = [];
    out.push(H(1, s.L(lang, "C. Cadre logique axé sur les résultats (RLF)",
                            "C. Results-Based Logical Framework (RLF)",
                            "C. Marco lógico basado en resultados (RLF)"), { color: AFDB_GREEN }));
    out.push(P(s.L(lang,
      "Le RLF articule la chaîne de résultats du projet. Chaque indicateur y figure avec sa référence, sa cible, sa valeur atteinte et son taux d'atteinte, sur les périodes suivies.",
      "The RLF sets out the project results chain. Each indicator appears with its baseline, target, actual value and achievement rate, over the tracked periods.",
      "El RLF articula la cadena de resultados del proyecto. Cada indicador figura con su línea de base, meta, valor alcanzado y tasa de logro, en los períodos seguidos.")));

    const bag = s.groupByLevel(indicators);
    const levels = [
      ["Impact", s.L(lang, "Impact", "Impact", "Impacto")],
      ["Outcome", s.L(lang, "Effets", "Outcomes", "Efectos")],
      ["Output", s.L(lang, "Produits", "Outputs", "Productos")],
    ];
    const Y = periods;

    levels.forEach(([key, label]) => {
      const list = bag[key] || [];
      if (!list.length) return;
      out.push(H(2, label, { color: AFDB_ORANGE }));
      const header = [
        Cell(s.L(lang, "Code", "Code", "Código"), { width: 800, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 18 }),
        Cell(s.L(lang, "Indicateur", "Indicator", "Indicador"), { width: 2600, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 18 }),
        Cell(s.L(lang, "Unité", "Unit", "Unidad"), { width: 600, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 18 }),
        Cell(s.L(lang, "Réf.", "Base.", "Ref."), { width: 800, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 18 }),
      ];
      const perf0 = s.computePerformance(list[0], year, Y);
      for (let i = 0; i < Y; i++) {
        header.push(Cell(perf0.years[i] + " " + s.L(lang, "cible", "target", "meta"), { width: 800, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 16 }));
        header.push(Cell(perf0.years[i] + " " + s.L(lang, "réel", "actual", "real"), { width: 800, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 16 }));
        header.push(Cell("%", { width: 520, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 16 }));
      }
      const rows = [Row(header)];
      list.forEach((ind) => rows.push(s.iptRow(ind, s.computePerformance(ind, year, Y), lang, Y)));
      out.push(Tbl(rows));
      out.push(Spacer());
    });

    if (!(indicators || []).length) {
      out.push(P(s.L(lang, "Aucun indicateur n'est rattaché au périmètre sélectionné.",
                           "No indicator is attached to the selected scope.",
                           "Ningún indicador está vinculado al alcance seleccionado."),
        { run: { italics: true, color: s.COLORS.MUTED } }));
    }
    return out;
  }

  // ── D. Notations de performance ────────────────────────────────────────
  function buildRatings({ indicators, year, lang, periods }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, Spacer } = s;
    const out = [];
    out.push(H(1, s.L(lang, "D. Notations de performance", "D. Performance ratings", "D. Calificaciones de desempeño"), { color: AFDB_GREEN }));
    out.push(P(s.L(lang,
      "La Banque note séparément le progrès de mise en œuvre (IP) et la probabilité d'atteindre l'objectif de développement (DO), sur une échelle à quatre points.",
      "The Bank rates implementation progress (IP) and the likelihood of achieving the development objective (DO) separately, on a four-point scale.",
      "El Banco califica por separado el progreso de la ejecución (IP) y la probabilidad de alcanzar el objetivo de desarrollo (DO), en una escala de cuatro puntos.")));

    const bag = s.groupByLevel(indicators);
    const ip = ratingFromPct(avgPct(bag.Output, year, periods));
    const dov = ratingFromPct(avgPct(bag.Outcome, year, periods));
    const scale = ratingScale(lang);
    const lbl = (n) => { const f = scale.find((x) => x[0] === String(n)); return f ? f[1] : "—"; };

    out.push(Tbl([
      Row([
        Cell(s.L(lang, "Dimension notée", "Rated dimension", "Dimensión calificada"), { width: 4200, bold: true, fill: AFDB_GREEN, color: "FFFFFF" }),
        Cell(s.L(lang, "Note proposée", "Proposed rating", "Calificación propuesta"), { width: 1800, bold: true, fill: AFDB_GREEN, color: "FFFFFF" }),
        Cell(s.L(lang, "Signification", "Meaning", "Significado"), { width: 3360, bold: true, fill: AFDB_GREEN, color: "FFFFFF" }),
      ]),
      Row([
        Cell(s.L(lang, "IP — Progrès de mise en œuvre (produits)", "IP — Implementation progress (outputs)", "IP — Progreso de la ejecución (productos)"), { width: 4200 }),
        Cell(ip == null ? "—" : String(ip), { width: 1800, bold: true }),
        Cell(ip == null ? "—" : lbl(ip), { width: 3360 }),
      ]),
      Row([
        Cell(s.L(lang, "DO — Objectif de développement (effets)", "DO — Development objective (outcomes)", "DO — Objetivo de desarrollo (efectos)"), { width: 4200 }),
        Cell(dov == null ? "—" : String(dov), { width: 1800, bold: true }),
        Cell(dov == null ? "—" : lbl(dov), { width: 3360 }),
      ]),
    ], { columnWidths: [4200, 1800, 3360] }));
    out.push(Spacer());

    out.push(H(2, s.L(lang, "Échelle de notation", "Rating scale", "Escala de calificación"), { color: AFDB_ORANGE }));
    out.push(P(s.L(lang,
      "Les notes proposées ci-dessus sont dérivées du taux d'atteinte moyen des indicateurs, selon les seuils suivants. L'agent de supervision reste libre de les corriger : le calcul fournit un point de départ traçable, non un verdict.",
      "The ratings proposed above are derived from the average indicator achievement rate, using the thresholds below. The supervision officer remains free to adjust them: the calculation provides a traceable starting point, not a verdict.",
      "Las calificaciones propuestas arriba se derivan de la tasa media de logro de los indicadores, según los umbrales siguientes. El oficial de supervisión puede corregirlas: el cálculo aporta un punto de partida trazable, no un veredicto.")));
    out.push(Tbl([
      Row([
        Cell(s.L(lang, "Note", "Rating", "Calificación"), { width: 1400, bold: true, fill: "F2F7F4" }),
        Cell(s.L(lang, "Libellé", "Label", "Etiqueta"), { width: 5560, bold: true, fill: "F2F7F4" }),
        Cell(s.L(lang, "Taux d'atteinte", "Achievement rate", "Tasa de logro"), { width: 2400, bold: true, fill: "F2F7F4" }),
      ]),
    ].concat(scale.map(([n, label, band]) => Row([
      Cell(n, { width: 1400, bold: true }),
      Cell(label, { width: 5560 }),
      Cell(band, { width: 2400 }),
    ]))), { columnWidths: [1400, 5560, 2400] }));
    out.push(Spacer());
    return out;
  }

  // ── E. Questions et actions convenues ──────────────────────────────────
  function buildIssues({ lang }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, Spacer } = s;
    const out = [];
    out.push(H(1, s.L(lang, "E. Questions de mise en œuvre et actions convenues",
                            "E. Implementation issues and agreed actions",
                            "E. Cuestiones de ejecución y acciones acordadas"), { color: AFDB_GREEN }));
    out.push(P(s.L(lang,
      "Cette section recense les difficultés rencontrées et les actions arrêtées avec la Banque, chacune assortie d'un responsable et d'une échéance. Elle se renseigne à partir du module Risques et du registre d'actions de MELR.",
      "This section lists the difficulties encountered and the actions agreed with the Bank, each with an owner and a deadline. It is populated from the MELR Risks module and action register.",
      "Esta sección enumera las dificultades encontradas y las acciones acordadas con el Banco, cada una con un responsable y un plazo. Se completa a partir del módulo de Riesgos y el registro de acciones de MELR.")));
    const th = (t, w) => Cell(t, { width: w, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 18 });
    out.push(Tbl([
      Row([
        th(s.L(lang, "Question / difficulté", "Issue", "Cuestión / dificultad"), 3400),
        th(s.L(lang, "Action convenue", "Agreed action", "Acción acordada"), 3400),
        th(s.L(lang, "Responsable", "Owner", "Responsable"), 1400),
        th(s.L(lang, "Échéance", "Deadline", "Plazo"), 1160),
      ]),
      Row([Cell("", { width: 3400 }), Cell("", { width: 3400 }), Cell("", { width: 1400 }), Cell("", { width: 1160 })]),
      Row([Cell("", { width: 3400 }), Cell("", { width: 3400 }), Cell("", { width: 1400 }), Cell("", { width: 1160 })]),
      Row([Cell("", { width: 3400 }), Cell("", { width: 3400 }), Cell("", { width: 1400 }), Cell("", { width: 1160 })]),
    ], { columnWidths: [3400, 3400, 1400, 1160] }));
    out.push(P(s.L(lang, "(À compléter par l'agent de supervision.)", "(To be completed by the supervision officer.)", "(A completar por el oficial de supervisión.)"),
      { run: { italics: true, color: s.COLORS.MUTED, size: 18 } }));
    out.push(Spacer());
    return out;
  }

  // ── F. Sauvegardes ─────────────────────────────────────────────────────
  function buildSafeguards({ lang }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, Spacer } = s;
    const out = [];
    out.push(H(1, s.L(lang, "F. Sauvegardes environnementales et sociales",
                            "F. Environmental and social safeguards",
                            "F. Salvaguardias ambientales y sociales"), { color: AFDB_GREEN }));
    out.push(P(s.L(lang,
      "Le système de sauvegardes intégré de la Banque classe les opérations en quatre catégories selon l'ampleur des risques. La conformité est appréciée à chaque supervision.",
      "The Bank's integrated safeguards system classifies operations into four categories according to the scale of risks. Compliance is assessed at each supervision.",
      "El sistema de salvaguardias integrado del Banco clasifica las operaciones en cuatro categorías según la magnitud de los riesgos. El cumplimiento se evalúa en cada supervisión.")));
    const th = (t, w) => Cell(t, { width: w, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 18 });
    out.push(Tbl([
      Row([
        th(s.L(lang, "Élément", "Item", "Elemento"), 4200),
        th(s.L(lang, "Statut", "Status", "Estado"), 5160),
      ]),
      Row([Cell(s.L(lang, "Catégorie environnementale et sociale", "Environmental and social category", "Categoría ambiental y social"), { width: 4200, bold: true }), Cell(s.L(lang, "À préciser (1, 2, 3 ou 4)", "To be specified (1, 2, 3 or 4)", "Por precisar (1, 2, 3 o 4)"), { width: 5160 })]),
      Row([Cell(s.L(lang, "EIES / PGES disponible", "ESIA / ESMP available", "EIAS / PGAS disponible"), { width: 4200, bold: true }), Cell("", { width: 5160 })]),
      Row([Cell(s.L(lang, "Plan d'action de réinstallation", "Resettlement action plan", "Plan de acción de reasentamiento"), { width: 4200, bold: true }), Cell("", { width: 5160 })]),
      Row([Cell(s.L(lang, "Conformité constatée à la supervision", "Compliance observed at supervision", "Cumplimiento constatado en la supervisión"), { width: 4200, bold: true }), Cell("", { width: 5160 })]),
    ], { columnWidths: [4200, 5160] }));
    out.push(Spacer());
    return out;
  }

  // ── G. Décaissements et passation ──────────────────────────────────────
  function buildDisbursement({ projects, lang }) {
    const s = _shared();
    const { H, P, Cell, Row, Tbl, Spacer } = s;
    const out = [];
    out.push(H(1, s.L(lang, "G. Décaissements et passation des marchés",
                            "G. Disbursements and procurement",
                            "G. Desembolsos y adquisiciones"), { color: AFDB_GREEN }));
    out.push(P(s.L(lang,
      "L'exécution financière est un déterminant central des notations IP. Le tableau rapproche l'engagement, le décaissement cumulé et le taux d'exécution.",
      "Financial execution is a central determinant of IP ratings. The table reconciles commitment, cumulative disbursement and execution rate.",
      "La ejecución financiera es un determinante central de las calificaciones IP. El cuadro concilia el compromiso, el desembolso acumulado y la tasa de ejecución.")));
    const th = (t, w) => Cell(t, { width: w, bold: true, fill: AFDB_GREEN, color: "FFFFFF", size: 18 });
    const p = (projects && projects[0]) || {};
    out.push(Tbl([
      Row([
        th(s.L(lang, "Rubrique", "Item", "Rubro"), 4200),
        th(s.L(lang, "Montant", "Amount", "Monto"), 2600),
        th(s.L(lang, "Taux", "Rate", "Tasa"), 2560),
      ]),
      Row([Cell(s.L(lang, "Engagement total", "Total commitment", "Compromiso total"), { width: 4200, bold: true }), Cell(p.budget != null ? s.fmtNum(p.budget) : "—", { width: 2600 }), Cell("—", { width: 2560 })]),
      Row([Cell(s.L(lang, "Décaissement cumulé", "Cumulative disbursement", "Desembolso acumulado"), { width: 4200, bold: true }), Cell(p.spent != null ? s.fmtNum(p.spent) : "—", { width: 2600 }), Cell(
        (p.budget && p.spent != null) ? Math.round((Number(p.spent) / Number(p.budget)) * 100) + " %" : "—", { width: 2560, bold: true })]),
      Row([Cell(s.L(lang, "Marchés attribués", "Contracts awarded", "Contratos adjudicados"), { width: 4200, bold: true }), Cell("", { width: 2600 }), Cell("", { width: 2560 })]),
    ], { columnWidths: [4200, 2600, 2560] }));
    out.push(Spacer());
    return out;
  }

  // ── H. Leçons apprises ─────────────────────────────────────────────────
  function buildLessons({ lang }) {
    const s = _shared();
    const { H, P } = s;
    const out = [];
    out.push(H(1, s.L(lang, "H. Leçons apprises", "H. Lessons learned", "H. Lecciones aprendidas"), { color: AFDB_GREEN }));
    out.push(P(s.L(lang,
      "Les enseignements consignés ici alimentent le rapport d'achèvement (PCR) et la conception des opérations suivantes.",
      "The lessons recorded here feed the project completion report (PCR) and the design of subsequent operations.",
      "Las lecciones registradas aquí alimentan el informe de terminación (PCR) y el diseño de operaciones posteriores.")));
    [
      s.L(lang, "Ce qui a fonctionné et mérite d'être reproduit", "What worked and is worth replicating", "Lo que funcionó y merece replicarse"),
      s.L(lang, "Ce qui a freiné l'exécution", "What slowed execution down", "Lo que frenó la ejecución"),
      s.L(lang, "Ajustements recommandés pour la période suivante", "Recommended adjustments for the next period", "Ajustes recomendados para el próximo período"),
    ].forEach((t) => out.push(P("• " + t, { run: { size: 22 } })));
    out.push(P(s.L(lang, "(À compléter à partir du module Apprentissage de MELR.)",
                         "(To be completed from the MELR Learning module.)",
                         "(A completar a partir del módulo de Aprendizaje de MELR.)"),
      { run: { italics: true, color: s.COLORS.MUTED } }));
    return out;
  }

  // ── Assemblage ─────────────────────────────────────────────────────────
  function buildDoc({ projects, indicators, activities, stakeholders, donorId, donorLabel, currency, year, scopeLabel, orgName, lang, periods }) {
    if (!_ok()) throw new Error("melrDonor shared module not loaded");
    const { Document, Footer, PageNumber, TextRun, Paragraph, AlignmentType } = window.docx;
    const s = _shared();
    const children = [];
    children.push(...buildCover({ orgName, scopeLabel, year, lang }));
    children.push(s.PageBreak());
    children.push(...buildProjectData({ projects, scopeLabel, orgName, year, lang }));
    children.push(...buildProgress({ indicators, year, lang, periods }));
    children.push(...buildRlf({ indicators, year, lang, periods }));
    // Activités du PTBA financées par la Banque : montants saisis source par
    // source, donc aucun prorata. Section fournie par le socle partagé pour
    // que tous les bailleurs présentent le même calcul.
    if (s.buildActivitySection && (activities || []).length) {
      children.push(...s.buildActivitySection({
        activities, stakeholders, donorId, donorLabel: donorLabel || "la BAD",
        lang, color: AFDB_GREEN, currency,
        title: s.L(lang, "C-bis. Activités du PTBA financées par la Banque",
                         "C-bis. Work plan activities financed by the Bank",
                         "C-bis. Actividades del plan financiadas por el Banco"),
      }));
    }
    children.push(...buildRatings({ indicators, year, lang, periods }));
    children.push(...buildIssues({ lang }));
    children.push(...buildSafeguards({ lang }));
    children.push(...buildDisbursement({ projects, lang }));
    children.push(...buildLessons({ lang }));
    return new Document({
      creator: "MELR",
      title: "AfDB IPR — " + (scopeLabel || "Project") + " — " + year,
      description: "AfDB Implementation Progress and Results Report auto-generated by MELR",
      styles: { default: { document: { run: { font: "Calibri", size: 22 } } } },
      sections: [{
        properties: { page: { margin: { top: 1200, right: 1200, bottom: 1200, left: 1200 } } },
        footers: {
          default: new Footer({
            children: [new Paragraph({
              alignment: AlignmentType.CENTER,
              children: [
                new TextRun({ text: "BAD · IPR · " + (scopeLabel || "Projet") + " · " + year + " · ", color: s.COLORS.MUTED, size: 18 }),
                new TextRun({ children: [PageNumber.CURRENT], color: s.COLORS.MUTED, size: 18 }),
                new TextRun({ text: " / ", color: s.COLORS.MUTED, size: 18 }),
                new TextRun({ children: [PageNumber.TOTAL_PAGES], color: s.COLORS.MUTED, size: 18 }),
              ],
            })],
          }),
        },
        children,
      }],
    });
  }

  async function exportAfdbIpr(args) {
    args = args || {};
    if (!_ok()) { alert((args.lang || "fr") === "fr" ? "Module partagé indisponible." : "Shared module unavailable."); return; }
    const s = _shared();
    try {
      const year = args.year || new Date().getFullYear();
      const doc = buildDoc({
        projects: args.projects || [],
        indicators: args.indicators || [],
        activities: args.activities || [],
        stakeholders: args.stakeholders || null,
        donorId: args.fundingSourceId || null,
        donorLabel: args.fundingSourceLabel || null,
        currency: args.currency || "XOF",
        year,
        scopeLabel: args.scopeLabel || s.L(args.lang, "Projet", "Project", "Proyecto"),
        orgName: args.orgName || "",
        lang: args.lang || "fr",
        periods: args.periods || 3,
      });
      await s.saveDocx(doc, (args.filename || ("BAD-IPR-" + year)), args.lang);
    } catch (e) {
      console.error("[AfDB export]", e);
      alert(((args.lang || "fr") === "fr" ? "Erreur BAD : " : "AfDB error: ") + e.message);
    }
  }
  window.exportAfdbIpr = exportAfdbIpr;
})();
