/* global */
// ============================================================================
// REPORTING — Chart rendering to PNG (canvas, no external deps)
// ============================================================================
// Mirrors exante-charts.jsx (same pattern, same canvas factory) but with
// chart types tailored to MERL reporting:
//   • drawIndicatorStatus  — donut: On target / At risk / Behind / Not measured
//   • drawProgressByProject — horizontal bar: avg progress per project
//   • drawBudgetByProject   — grouped bar: budget vs disbursed per project
//   • drawIndicatorLevels   — stacked bar: indicator counts by level (output/outcome/impact)
//
// Public API exposed at window.reportingCharts:
//   {
//     dataURLs(opts)     → { indicatorStatus, progressByProject, budgetByProject, indicatorLevels }
//     arrayBuffers(opts) → same fields as ArrayBuffer (for docx ImageRun)
//     SIZES              → { kind: { w, h } } used by both HTML and docx renderers
//   }
// ============================================================================

(function () {

  const STATUS_COLORS = {
    ok:      "#16a34a",
    amber:   "#f59e0b",
    bad:     "#dc2626",
    neutral: "#9ca3af",
  };
  const LEVEL_COLORS = {
    impact:  "#7c3aed",
    outcome: "#2563eb",
    output:  "#16a34a",
    other:   "#9ca3af",
  };

  // ---------- canvas factory ------------------------------------------------
  function makeCanvas(w, h) {
    const dpr = (typeof window !== "undefined" && window.devicePixelRatio) || 1;
    const c = document.createElement("canvas");
    c.width  = Math.round(w * dpr);
    c.height = Math.round(h * dpr);
    c.style.width  = w + "px";
    c.style.height = h + "px";
    const ctx = c.getContext("2d");
    ctx.scale(dpr, dpr);
    ctx.textBaseline = "top";
    ctx.font = "12px Arial, sans-serif";
    return { canvas: c, ctx, w, h };
  }

  function canvasToBlob(canvas) {
    return new Promise((resolve) => canvas.toBlob((b) => resolve(b), "image/png"));
  }
  async function canvasToArrayBuffer(canvas) {
    const blob = await canvasToBlob(canvas);
    if (!blob) return null;
    return blob.arrayBuffer();
  }

  function fmtCompact(v) {
    if (v == null || !isFinite(v)) return "—";
    const abs = Math.abs(v);
    if (abs >= 1e9) return (v / 1e9).toFixed(1) + "B";
    if (abs >= 1e6) return (v / 1e6).toFixed(1) + "M";
    if (abs >= 1e3) return (v / 1e3).toFixed(0) + "K";
    return Math.round(v).toString();
  }
  function fmtPct(v) {
    if (v == null || !isFinite(v)) return "—";
    return Math.round(v * 100) + "%";
  }

  function niceBounds(min, max, ticks) {
    if (min === max) { min -= 1; max += 1; }
    const span = max - min;
    const step = Math.pow(10, Math.floor(Math.log10(span / (ticks - 1))));
    const niceStep = step * (span / (ticks - 1) / step <= 2 ? 2 : span / (ticks - 1) / step <= 5 ? 5 : 10);
    const niceMin = Math.floor(min / niceStep) * niceStep;
    const niceMax = Math.ceil(max / niceStep) * niceStep;
    return { min: niceMin, max: niceMax, step: niceStep };
  }

  // ---------- 1) Indicator status donut -------------------------------------
  // opts = { onTarget, atRisk, behind, notMeasured, lang }
  function drawIndicatorStatus(opts) {
    const { onTarget = 0, atRisk = 0, behind = 0, notMeasured = 0, lang = "fr" } = opts || {};
    const W = 460, H = 260;
    const { canvas, ctx } = makeCanvas(W, H);
    ctx.fillStyle = "#ffffff";
    ctx.fillRect(0, 0, W, H);
    ctx.fillStyle = "#111827";
    ctx.font = "bold 14px Arial, sans-serif";
    ctx.fillText(lang === "fr" ? "Répartition des indicateurs par statut" : "Indicators by status", 20, 8);

    const cx = 130, cy = 130, R = 90, r = 50;
    const items = [
      { name: lang === "fr" ? "Sur cible"      : "On target",     value: onTarget,    color: STATUS_COLORS.ok      },
      { name: lang === "fr" ? "À surveiller"   : "At risk",       value: atRisk,      color: STATUS_COLORS.amber   },
      { name: lang === "fr" ? "En retard"      : "Behind",        value: behind,      color: STATUS_COLORS.bad     },
      { name: lang === "fr" ? "Non mesurés"    : "Not measured",  value: notMeasured, color: STATUS_COLORS.neutral },
    ];
    const total = items.reduce((s, x) => s + (Number(x.value) || 0), 0);
    if (total <= 0) {
      ctx.fillStyle = "#9ca3af";
      ctx.font = "12px Arial";
      ctx.textAlign = "center";
      ctx.fillText(lang === "fr" ? "(aucune donnée)" : "(no data)", cx, cy);
      return canvas;
    }

    let start = -Math.PI / 2;
    items.forEach((it) => {
      const v = Number(it.value) || 0;
      if (v <= 0) return;
      const angle = (v / total) * Math.PI * 2;
      const end = start + angle;
      ctx.fillStyle = it.color;
      ctx.beginPath();
      ctx.arc(cx, cy, R, start, end);
      ctx.arc(cx, cy, r, end, start, true);
      ctx.closePath();
      ctx.fill();
      start = end;
    });

    // Centre label = total count
    ctx.fillStyle = "#374151";
    ctx.font = "11px Arial";
    ctx.textAlign = "center";
    ctx.fillText(lang === "fr" ? "Total" : "Total", cx, cy - 8);
    ctx.font = "bold 18px Arial";
    ctx.fillText(String(total), cx, cy + 4);

    // Right-side legend
    ctx.font = "11px Arial";
    ctx.textAlign = "left";
    let ly = 50;
    items.forEach((it) => {
      const v = Number(it.value) || 0;
      const pct = total > 0 ? (v / total * 100) : 0;
      ctx.fillStyle = it.color;
      ctx.fillRect(250, ly, 10, 10);
      ctx.fillStyle = "#374151";
      ctx.fillText(it.name + " — " + v + " (" + pct.toFixed(0) + "%)", 268, ly + 1);
      ly += 22;
    });
    return canvas;
  }

  // ---------- 2) Avg progress per project — horizontal bar ------------------
  // opts = { rows: [{ code, name, progress (0..1), status }], lang }
  function drawProgressByProject(opts) {
    const { rows = [], lang = "fr" } = opts || {};
    const W = 700, H = Math.max(200, 60 + rows.length * 26 + 40);
    const { canvas, ctx } = makeCanvas(W, H);
    ctx.fillStyle = "#ffffff";
    ctx.fillRect(0, 0, W, H);
    ctx.fillStyle = "#111827";
    ctx.font = "bold 14px Arial, sans-serif";
    ctx.fillText(lang === "fr" ? "Avancement moyen par projet" : "Average progress per project", 20, 8);

    if (rows.length === 0) {
      ctx.fillStyle = "#9ca3af"; ctx.font = "12px Arial"; ctx.textAlign = "center";
      ctx.fillText(lang === "fr" ? "(aucun projet)" : "(no projects)", W / 2, H / 2);
      return canvas;
    }

    const labelW = 160, padL = 20, padR = 60, padT = 40;
    const barAreaW = W - labelW - padL - padR;
    const rowH = 22;

    // Vertical grid lines at 0, 25, 50, 75, 100%
    ctx.strokeStyle = "#e5e7eb";
    ctx.fillStyle = "#9ca3af";
    ctx.font = "10px Arial";
    ctx.textAlign = "center";
    for (let p = 0; p <= 1; p += 0.25) {
      const x = padL + labelW + p * barAreaW;
      ctx.beginPath();
      ctx.moveTo(x, padT - 6); ctx.lineTo(x, padT + rows.length * (rowH + 4));
      ctx.strokeStyle = p === 0 || p === 1 ? "#d1d5db" : "#e5e7eb";
      ctx.stroke();
      ctx.fillStyle = "#9ca3af";
      ctx.fillText(Math.round(p * 100) + "%", x, padT - 18);
    }

    rows.forEach((r, i) => {
      const y = padT + i * (rowH + 4);
      // Project label
      ctx.fillStyle = "#374151";
      ctx.font = "11px Arial";
      ctx.textAlign = "left";
      const label = (r.code || "—") + " — " + (r.name || "");
      ctx.fillText(label.slice(0, 26), padL, y + 6);
      // Bar
      const p = Math.max(0, Math.min(1, Number(r.progress) || 0));
      const barX = padL + labelW;
      const fillW = p * barAreaW;
      ctx.fillStyle = "#f3f4f6";
      ctx.fillRect(barX, y, barAreaW, rowH);
      ctx.fillStyle = STATUS_COLORS[r.status] || STATUS_COLORS.neutral;
      ctx.fillRect(barX, y, fillW || 1, rowH);
      // Value label
      ctx.fillStyle = "#111827";
      ctx.font = "bold 11px Arial";
      ctx.textAlign = "left";
      ctx.fillText(fmtPct(p), barX + fillW + 6, y + 6);
    });
    return canvas;
  }

  // ---------- 3) Budget vs disbursed per project — grouped bar --------------
  // opts = { rows: [{ code, name, budgetEur, disbursedEur }], lang }
  function drawBudgetByProject(opts) {
    const { rows = [], lang = "fr" } = opts || {};
    const W = 700, H = 340, pad = { l: 60, r: 20, t: 30, b: 70 };
    const { canvas, ctx } = makeCanvas(W, H);
    ctx.fillStyle = "#ffffff";
    ctx.fillRect(0, 0, W, H);
    ctx.fillStyle = "#111827";
    ctx.font = "bold 14px Arial, sans-serif";
    ctx.fillText(lang === "fr" ? "Budget vs décaissé par projet (M€)" : "Budget vs disbursed per project (M€)", pad.l, 8);

    const n = rows.length;
    if (n === 0) {
      ctx.fillStyle = "#9ca3af"; ctx.font = "12px Arial"; ctx.textAlign = "center";
      ctx.fillText(lang === "fr" ? "(aucun projet)" : "(no projects)", W / 2, H / 2);
      return canvas;
    }

    const series = [
      { key: "budgetEur",    color: "#2563eb", label: lang === "fr" ? "Budget"   : "Budget" },
      { key: "disbursedEur", color: "#16a34a", label: lang === "fr" ? "Décaissé" : "Disbursed" },
    ];
    const allValues = rows.flatMap((r) => series.map((s) => r[s.key] || 0));
    const { min, max, step } = niceBounds(0, Math.max(1, ...allValues), 5);

    const innerW = W - pad.l - pad.r;
    const innerH = H - pad.t - pad.b;
    const groupW = innerW / n;
    const barW = Math.min(22, (groupW - 6) / series.length);

    // Y grid
    ctx.strokeStyle = "#e5e7eb"; ctx.fillStyle = "#6b7280"; ctx.font = "10px Arial";
    for (let v = min; v <= max + 1e-9; v += step) {
      const y = pad.t + innerH - ((v - min) / (max - min || 1)) * innerH;
      ctx.beginPath(); ctx.moveTo(pad.l, y); ctx.lineTo(W - pad.r, y);
      ctx.strokeStyle = v === 0 ? "#9ca3af" : "#e5e7eb"; ctx.stroke();
      ctx.fillStyle = "#6b7280"; ctx.textAlign = "right";
      ctx.fillText(fmtCompact(v), pad.l - 6, y - 5);
    }

    rows.forEach((r, i) => {
      const groupX = pad.l + i * groupW + (groupW - barW * series.length) / 2;
      series.forEach((s, j) => {
        const v = r[s.key] || 0;
        const x = groupX + j * barW;
        const y0 = pad.t + innerH - ((0 - min) / (max - min || 1)) * innerH;
        const y1 = pad.t + innerH - ((v - min) / (max - min || 1)) * innerH;
        ctx.fillStyle = s.color;
        const yTop = Math.min(y0, y1), barH = Math.abs(y1 - y0);
        ctx.fillRect(x, yTop, barW, barH || 1);
      });
      // X tick = project code (rotated label, truncated)
      ctx.save();
      ctx.translate(pad.l + i * groupW + groupW / 2, H - pad.b + 6);
      ctx.rotate(-Math.PI / 8);
      ctx.fillStyle = "#6b7280"; ctx.font = "10px Arial";
      ctx.textAlign = "right";
      ctx.fillText((r.code || "").slice(0, 12), 0, 0);
      ctx.restore();
    });

    // Legend
    ctx.font = "11px Arial, sans-serif";
    series.forEach((s, i) => {
      const lx = pad.l + i * 130;
      ctx.fillStyle = s.color; ctx.fillRect(lx, H - 24, 14, 10);
      ctx.fillStyle = "#374151"; ctx.textAlign = "left";
      ctx.fillText(s.label, lx + 20, H - 24);
    });
    return canvas;
  }

  // ---------- 4) Indicator level breakdown — stacked bar --------------------
  // opts = { rows: [{ code, name, byLevel: { impact, outcome, output, other } }], lang }
  function drawIndicatorLevels(opts) {
    const { rows = [], lang = "fr" } = opts || {};
    const W = 700, H = Math.max(220, 60 + rows.length * 26 + 50);
    const { canvas, ctx } = makeCanvas(W, H);
    ctx.fillStyle = "#ffffff";
    ctx.fillRect(0, 0, W, H);
    ctx.fillStyle = "#111827";
    ctx.font = "bold 14px Arial, sans-serif";
    ctx.fillText(lang === "fr" ? "Indicateurs par niveau du cadre logique" : "Indicators by logframe level", 20, 8);

    if (rows.length === 0) {
      ctx.fillStyle = "#9ca3af"; ctx.font = "12px Arial"; ctx.textAlign = "center";
      ctx.fillText(lang === "fr" ? "(aucun indicateur)" : "(no indicators)", W / 2, H / 2);
      return canvas;
    }

    // "mixed" sits BETWEEN outcome and output in the logframe pyramid, so
    // we render it as its own stacked-bar segment to make cross-cutting
    // indicators visible in the report.
    const levels = ["impact", "outcome", "mixed", "output", "other"];
    const labels = {
      impact:  lang === "fr" ? "Impact"           : "Impact",
      outcome: lang === "fr" ? "Effet"            : "Outcome",
      mixed:   lang === "fr" ? "Mixte (Out/Out)"  : "Mixed (Out/Out)",
      output:  lang === "fr" ? "Produit"          : "Output",
      other:   lang === "fr" ? "Autre"            : "Other",
    };

    const padL = 20, padR = 20, padT = 38, labelW = 160;
    const totals = rows.map((r) => levels.reduce((s, k) => s + ((r.byLevel || {})[k] || 0), 0));
    const maxTot = Math.max(1, ...totals);
    const barAreaW = W - padL - padR - labelW;
    const rowH = 22;

    rows.forEach((r, i) => {
      const y = padT + i * (rowH + 4);
      // Project label
      ctx.fillStyle = "#374151"; ctx.font = "11px Arial"; ctx.textAlign = "left";
      ctx.fillText(((r.code || "—") + " — " + (r.name || "")).slice(0, 26), padL, y + 6);

      // Stacked bar
      const total = totals[i];
      let x = padL + labelW;
      levels.forEach((k) => {
        const v = (r.byLevel || {})[k] || 0;
        if (!v) return;
        const w = (v / maxTot) * barAreaW;
        ctx.fillStyle = LEVEL_COLORS[k];
        ctx.fillRect(x, y, w, rowH);
        // Inline value if there's room
        if (w >= 22) {
          ctx.fillStyle = "#fff"; ctx.font = "bold 10px Arial"; ctx.textAlign = "center";
          ctx.fillText(String(v), x + w / 2, y + 6);
        }
        x += w;
      });
      // Total at end
      ctx.fillStyle = "#111827"; ctx.font = "bold 11px Arial"; ctx.textAlign = "left";
      ctx.fillText(String(total), x + 6, y + 6);
    });

    // Legend
    ctx.font = "11px Arial, sans-serif";
    levels.forEach((k, i) => {
      const lx = padL + i * 130;
      ctx.fillStyle = LEVEL_COLORS[k]; ctx.fillRect(lx, H - 24, 14, 10);
      ctx.fillStyle = "#374151"; ctx.textAlign = "left";
      ctx.fillText(labels[k], lx + 20, H - 24);
    });
    return canvas;
  }

  // ---------- Public API ----------------------------------------------------
  async function dataURLs(opts) {
    const out = {};
    if (opts.indicatorStatus)   out.indicatorStatus   = drawIndicatorStatus(opts.indicatorStatus).toDataURL("image/png");
    if (opts.progressByProject) out.progressByProject = drawProgressByProject(opts.progressByProject).toDataURL("image/png");
    if (opts.budgetByProject)   out.budgetByProject   = drawBudgetByProject(opts.budgetByProject).toDataURL("image/png");
    if (opts.indicatorLevels)   out.indicatorLevels   = drawIndicatorLevels(opts.indicatorLevels).toDataURL("image/png");
    return out;
  }

  async function arrayBuffers(opts) {
    const out = {};
    if (opts.indicatorStatus)   out.indicatorStatus   = await canvasToArrayBuffer(drawIndicatorStatus(opts.indicatorStatus));
    if (opts.progressByProject) out.progressByProject = await canvasToArrayBuffer(drawProgressByProject(opts.progressByProject));
    if (opts.budgetByProject)   out.budgetByProject   = await canvasToArrayBuffer(drawBudgetByProject(opts.budgetByProject));
    if (opts.indicatorLevels)   out.indicatorLevels   = await canvasToArrayBuffer(drawIndicatorLevels(opts.indicatorLevels));
    return out;
  }

  // Dimensions shared between HTML and docx renderers (h is approximate
  // for the variable-height charts — used as a Word transformation height).
  const SIZES = {
    indicatorStatus:   { w: 460, h: 260 },
    progressByProject: { w: 700, h: 320 },
    budgetByProject:   { w: 700, h: 340 },
    indicatorLevels:   { w: 700, h: 280 },
  };

  window.reportingCharts = { dataURLs, arrayBuffers, SIZES };
})();
