/* global window */
// ============================================================================
// MELR · Donor reporting · Shared helpers
// ----------------------------------------------------------------------------
// Common docx-js primitives, stats, and constants used by every donor
// template (USAID PMP, GAVI Performance Framework, Global Fund Grant
// Performance Framework, etc.). Loaded BEFORE the donor-specific modules
// so they can call window.melrDonor.* without inlining helpers.
//
// Design notes :
//   - Each helper is pure (no React state). Templates can call them from
//     anywhere — typically inside an exportXxx() entry function.
//   - Colors live here so all donor reports share a coherent visual
//     vocabulary for the status RAG code (green/amber/red) and muted text.
//   - Donor brand colors are set per-template by the caller — we don't
//     hardcode them here.
// ============================================================================

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

  function docxLib() { return window.docx; }
  function isReady() {
    const d = docxLib();
    return !!(d && d.Document && d.Packer && d.Paragraph && d.TextRun && d.Table);
  }

  // ── i18n ────────────────────────────────────────────────────────────────
  function L(lang, fr, en, es) { const l = lang || "fr"; return l === "es" ? (es != null ? es : en) : l === "fr" ? fr : en; }

  // ── Shared color palette (status code + neutral) ────────────────────────
  // Donor brand colors are passed in by each template — only RAG and
  // neutral text colors live here.
  const COLORS = {
    GREEN: "15803D",
    AMBER: "B45309",
    RED:   "B91C1C",
    MUTED: "6B7280",
    LINE:  "D1D5DB",
  };

  // ── Status helpers ─────────────────────────────────────────────────────
  // Map a numeric % achievement onto a USAID-style RAG band. Used by every
  // donor template's IPTT to color the percentage cells.
  function statusFromPct(pct) {
    if (pct == null || isNaN(pct)) return "n/a";
    if (pct >= 90) return "ok";
    if (pct >= 70) return "warn";
    return "risk";
  }
  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 COLORS.GREEN;
    if (status === "warn") return COLORS.AMBER;
    if (status === "risk") return COLORS.RED;
    return COLORS.MUTED;
  }

  // ── Number formatting ──────────────────────────────────────────────────
  // Locale-aware formatting that auto-detects percentages (values in [0,1])
  // and adds thousand separators on large numbers. Accepts an optional unit.
  function fmtNum(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;
  }

  // ── Indicator performance computation ──────────────────────────────────
  // Given an indicator + reporting year + how many FY columns to expose,
  // return the structured perf object used by every donor template.
  //   { years, targets[], actuals[], pct[], status[], baseline }
  // Handles `invert: true` for "lower is better" indicators (e.g. stunting,
  // mortality, turnaround time).
  function computePerformance(indicator, year, periodsCount) {
    const Y = periodsCount || 3;
    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` array if present (last value = 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)
    const inv = !!indicator.invert;
    const pct = targets.map((t, i) => {
      const a = actuals[i];
      if (a == null || t === baseline) return null;
      if (inv) {
        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(statusFromPct);

    const years = [];
    // FY labels: the reporting year is the LAST one; earlier years to the left
    for (let i = 1; i <= Y; i++) years.push("FY" + (baseYear - Y + i));

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

  // ── Paragraph / heading helpers ────────────────────────────────────────
  function Spacer(after) {
    const { Paragraph } = docxLib();
    return new Paragraph({ children: [], spacing: { after: after || 160 } });
  }
  function PageBreak() {
    const { Paragraph, PageBreak: PB } = docxLib();
    return new Paragraph({ children: [new PB()] });
  }
  function P(text, opts) {
    const { Paragraph, TextRun } = docxLib();
    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 } = docxLib();
    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: (opts.color || "002F6C"), bold: true, ...(opts.run || {}) })],
      spacing: { before: 280, after: 140, ...(opts.spacing || {}) },
    });
  }

  // ── Table primitives ───────────────────────────────────────────────────
  function _cellBorder(color) {
    const { BorderStyle } = docxLib();
    const c = color || COLORS.LINE;
    return {
      top:    { style: BorderStyle.SINGLE, size: 4, color: c },
      bottom: { style: BorderStyle.SINGLE, size: 4, color: c },
      left:   { style: BorderStyle.SINGLE, size: 4, color: c },
      right:  { style: BorderStyle.SINGLE, size: 4, color: c },
    };
  }
  function Cell(content, opts) {
    const { TableCell, Paragraph, TextRun, ShadingType, WidthType } = docxLib();
    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 } = docxLib();
    return new TableRow({ children: cells });
  }
  function Tbl(rows, opts) {
    const { Table, WidthType } = docxLib();
    opts = opts || {};
    return new Table({
      width: { size: opts.width || 9360, type: WidthType.DXA },
      columnWidths: opts.columnWidths,
      rows,
    });
  }

  // ── Common IPTT row builder (used by USAID, GAVI, GF) ──────────────────
  // Builds an indicator row for an IPTT-style table : Code, Name, Unit,
  // Baseline, then Y x (Target / Actual / %). Each donor template wraps
  // this with its own header row + table widths.
  function iptRow(indicator, perf, lang, periodsCount) {
    const Y = periodsCount || 3;
    const cells = [
      Cell(indicator.id || indicator.code || "—", { width: 800, bold: true, size: 18 }),
      Cell(lang === "fr" ? (indicator.name_fr || indicator.name) : (indicator.name_en || indicator.name), { width: 2600, size: 18 }),
      Cell(indicator.unit || "—", { width: 600, size: 18, align: docxLib().AlignmentType.CENTER }),
      Cell(fmtNum(perf.baseline, indicator.unit), { width: 800, size: 18, align: docxLib().AlignmentType.CENTER }),
    ];
    for (let i = 0; i < Y; i++) {
      cells.push(Cell(fmtNum(perf.targets[i], indicator.unit), { width: 800, size: 18, align: docxLib().AlignmentType.CENTER }));
      cells.push(Cell(fmtNum(perf.actuals[i], indicator.unit), { width: 800, size: 18, align: docxLib().AlignmentType.CENTER }));
      const pctTxt = perf.pct[i] == null ? "—" : (perf.pct[i] + " %");
      cells.push(Cell(pctTxt, {
        width: 520, size: 18, align: docxLib().AlignmentType.CENTER, bold: true,
        fill: statusFill(perf.status[i]),
        color: statusColor(perf.status[i]),
      }));
    }
    return Row(cells);
  }

  // ── Common indicators grouping ────────────────────────────────────────
  // Some donor templates need indicators grouped by hierarchy level
  // (Impact / Outcome / Output) — return a {Impact:[…], Outcome:[…],
  // Output:[…]} bag with safe defaults for unknown levels.
  function groupByLevel(indicators) {
    const bag = { Impact: [], Outcome: [], Output: [] };
    (indicators || []).forEach((i) => {
      const lvl = (i.level || "Output").trim();
      if (bag[lvl]) bag[lvl].push(i);
      else bag.Output.push(i);
    });
    return bag;
  }

  // ── Common file-save helper ───────────────────────────────────────────
  // Triggers a browser download for a docx Document. Wraps Packer + Blob +
  // anchor click + URL.revokeObjectURL. Catches & alerts on failure.
  async function saveDocx(doc, filename, lang) {
    try {
      const blob = await docxLib().Packer.toBlob(doc);
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = (filename || "report") + ".docx";
      document.body.appendChild(a);
      a.click();
      a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 4000);
    } catch (e) {
      console.error("[donor-shared] saveDocx:", e);
      alert((window.L("Erreur d'export : ", "Export error: ", "Error de exportación: ")) + e.message);
    }
  }

  // Expose namespace
  window.melrDonor = {
    isReady, L,
    COLORS,
    statusFromPct, statusFill, statusColor,
    fmtNum, computePerformance,
    Spacer, PageBreak, P, H,
    Cell, Row, Tbl,
    iptRow, groupByLevel,
    saveDocx,
  };
})();
