/* global React, window */
// ============================================================================
// MELR · USAID Performance Monitoring Plan (PMP) export
// ----------------------------------------------------------------------------
// Generates a USAID-formatted Performance Monitoring Plan as a .docx file.
//
// A USAID PMP is required by every USAID-funded activity (ADS 201). It is
// the most time-consuming MEL deliverable for implementing partners — they
// typically rebuild it manually each year from spreadsheets. Generating it
// in one click from MELR's existing indicator data is a major differentiator.
//
// Structure produced (per USAID ADS 201mah, "Mission Performance Monitoring
// and Evaluation Plan"):
//
//   1. Cover Page
//   2. Table of Contents
//   3. Activity Overview (auto-filled from project metadata)
//   4. Results Framework (Goal → SO → IR → Output, hierarchical)
//   5. Indicator Performance Tracking Table (IPTT)
//        Wide pivot: Indicator × (Baseline, Y1 Target, Y1 Actual, Y1 %, …)
//        Color coded: green ≥ 90 %, amber 70-89 %, red < 70 %.
//   6. Performance Indicator Reference Sheets (PIRS) — one per indicator
//        USAID standard ADS template: definition, unit, disaggregation,
//        data source, frequency, responsible, baseline+targets+actuals.
//   7. Data Quality Assessment (DQA) narrative
//        Five USAID dimensions: validity, reliability, integrity, precision,
//        timeliness.
//
// Entry point exposed on window:
//   await window.exportUsaidPmp({projects, indicators, scope, year, lang, orgName})
//
// All text is bilingual FR/EN — choose at call time via lang.
// ============================================================================

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

  // Bail early if docx-js is not loaded — the caller is expected to gate
  // the button on window.exportReportingDocxAvailable, but we double-check.
  function _docxOk() {
    return !!(window.docx && window.docx.Document && window.docx.Packer);
  }

  // ── i18n helpers ─────────────────────────────────────────────────────────
  function L(lang, fr, en) {
    return (lang || "fr") === "fr" ? fr : en;
  }

  // ── docx styling shortcuts ───────────────────────────────────────────────
  // Pulled from window.docx (UMD build). Reused across the file to avoid
  // 30 `window.docx.Paragraph` per section.
  function _d() { return window.docx; }

  const COLOR_PRIMARY = "0F766E"; // teal
  const COLOR_ACCENT  = "002F6C"; // USAID blue
  const COLOR_GREEN   = "15803D";
  const COLOR_AMBER   = "B45309";
  const COLOR_RED     = "B91C1C";
  const COLOR_MUTED   = "6B7280";
  const COLOR_LINE    = "D1D5DB";

  function P(text, opts) {
    const { Paragraph, TextRun } = _d();
    opts = opts || {};
    return new Paragraph({
      children: [new TextRun({ text: text == null ? "" : String(text), ...(opts.run || {}) })],
      spacing: { after: opts.after == null ? 80 : opts.after },
      alignment: opts.align,
      ...(opts.heading ? { heading: opts.heading } : {}),
      ...(opts.border ? { border: opts.border } : {}),
    });
  }

  function H(level, text, opts) {
    const { Paragraph, TextRun, HeadingLevel } = _d();
    const map = { 1: HeadingLevel.HEADING_1, 2: HeadingLevel.HEADING_2, 3: HeadingLevel.HEADING_3, 4: HeadingLevel.HEADING_4 };
    opts = opts || {};
    return new Paragraph({
      heading: map[level] || HeadingLevel.HEADING_1,
      children: [new TextRun({ text: text || "", color: COLOR_ACCENT, bold: true, ...(opts.run || {}) })],
      spacing: { before: 280, after: 140, ...(opts.spacing || {}) },
    });
  }

  function Spacer(after) {
    const { Paragraph } = _d();
    return new Paragraph({ children: [], spacing: { after: after || 160 } });
  }

  function PageBreak() {
    const { Paragraph, PageBreak } = _d();
    return new Paragraph({ children: [new PageBreak()] });
  }

  // ── Table primitives ─────────────────────────────────────────────────────
  // USAID PMP heavy uses tables. Wrap docx-js TableCell / TableRow to keep
  // call-sites concise. All borders default to a light grey.
  function _cellBorder(color) {
    const { BorderStyle } = _d();
    return {
      top:    { style: BorderStyle.SINGLE, size: 4, color: color || COLOR_LINE },
      bottom: { style: BorderStyle.SINGLE, size: 4, color: color || COLOR_LINE },
      left:   { style: BorderStyle.SINGLE, size: 4, color: color || COLOR_LINE },
      right:  { style: BorderStyle.SINGLE, size: 4, color: color || COLOR_LINE },
    };
  }

  function Cell(content, opts) {
    const { TableCell, Paragraph, TextRun, ShadingType, WidthType } = _d();
    opts = opts || {};
    const children = Array.isArray(content)
      ? content
      : [new Paragraph({
          children: [new TextRun({
            text: content == null ? "" : String(content),
            bold: !!opts.bold,
            color: opts.color || undefined,
            size: opts.size || 20,
          })],
          alignment: opts.align,
        })];
    return new TableCell({
      children,
      width: opts.width ? { size: opts.width, type: WidthType.DXA } : undefined,
      shading: opts.fill ? { type: ShadingType.CLEAR, color: "auto", fill: opts.fill } : undefined,
      margins: { top: 60, bottom: 60, left: 100, right: 100 },
      verticalAlign: opts.vAlign || undefined,
      columnSpan: opts.colSpan,
      rowSpan: opts.rowSpan,
      borders: _cellBorder(opts.borderColor),
    });
  }

  function Row(cells) {
    const { TableRow } = _d();
    return new TableRow({ children: cells });
  }

  function Tbl(rows, opts) {
    const { Table, WidthType } = _d();
    opts = opts || {};
    return new Table({
      width: { size: opts.width || 9360, type: WidthType.DXA },
      columnWidths: opts.columnWidths,
      rows,
    });
  }

  // ── Stats helpers ────────────────────────────────────────────────────────
  // Compute Year-1..Y-N actuals and targets given an indicator. Falls back
  // gracefully when fields are missing. Returns { years: ["Y1","Y2",…],
  // targets:[…], actuals:[…], pct:[…], status:[…] }.
  function _computePerformance(indicator, year, periodsCount) {
    const Y = periodsCount || 5;
    const baseYear = parseInt(year || new Date().getFullYear(), 10) || new Date().getFullYear();
    const baseline = Number(indicator.baseline) || 0;
    const finalTarget = Number(indicator.target) || baseline;
    // Linear interpolation baseline → target across Y periods.
    const targets = [];
    for (let i = 1; i <= Y; i++) targets.push(baseline + ((finalTarget - baseline) * i) / Y);

    // Actuals come from `trend` if present (last value is the most recent).
    const trend = Array.isArray(indicator.trend) ? indicator.trend.slice() : null;
    const actuals = [];
    for (let i = 1; i <= Y; i++) {
      if (trend && i <= trend.length) actuals.push(trend[trend.length - Y + i - 1] || trend[Math.min(i - 1, trend.length - 1)]);
      else if (i === 1) actuals.push(Number(indicator.actual) || 0);
      else actuals.push(null);
    }

    // % achievement (handles `invert` for "lower is better" indicators).
    const inv = !!indicator.invert;
    const pct = targets.map((t, i) => {
      const a = actuals[i];
      if (a == null || t === baseline) return null;
      if (inv) {
        // Lower is better: % = (baseline - actual) / (baseline - target) * 100
        const num = baseline - a;
        const den = baseline - t;
        return den === 0 ? null : Math.round((num / den) * 100);
      }
      const num = a - baseline;
      const den = t - baseline;
      return den === 0 ? null : Math.round((num / den) * 100);
    });

    const status = pct.map((p) => {
      if (p == null) return "n/a";
      if (p >= 90) return "ok";
      if (p >= 70) return "warn";
      return "risk";
    });

    const years = [];
    for (let i = 1; i <= Y; i++) years.push("FY" + (baseYear + i - 1));

    return { years, targets, actuals, pct, status, baseline };
  }

  function _fmt(n, unit) {
    if (n == null || isNaN(n)) return "—";
    let s;
    if (Math.abs(n) < 1 && n !== 0) s = (n * 100).toFixed(1) + " %";
    else if (Math.abs(n) >= 1000) s = Math.round(n).toLocaleString(window.melrLocale(window.__melrLang)).replace(/ /g, " ");
    else s = (Math.round(n * 100) / 100).toString();
    if (unit && unit !== "%" && unit !== "/10") s = s + " " + unit;
    return s;
  }

  function _statusFill(status) {
    if (status === "ok")   return "DCFCE7"; // green-100
    if (status === "warn") return "FEF3C7"; // amber-100
    if (status === "risk") return "FEE2E2"; // red-100
    return undefined;
  }
  function _statusColor(status) {
    if (status === "ok")   return COLOR_GREEN;
    if (status === "warn") return COLOR_AMBER;
    if (status === "risk") return COLOR_RED;
    return COLOR_MUTED;
  }

  // ── Section builders ─────────────────────────────────────────────────────

  // 1. Cover Page
  function buildCover({ orgName, scopeLabel, year, lang }) {
    const { TextRun, Paragraph, AlignmentType } = _d();
    return [
      new Paragraph({ spacing: { before: 1200 }, children: [] }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        children: [new TextRun({ text: "USAID", bold: true, size: 56, color: COLOR_ACCENT })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        spacing: { after: 600 },
        children: [new TextRun({ text: L(lang, "Performance Monitoring Plan", "Performance Monitoring Plan"), bold: true, size: 36, color: COLOR_ACCENT })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        spacing: { after: 200 },
        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 || L(lang, "Organisation bénéficiaire", "Implementing organization", "Organización beneficiaria"), size: 24, color: COLOR_MUTED })],
      }),
      new Paragraph({
        alignment: AlignmentType.CENTER,
        spacing: { before: 800, after: 200 },
        children: [new TextRun({ text: L(lang, "Période de référence", "Reference period", "Período de referencia") + " : FY" + 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, color: COLOR_MUTED, italics: true })],
      }),
      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: COLOR_MUTED })],
      }),
    ];
  }

  // 3. Activity Overview
  function buildActivityOverview({ projects, scope, scopeLabel, lang }) {
    const out = [];
    out.push(H(1, L(lang, "1. Aperçu de l'activité", "1. Activity Overview", "1. Panorama de la actividad")));
    out.push(P(L(lang,
      "Cette section présente l'activité (ou le portefeuille) couverte par le présent Performance Monitoring Plan, y compris les projets contributifs, leur zone d'intervention et leur calendrier.",
      "This section presents the activity (or portfolio) covered by this Performance Monitoring Plan, including contributing projects, their geographic scope and timeline.", "Esta sección presenta la actividad (o la cartera) cubierta por el presente Performance Monitoring Plan, incluidos los proyectos contributivos, su zona de intervención y su calendario.")));

    out.push(H(2, L(lang, "Périmètre", "Scope", "Alcance")));
    out.push(P((scopeLabel || L(lang, "Portefeuille complet", "Full portfolio", "Cartera completa")), { run: { bold: true } }));

    out.push(H(2, L(lang, "Projets contributifs", "Contributing projects", "Proyectos contribuyentes")));

    const headerFill = COLOR_ACCENT;
    const headerOpts = { bold: true, color: "FFFFFF", fill: headerFill };
    const projHeaderRow = Row([
      Cell(L(lang, "Code", "Code"), { ...headerOpts, width: 1200 }),
      Cell(L(lang, "Nom", "Name", "Nombre"), { ...headerOpts, width: 3600 }),
      Cell(L(lang, "Pays", "Country", "País"), { ...headerOpts, width: 1600 }),
      Cell(L(lang, "Phase", "Phase"), { ...headerOpts, width: 1400 }),
      Cell(L(lang, "Secteur", "Sector", "Sector"), { ...headerOpts, width: 1560 }),
    ]);
    const projRows = (projects || []).map((p) =>
      Row([
        Cell(p.id || p.code || "—",                      { width: 1200 }),
        Cell(p.nameFr || p.name_fr || p.name || "—",     { width: 3600 }),
        Cell((p.countries || [p.country]).filter(Boolean).join(", ") || "—", { width: 1600 }),
        Cell(p.phase || p.status || "—",                 { width: 1400 }),
        Cell(p.sector || p.sectorCode || "—",            { width: 1560 }),
      ])
    );
    if (projRows.length === 0) {
      projRows.push(Row([Cell(L(lang, "Aucun projet dans le périmètre", "No project in scope", "Ningún proyecto en el perímetro"), { colSpan: 5, color: COLOR_MUTED, align: _d().AlignmentType.CENTER })]));
    }
    out.push(Tbl([projHeaderRow, ...projRows], { columnWidths: [1200, 3600, 1600, 1400, 1560] }));
    out.push(Spacer());

    return out;
  }

  // 4. Results Framework — hierarchical from indicator levels
  function buildResultsFramework({ indicators, lang }) {
    const out = [];
    out.push(PageBreak());
    out.push(H(1, L(lang, "2. Cadre de résultats", "2. Results Framework", "2. Marco de resultados")));
    out.push(P(L(lang,
      "Le cadre de résultats articule la chaîne logique du programme : Impact (long terme) → Résultats intermédiaires (Outcomes) → Produits (Outputs). Chaque indicateur est rattaché à un niveau et contribue à la mesure de la performance globale.",
      "The results framework articulates the program's logical chain: Impact (long term) → Intermediate Results (Outcomes) → Outputs. Each indicator is mapped to a level and contributes to overall performance measurement.", "El marco de resultados articula la cadena lógica del programa: Impacto (largo plazo) → Resultados intermedios (Outcomes) → Productos (Outputs). Cada indicador se vincula a un nivel y contribuye a la medición del desempeño global.")));

    const byLevel = { Impact: [], Outcome: [], Output: [] };
    (indicators || []).forEach((i) => {
      const lvl = (i.level || "Output").trim();
      if (byLevel[lvl]) byLevel[lvl].push(i);
      else byLevel.Output.push(i);
    });

    const levels = [
      { key: "Impact",  fr: "Impact (long terme)",                  en: "Impact (long term)", es: "Impacto (largo plazo)" },
      { key: "Outcome", fr: "Résultats intermédiaires (Outcomes)", en: "Intermediate Results (Outcomes)", es: "Resultados intermedios (Outcomes)" },
      { key: "Output",  fr: "Produits (Outputs)",                  en: "Outputs", es: "Productos (Outputs)" },
    ];
    levels.forEach((lvl) => {
      out.push(H(2, L(lang, lvl.fr, lvl.en)));
      const arr = byLevel[lvl.key];
      if (!arr || arr.length === 0) {
        out.push(P(L(lang, "Aucun indicateur à ce niveau.", "No indicators at this level.", "Ningún indicador en este nivel."), { run: { italics: true, color: COLOR_MUTED } }));
        return;
      }
      arr.forEach((i) => {
        const code = i.id || i.code || "—";
        const name = lang === "fr" ? (i.name_fr || i.name) : (i.name_en || i.name);
        out.push(P("• " + code + " — " + name, { run: { size: 22 } }));
      });
      out.push(Spacer(120));
    });

    return out;
  }

  // 5. IPTT — Indicator Performance Tracking Table
  function buildIPTT({ indicators, year, lang }) {
    const Y = 3; // Show last 3 FYs by default; can be extended.
    const out = [];
    out.push(PageBreak());
    out.push(H(1, L(lang, "3. Tableau de suivi de la performance des indicateurs (IPTT)", "3. Indicator Performance Tracking Table (IPTT)", "3. Tabla de seguimiento del desempeño de los indicadores (IPTT)")));
    out.push(P(L(lang,
      "L'IPTT consolide en un seul tableau la cible, le réalisé et le taux d'atteinte pour chaque indicateur sur les trois derniers exercices fiscaux. Le code couleur USAID : vert ≥ 90 %, ambre 70-89 %, rouge < 70 %.",
      "The IPTT consolidates target, actual and achievement rate for each indicator across the last three fiscal years in one table. USAID color code: green ≥ 90 %, amber 70-89 %, red < 70 %.", "La IPTT consolida en una sola tabla la meta, el valor real y la tasa de cumplimiento de cada indicador a lo largo de los tres últimos ejercicios fiscales. El código de color USAID: verde ≥ 90 %, ámbar 70-89 %, rojo < 70 %.")));

    // Build header: Indicator | Unit | Baseline | Y1 T | Y1 A | Y1 % | Y2 ...
    const headerCells = [
      Cell(L(lang, "Code", "Code"),       { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", width: 800 }),
      Cell(L(lang, "Indicateur", "Indicator", "Indicador"), { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", width: 2600 }),
      Cell(L(lang, "Unité", "Unit", "Unidad"),      { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", width: 600 }),
      Cell(L(lang, "Base", "Baseline", "Línea de base"),   { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", width: 800 }),
    ];
    const yLabels = [];
    const baseYear = parseInt(year, 10) || new Date().getFullYear();
    for (let i = 1; i <= Y; i++) yLabels.push("FY" + (baseYear - Y + i));
    yLabels.forEach((yl) => {
      headerCells.push(Cell(yl + " " + L(lang, "Cible", "Target", "Meta"),  { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", width: 800, align: _d().AlignmentType.CENTER }));
      headerCells.push(Cell(yl + " " + L(lang, "Réal.", "Actual", "Real."),  { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", width: 800, align: _d().AlignmentType.CENTER }));
      headerCells.push(Cell("%", { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", width: 520, align: _d().AlignmentType.CENTER }));
    });

    const rows = [Row(headerCells)];

    (indicators || []).forEach((ind) => {
      const perf = _computePerformance(ind, baseYear, Y);
      const cells = [
        Cell(ind.id || ind.code || "—", { width: 800, bold: true, size: 18 }),
        Cell(lang === "fr" ? (ind.name_fr || ind.name) : (ind.name_en || ind.name), { width: 2600, size: 18 }),
        Cell(ind.unit || "—", { width: 600, size: 18, align: _d().AlignmentType.CENTER }),
        Cell(_fmt(perf.baseline, ind.unit), { width: 800, size: 18, align: _d().AlignmentType.CENTER }),
      ];
      for (let i = 0; i < Y; i++) {
        cells.push(Cell(_fmt(perf.targets[i], ind.unit), { width: 800, size: 18, align: _d().AlignmentType.CENTER }));
        cells.push(Cell(_fmt(perf.actuals[i], ind.unit), { width: 800, size: 18, align: _d().AlignmentType.CENTER }));
        const pctTxt = perf.pct[i] == null ? "—" : (perf.pct[i] + " %");
        cells.push(Cell(pctTxt, {
          width: 520, size: 18, align: _d().AlignmentType.CENTER, bold: true,
          fill: _statusFill(perf.status[i]),
          color: _statusColor(perf.status[i]),
        }));
      }
      rows.push(Row(cells));
    });

    if ((indicators || []).length === 0) {
      const empty = Row([Cell(L(lang, "Aucun indicateur dans le périmètre", "No indicators in scope", "Ningún indicador en el perímetro"), { colSpan: 4 + 3 * Y, color: COLOR_MUTED, align: _d().AlignmentType.CENTER })]);
      rows.push(empty);
    }

    const widths = [800, 2600, 600, 800];
    for (let i = 0; i < Y; i++) widths.push(800, 800, 520);
    out.push(Tbl(rows, { columnWidths: widths, width: widths.reduce((a, b) => a + b, 0) }));
    out.push(Spacer());

    return out;
  }

  // 6. PIRS — one per indicator, USAID ADS standard format
  function buildPIRS({ indicators, year, lang }) {
    const out = [];
    out.push(PageBreak());
    out.push(H(1, L(lang, "4. Fiches de référence des indicateurs (PIRS)", "4. Performance Indicator Reference Sheets (PIRS)", "4. Fichas de referencia de los indicadores (PIRS)")));
    out.push(P(L(lang,
      "Chaque indicateur dispose d'une fiche de référence (PIRS) conforme à l'ADS 201 USAID. Elle documente la définition précise, la méthode de collecte, la fréquence, le responsable, ainsi que les cibles et réalisés annuels.",
      "Each indicator has a reference sheet (PIRS) compliant with USAID ADS 201. It documents the precise definition, collection method, frequency, responsible party, and annual targets and actuals.", "Cada indicador dispone de una ficha de referencia (PIRS) conforme al ADS 201 de USAID. Documenta la definición precisa, el método de recopilación, la frecuencia, el responsable, así como las metas y los valores reales anuales.")));

    (indicators || []).forEach((ind, idx) => {
      if (idx > 0) out.push(PageBreak());
      out.push(H(2, "PIRS — " + (ind.id || ind.code || "—")));
      out.push(buildPIRSTable(ind, year, lang));
      out.push(Spacer(200));
    });

    if ((indicators || []).length === 0) {
      out.push(P(L(lang, "Aucun indicateur à documenter.", "No indicators to document.", "Ningún indicador que documentar."), { run: { italics: true, color: COLOR_MUTED } }));
    }

    return out;
  }

  function buildPIRSTable(ind, year, lang) {
    const perf = _computePerformance(ind, year, 3);
    const FILL = "F3F4F6"; // light grey for labels
    const fmt = (s) => (s == null || s === "" ? "—" : String(s));

    const name = lang === "fr" ? (ind.name_fr || ind.name) : (ind.name_en || ind.name);

    // Two-column layout: label | value (rows). Values can be multi-line.
    const rowsData = [
      [L(lang, "Titre de l'indicateur", "Indicator title", "Título del indicador"),                        name],
      [L(lang, "Code", "Code"),                                                    fmt(ind.id || ind.code)],
      [L(lang, "Type", "Type"),                                                    fmt(ind.level) + " (USAID " + (ind.level === "Impact" ? "Custom/SO" : ind.level === "Outcome" ? "IR" : "Output") + ")"],
      [L(lang, "Projet / Activité", "Project / Activity", "Proyecto / Actividad"),                         fmt(ind.project || ind.project_id)],
      [L(lang, "Secteur DAC", "DAC sector", "Sector CAD"),                                       fmt(ind.sector || ind.sectorCode)],
      [L(lang, "Définition précise", "Precise definition", "Definición precisa"),                        fmt(lang === "fr" ? (ind.definition_fr || ind.definition) : (ind.definition_en || ind.definition || name))],
      [L(lang, "Unité de mesure", "Unit of measure", "Unidad de medida"),                              fmt(ind.unit)],
      [L(lang, "Désagrégation", "Disaggregation", "Desagregación"),                                 fmt(ind.disaggregation_required ? (window.L("Sexe, âge, géographie (voir grille)", "Sex, age, geography (see grid)", "Sexo, edad, geografía (ver cuadrícula)")) : (window.L("Aucune", "None", "Ninguna")))],
      [L(lang, "Justification & utilité", "Justification & management utility", "Justificación y utilidad"),   fmt(window.L("Mesure la progression vers le résultat ciblé. Sert au pilotage opérationnel et au reporting bailleur.", "Measures progress towards the targeted result. Used for operational steering and donor reporting.", "Mide el avance hacia el resultado previsto. Sirve para el pilotaje operativo y el reporte al donante."))],
      [L(lang, "Méthode de collecte", "Data collection method", "Método de recopilación"),                   fmt(window.L("Saisie terrain via MELR (PWA mobile + back-office)", "Field data entry via MELR (mobile PWA + back-office)", "Captura de datos en campo vía MELR (PWA móvil + back-office)"))],
      [L(lang, "Source des données", "Data source", "Fuente de los datos"),                               fmt(window.L("Sites de collecte rattachés au projet (cf. Baseline)", "Collection sites linked to the project (see Baseline)", "Sitios de recopilación vinculados al proyecto (cf. Línea de base)"))],
      [L(lang, "Moyens de vérification (MoV)", "Means of verification", "Medios de verificación (MoV)"),           fmt(window.L("Registres terrain, formulaires signés, photos GPS", "Field registers, signed forms, GPS photos", "Registros de campo, formularios firmados, fotos GPS"))],
      [L(lang, "Fréquence de collecte", "Frequency / Timing", "Frecuencia de recopilación"),                     fmt(window.L("Mensuelle (agrégation trimestrielle)", "Monthly (quarterly aggregation)", "Mensual (agregación trimestral)"))],
      [L(lang, "Responsable", "Office responsible", "Responsable"),                               fmt(window.L("Chargé(e) M&E du projet", "Project M&E Officer", "Responsable de S&E del proyecto"))],
      [L(lang, "Coût / Niveau d'effort", "Cost / Level of effort", "Costo / Nivel de esfuerzo"),                fmt(window.L("Inclus dans le budget projet (poste M&E)", "Included in project budget (M&E line)", "Incluido en el presupuesto del proyecto (partida de S&E)"))],
      [L(lang, "Date du DQA initial", "Initial DQA date", "Fecha del DQA inicial"),                         fmt(window.L("À renseigner", "To be completed", "Por completar"))],
      [L(lang, "Limites connues", "Known data limitations", "Limitaciones conocidas"),                       fmt(window.L("À documenter au cours du DQA annuel", "To be documented during the annual DQA", "Por documentar durante el DQA anual"))],
      [L(lang, "Plan d'analyse", "Plan for data analysis", "Plan de análisis"),                        fmt(window.L("Tableau de bord MELR + revues trimestrielles avec l'équipe projet", "MELR dashboard + quarterly project reviews", "Panel de control MELR + revisiones trimestrales con el equipo de proyecto"))],
      [L(lang, "Plan de revue & reporting", "Review & reporting", "Plan de revisión y reporte"),                 fmt(window.L("Reporting trimestriel bailleur, revue annuelle ADS 201", "Quarterly donor reporting, annual ADS 201 review", "Reporte trimestral al donante, revisión anual ADS 201"))],
    ];

    const rows = rowsData.map(([k, v]) => Row([
      Cell(k, { width: 3200, bold: true, fill: FILL, color: COLOR_ACCENT, size: 18 }),
      Cell(v, { width: 6160, size: 20 }),
    ]));

    // Append the indicator performance values table (mini-IPTT for this PIRS)
    const valuesHeader = Row([
      Cell(L(lang, "Année fiscale", "Fiscal year", "Año fiscal"), { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", size: 18 }),
      Cell(L(lang, "Cible", "Target", "Meta"),              { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", size: 18, align: _d().AlignmentType.CENTER }),
      Cell(L(lang, "Réalisé", "Actual", "Realizado"),            { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", size: 18, align: _d().AlignmentType.CENTER }),
      Cell(L(lang, "% atteinte", "% achievement", "% de consecución"),  { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", size: 18, align: _d().AlignmentType.CENTER }),
      Cell(L(lang, "Notes", "Notes"),               { bold: true, fill: COLOR_ACCENT, color: "FFFFFF", size: 18 }),
    ]);
    const baseRow = Row([
      Cell(L(lang, "Référence (baseline)", "Baseline", "Línea de base (baseline)"), { size: 18, bold: true, fill: FILL }),
      Cell(_fmt(perf.baseline, ind.unit), { size: 18, align: _d().AlignmentType.CENTER, colSpan: 3 }),
      Cell(L(lang, "Point de départ", "Starting point", "Punto de partida"), { size: 18 }),
    ]);
    const yearRows = perf.years.map((yl, i) => Row([
      Cell(yl, { size: 18, bold: true, fill: FILL }),
      Cell(_fmt(perf.targets[i], ind.unit), { size: 18, align: _d().AlignmentType.CENTER }),
      Cell(_fmt(perf.actuals[i], ind.unit), { size: 18, align: _d().AlignmentType.CENTER }),
      Cell(perf.pct[i] == null ? "—" : (perf.pct[i] + " %"), {
        size: 18, align: _d().AlignmentType.CENTER, bold: true,
        fill: _statusFill(perf.status[i]), color: _statusColor(perf.status[i]),
      }),
      Cell("", { size: 18 }),
    ]));

    const allRows = [...rows, Row([Cell(L(lang, "Valeurs de performance", "Performance values", "Valores de desempeño"), { colSpan: 2, bold: true, fill: COLOR_ACCENT, color: "FFFFFF", align: _d().AlignmentType.CENTER })])];

    // Use a separate sub-table for the values block so column widths work
    const valuesTable = Tbl([valuesHeader, baseRow, ...yearRows], {
      columnWidths: [1800, 1800, 1800, 1800, 2160],
    });

    return [Tbl(allRows, { columnWidths: [3200, 6160] }), Spacer(120), valuesTable];
  }

  // 7. DQA narrative
  function buildDQA({ lang }) {
    const out = [];
    out.push(PageBreak());
    out.push(H(1, L(lang, "5. Évaluation de la qualité des données (DQA)", "5. Data Quality Assessment (DQA)", "5. Evaluación de la calidad de los datos (DQA)")));
    out.push(P(L(lang,
      "Conformément à l'ADS 201, l'organisation bénéficiaire conduit un DQA annuel sur chaque indicateur. Le DQA évalue cinq dimensions standard USAID : Validity (validité), Reliability (fiabilité), Integrity (intégrité), Precision (précision) et Timeliness (rapidité). Cette section présente l'approche méthodologique commune ; les résultats détaillés par indicateur seront consignés dans les fiches PIRS lors du DQA suivant.",
      "Per ADS 201, the implementing organization conducts an annual DQA on each indicator. The DQA evaluates five USAID standard dimensions: Validity, Reliability, Integrity, Precision, and Timeliness. This section presents the common methodological approach; detailed results per indicator will be recorded in the PIRS sheets at the next DQA cycle.", "Conforme al ADS 201, la organización beneficiaria realiza un DQA anual sobre cada indicador. El DQA evalúa cinco dimensiones estándar de USAID: Validity (validez), Reliability (fiabilidad), Integrity (integridad), Precision (precisión) y Timeliness (oportunidad). Esta sección presenta el enfoque metodológico común; los resultados detallados por indicador se consignarán en las fichas PIRS durante el siguiente DQA.")));

    const dims = [
      {
        title_fr: "Validity (Validité)", title_en: "Validity", title_es: "Validity (Validez)",
        def_fr: "L'indicateur mesure-t-il vraiment ce qu'il prétend mesurer ?",
        def_en: "Does the indicator truly measure what it claims to measure?", def_es: "¿Mide el indicador realmente lo que afirma medir?",
        approach_fr: "Revue des définitions PIRS par le M&E Lead et le bailleur. Comparaison avec les standards sectoriels (USAID Standard Foreign Assistance, OMS, OCDE-DAC).",
        approach_en: "PIRS definitions reviewed by the M&E Lead and the donor. Cross-checked against sectoral standards (USAID Standard Foreign Assistance, WHO, OECD-DAC).", approach_es: "Revisión de las definiciones PIRS por parte del M&E Lead y del donante. Comparación con los estándares sectoriales (USAID Standard Foreign Assistance, OMS, OCDE-CAD).",
      },
      {
        title_fr: "Reliability (Fiabilité)", title_en: "Reliability", title_es: "Reliability (Fiabilidad)",
        def_fr: "Les méthodes de collecte produisent-elles des données cohérentes dans le temps et entre les sites ?",
        def_en: "Do collection methods produce data that is consistent over time and across sites?", def_es: "¿Producen los métodos de recopilación datos coherentes en el tiempo y entre los distintos sitios?",
        approach_fr: "Standardisation des formulaires MELR, formation harmonisée des collecteurs, audits trimestriels sur 10 % des soumissions tirées au sort.",
        approach_en: "Standardized MELR forms, harmonized data-collector training, quarterly spot-check audits on 10 % of submissions sampled at random.", approach_es: "Estandarización de los formularios MELR, formación armonizada de los recopiladores, auditorías trimestrales sobre el 10 % de las entregas seleccionadas al azar.",
      },
      {
        title_fr: "Integrity (Intégrité)", title_en: "Integrity", title_es: "Integrity (Integridad)",
        def_fr: "Les données sont-elles protégées contre la manipulation accidentelle ou délibérée ?",
        def_en: "Are data protected against accidental or deliberate manipulation?", def_es: "¿Están los datos protegidos contra la manipulación accidental o deliberada?",
        approach_fr: "MELR applique le Row-Level Security (RLS) par organisation et trace toutes les modifications dans des logs d'audit. Les soumissions terrain incluent GPS et signature.",
        approach_en: "MELR applies Row-Level Security (RLS) per organization and records all modifications in audit logs. Field submissions include GPS coordinates and signature.", approach_es: "MELR aplica el Row-Level Security (RLS) por organización y registra todas las modificaciones en logs de auditoría. Las entregas de terreno incluyen GPS y firma.",
      },
      {
        title_fr: "Precision (Précision)", title_en: "Precision", title_es: "Precision (Precisión)",
        def_fr: "Le niveau de désagrégation est-il suffisant pour la prise de décision ?",
        def_en: "Is the level of disaggregation sufficient for decision-making?", def_es: "¿Es el nivel de desagregación suficiente para la toma de decisiones?",
        approach_fr: "Désagrégation par sexe, âge, géographie (région/département/commune) configurée au niveau définition d'indicateur. Précision spatiale GPS ≤ 10 m.",
        approach_en: "Disaggregation by sex, age, geography (region/department/commune) configured at the indicator definition level. GPS spatial precision ≤ 10 m.", approach_es: "Desagregación por sexo, edad, geografía (región/departamento/comuna) configurada a nivel de definición del indicador. Precisión espacial GPS ≤ 10 m.",
      },
      {
        title_fr: "Timeliness (Rapidité)", title_en: "Timeliness", title_es: "Timeliness (Oportunidad)",
        def_fr: "Les données sont-elles disponibles à temps pour informer les décisions ?",
        def_en: "Are data available in time to inform decisions?", def_es: "¿Están los datos disponibles a tiempo para fundamentar las decisiones?",
        approach_fr: "Synchronisation automatique via la PWA offline → backend. Tableau de bord rafraîchi en temps réel ; reporting mensuel disponible au plus tard J+5 du mois suivant.",
        approach_en: "Automatic sync via the offline PWA → backend. Real-time dashboard refresh; monthly reporting available by D+5 of the following month at the latest.", approach_es: "Sincronización automática mediante la PWA offline → backend. Cuadro de mando actualizado en tiempo real; reporte mensual disponible a más tardar J+5 del mes siguiente.",
      },
    ];

    dims.forEach((d) => {
      out.push(H(2, L(lang, d.title_fr, d.title_en)));
      out.push(P(L(lang, "Définition : ", "Definition: ", "Definición: ") + L(lang, d.def_fr, d.def_en), { run: { italics: true, color: COLOR_MUTED } }));
      out.push(P(L(lang, "Approche MELR : ", "MELR approach: ", "Enfoque MELR: ") + L(lang, d.approach_fr, d.approach_en)));
    });

    out.push(Spacer(200));
    out.push(P(L(lang,
      "Signature du M&E Lead : _________________________________   Date : _____________",
      "M&E Lead signature: _________________________________   Date: _____________", "Firma del M&E Lead: _________________________________   Fecha: _____________"
    ), { run: { size: 22 } }));
    out.push(P(L(lang,
      "Signature COR/AOR USAID : _____________________________   Date : _____________",
      "USAID COR/AOR signature: _____________________________   Date: _____________", "Firma COR/AOR USAID: _____________________________   Fecha: _____________"
    ), { run: { size: 22 } }));

    return out;
  }

  // ── Master builder ───────────────────────────────────────────────────────
  function buildPmpDoc({ projects, indicators, scope, year, scopeLabel, orgName, lang }) {
    if (!_docxOk()) throw new Error("docx library not loaded");
    const { Document, Footer, PageNumber, TextRun, Paragraph, AlignmentType } = _d();

    const children = [];
    children.push(...buildCover({ orgName, scopeLabel, year, lang }));
    children.push(PageBreak());
    children.push(...buildActivityOverview({ projects, scope, scopeLabel, lang }));
    children.push(...buildResultsFramework({ indicators, lang }));
    children.push(...buildIPTT({ indicators, year, lang }));
    children.push(...buildPIRS({ indicators, year, lang }));
    children.push(...buildDQA({ lang }));

    return new Document({
      creator: "MELR",
      title: "USAID PMP — " + (scopeLabel || "Portfolio") + " — FY" + year,
      description: "Performance Monitoring Plan auto-generated by MELR",
      styles: {
        default: {
          document: { run: { font: "Calibri", size: 22 } },
        },
      },
      sections: [{
        properties: {
          page: {
            margin: { top: 1200, right: 1200, bottom: 1200, left: 1200 },
            size: { width: 12240, height: 15840 }, // US Letter — USAID standard
          },
        },
        footers: {
          default: new Footer({
            children: [new Paragraph({
              alignment: AlignmentType.CENTER,
              children: [
                new TextRun({ text: "USAID PMP · " + (scopeLabel || "Portfolio") + " · FY" + year + " · ", color: COLOR_MUTED, size: 18 }),
                new TextRun({ children: [PageNumber.CURRENT], color: COLOR_MUTED, size: 18 }),
                new TextRun({ text: " / ", color: COLOR_MUTED, size: 18 }),
                new TextRun({ children: [PageNumber.TOTAL_PAGES], color: COLOR_MUTED, size: 18 }),
              ],
            })],
          }),
        },
        children,
      }],
    });
  }

  // ── Public entry point ───────────────────────────────────────────────────
  async function exportUsaidPmp(args) {
    args = args || {};
    const { projects, indicators, scope, year, scopeLabel, orgName, lang, filename } = args;
    if (!_docxOk()) {
      alert(L(lang, "Bibliothèque Word indisponible.", "Word library unavailable.", "Biblioteca Word no disponible."));
      return;
    }
    try {
      const doc = buildPmpDoc({
        projects: projects || [],
        indicators: indicators || [],
        scope: scope || { mode: "all" },
        year: year || new Date().getFullYear(),
        scopeLabel: scopeLabel || L(lang, "Portefeuille", "Portfolio", "Cartera"),
        orgName: orgName || "",
        lang: lang || "fr",
      });
      const blob = await _d().Packer.toBlob(doc);
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      const safeYear = String(year || new Date().getFullYear());
      a.href = url;
      a.download = (filename || ("USAID-PMP-" + safeYear)) + ".docx";
      document.body.appendChild(a);
      a.click();
      a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 4000);
    } catch (e) {
      console.error("[USAID PMP export]", e);
      alert((window.L("Erreur lors de la génération du PMP : ", "Error generating PMP: ", "Error al generar el PMP: ")) + e.message);
    }
  }

  // Expose
  window.exportUsaidPmp = exportUsaidPmp;
})();
