// Tiny pure-SVG chart primitives for the admin dashboards. All client-side,
// no external deps. Exposed on `window.MMCharts` so any admin section can
// pull them in.

(function () {
  // ---------- Sparkline ----------
  // Compact line graph. `values` is an array of numbers (oldest first).
  function Sparkline({ values, width = 96, height = 28, color, fill = true, strokeWidth = 1.6 }) {
    if (!Array.isArray(values) || values.length === 0) {
      return <svg width={width} height={height} aria-hidden="true" />;
    }
    const max = Math.max(1, ...values);
    const min = Math.min(0, ...values);
    const span = Math.max(1, max - min);
    const stepX = values.length > 1 ? width / (values.length - 1) : 0;
    const yAt = (v) => height - 2 - ((v - min) / span) * (height - 4);

    const linePath = values.map((v, i) => `${i === 0 ? 'M' : 'L'}${(i * stepX).toFixed(1)},${yAt(v).toFixed(1)}`).join(' ');
    const fillPath = fill && values.length > 1
      ? `${linePath} L${((values.length - 1) * stepX).toFixed(1)},${height} L0,${height} Z`
      : null;

    const c = color || 'var(--id-brand-blue)';
    return (
      <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} aria-hidden="true" className="mm-sparkline">
        {fillPath && <path d={fillPath} fill={c} fillOpacity="0.15" />}
        <path d={linePath} stroke={c} strokeWidth={strokeWidth} fill="none" strokeLinecap="round" strokeLinejoin="round" />
        <circle cx={(values.length - 1) * stepX} cy={yAt(values[values.length - 1])} r="2.5" fill={c} />
      </svg>
    );
  }

  // ---------- TrendDelta ----------
  // ↑ +12.4% / ↓ −5.2% / — (no change). Negative-good controls the colouring
  // (e.g. drop-off lower-is-better).
  function TrendDelta({ current, previous, suffix = '%', negativeGood = false, mute = false }) {
    if (previous == null || current == null) return <span className="mm-delta mm-delta-zero">—</span>;
    if (previous === 0 && current === 0) return <span className="mm-delta mm-delta-zero">—</span>;
    if (previous === 0)                 return <span className="mm-delta mm-delta-up">▲ new</span>;
    const pct = ((current - previous) / Math.max(1, Math.abs(previous))) * 100;
    const rounded = Math.abs(pct) < 0.05 ? 0 : Math.round(pct * 10) / 10;
    const sign = rounded > 0 ? '▲' : rounded < 0 ? '▼' : '–';
    const direction = rounded > 0 ? 'up' : rounded < 0 ? 'down' : 'zero';
    const cls = mute
      ? 'mm-delta-zero'
      : direction === 'zero'
        ? 'mm-delta-zero'
        : (direction === 'up' ? (negativeGood ? 'mm-delta-down' : 'mm-delta-up')
                              : (negativeGood ? 'mm-delta-up'   : 'mm-delta-down'));
    return (
      <span className={`mm-delta ${cls}`}>
        {sign} {rounded > 0 ? '+' : ''}{rounded}{suffix}
      </span>
    );
  }

  // ---------- KPICard ----------
  // Hero KPI tile: big number, optional sparkline + delta vs previous period,
  // optional sub-label. Replaces the old plain <KPI> for upgraded sections.
  function KPICard({ label, value, sub, sparkline, color, current, previous, suffix, negativeGood, accent }) {
    return (
      <div className="mm-kpi-card" style={accent ? { '--kpi-accent': accent } : {}}>
        <div className="mm-kpi-row">
          <div className="mm-kpi-l">{label}</div>
          {(current != null || previous != null) && (
            <TrendDelta current={current} previous={previous} suffix={suffix} negativeGood={negativeGood} />
          )}
        </div>
        <div className="mm-kpi-row mm-kpi-row-bottom">
          <div className="mm-kpi-v">{value}</div>
          {sparkline?.length > 0 && (
            <Sparkline values={sparkline} color={color} width={96} height={28} />
          )}
        </div>
        {sub && <div className="mm-kpi-s">{sub}</div>}
      </div>
    );
  }

  // ---------- TimeseriesChart ----------
  // Multi-line SVG chart for daily series. `series` is an array of:
  //   { id, label, values, color }
  // `labels` is an optional array of x-axis labels (one per data point).
  function TimeseriesChart({ series, labels, height = 220, ySuffix = '', stack = false }) {
    if (!series?.length || !series[0].values?.length) {
      return <div className="mm-empty-chart">No data in this window.</div>;
    }
    const N = series[0].values.length;
    const padL = 36, padR = 12, padT = 14, padB = 28;
    const W = 720;
    const innerW = W - padL - padR;
    const innerH = height - padT - padB;

    // Compute max across all series.
    let max = 0;
    for (const s of series) for (const v of s.values) if (v > max) max = v;
    if (max === 0) max = 1;
    // Round max to a "nice" number.
    const nice = (m) => {
      const p = Math.pow(10, Math.max(0, Math.floor(Math.log10(m)) - 1));
      return Math.ceil(m / p) * p;
    };
    const niceMax = nice(max);

    const xAt = (i) => padL + (N === 1 ? innerW / 2 : (i / (N - 1)) * innerW);
    const yAt = (v) => padT + innerH - (v / niceMax) * innerH;

    // Pick ~4 horizontal gridline values.
    const gridY = [0, 0.25, 0.5, 0.75, 1].map((p) => Math.round(niceMax * p));

    return (
      <div className="mm-tsc-wrap">
        <svg viewBox={`0 0 ${W} ${height}`} className="mm-tsc" preserveAspectRatio="none" style={{ height }}>
          {/* Gridlines */}
          {gridY.map((g, i) => {
            const y = yAt(g);
            return (
              <g key={i}>
                <line x1={padL} y1={y} x2={W - padR} y2={y} stroke="currentColor" strokeOpacity="0.08" strokeDasharray="3 3" />
                <text x={padL - 6} y={y} fontSize="10" fill="currentColor" opacity="0.55" textAnchor="end" dominantBaseline="middle">
                  {g.toLocaleString()}{ySuffix}
                </text>
              </g>
            );
          })}
          {/* Series */}
          {series.map((s) => {
            const path = s.values.map((v, i) => `${i === 0 ? 'M' : 'L'}${xAt(i).toFixed(1)},${yAt(v).toFixed(1)}`).join(' ');
            const last = s.values.length - 1;
            return (
              <g key={s.id}>
                <path d={path} stroke={s.color || 'var(--id-brand-blue)'} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
                <circle cx={xAt(last)} cy={yAt(s.values[last])} r="3" fill={s.color || 'var(--id-brand-blue)'} />
              </g>
            );
          })}
          {/* X-axis labels (~6 labels, evenly spaced) */}
          {labels?.length && Array.from({ length: Math.min(6, N) }, (_, k) => {
            const i = Math.round((k / Math.max(1, Math.min(5, N - 1))) * (N - 1));
            const lbl = labels[i];
            return (
              <text key={k} x={xAt(i)} y={height - 6} fontSize="10" fill="currentColor" opacity="0.55" textAnchor="middle">
                {lbl}
              </text>
            );
          })}
        </svg>
        <div className="mm-tsc-legend">
          {series.map((s) => (
            <span key={s.id} className="mm-tsc-leg">
              <span className="mm-tsc-swatch" style={{ background: s.color || 'var(--id-brand-blue)' }} />
              {s.label}
            </span>
          ))}
        </div>
      </div>
    );
  }

  // ---------- BarChart (vertical) ----------
  // Simple vertical bars with labels. Each entry: { label, value, color? }.
  function BarChart({ data, height = 180, valueFormat }) {
    if (!data?.length) return <div className="mm-empty-chart">No data.</div>;
    const max = Math.max(1, ...data.map((d) => Number(d.value) || 0));
    return (
      <div className="mm-barchart" style={{ height }}>
        {data.map((d, i) => {
          const h = Math.max(2, Math.round(((Number(d.value) || 0) / max) * 100));
          return (
            <div key={i} className="mm-bar-col" title={`${d.label}: ${d.value}`}>
              <div className="mm-bar-v">{valueFormat ? valueFormat(d.value) : d.value}</div>
              <div className="mm-bar-bg">
                <div className="mm-bar-fill" style={{ height: h + '%', background: d.color || 'var(--id-brand-blue)' }} />
              </div>
              <div className="mm-bar-l">{d.label}</div>
            </div>
          );
        })}
      </div>
    );
  }

  // ---------- DonutChart ----------
  // Single-ring donut for category share. `slices` is an array of:
  //   { label, value, color }
  function DonutChart({ slices, size = 160, thickness = 22, centerLabel, centerSub }) {
    const total = slices.reduce((s, x) => s + (Number(x.value) || 0), 0);
    if (total === 0) return <div className="mm-empty-chart">No data.</div>;
    const r = (size - thickness) / 2;
    const cx = size / 2, cy = size / 2;
    const C = 2 * Math.PI * r;
    let acc = 0;
    return (
      <div className="mm-donut-wrap" style={{ width: size }}>
        <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className="mm-donut">
          {slices.map((s, i) => {
            const frac = (Number(s.value) || 0) / total;
            const len = frac * C;
            const dasharray = `${len} ${C}`;
            const dashoffset = -acc;
            acc += len;
            return (
              <circle
                key={i}
                cx={cx} cy={cy} r={r}
                fill="none"
                stroke={s.color || 'var(--id-brand-blue)'}
                strokeWidth={thickness}
                strokeDasharray={dasharray}
                strokeDashoffset={dashoffset}
                transform={`rotate(-90 ${cx} ${cy})`}
              />
            );
          })}
          {centerLabel && (
            <text x={cx} y={cy - 2} fontSize="22" fontWeight="800" fill="currentColor" textAnchor="middle" dominantBaseline="middle">
              {centerLabel}
            </text>
          )}
          {centerSub && (
            <text x={cx} y={cy + 16} fontSize="10" fill="currentColor" opacity="0.6" textAnchor="middle" dominantBaseline="middle">
              {centerSub}
            </text>
          )}
        </svg>
        <div className="mm-donut-legend">
          {slices.map((s, i) => {
            const frac = (Number(s.value) || 0) / total;
            return (
              <div key={i} className="mm-donut-row">
                <span className="mm-donut-swatch" style={{ background: s.color || 'var(--id-brand-blue)' }} />
                <span className="mm-donut-label">{s.label}</span>
                <span className="mm-donut-pct">{(frac * 100).toFixed(0)}%</span>
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  // ---------- HealthBadge ----------
  // Coloured pill that flips based on a numeric threshold or boolean.
  function HealthBadge({ status, label }) {
    return <span className={`mm-health mm-health-${status}`}>{label}</span>;
  }

  // ---------- CSV export helper ----------
  // `rows` should be an array of plain objects. `name` is the file name (no ext).
  function downloadCsv(name, rows) {
    if (!rows?.length) return;
    const cols = Array.from(rows.reduce((s, r) => { Object.keys(r || {}).forEach((k) => s.add(k)); return s; }, new Set()));
    const escape = (v) => {
      if (v == null) return '';
      const s = typeof v === 'string' ? v : JSON.stringify(v);
      return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
    };
    const csv = [cols.join(','), ...rows.map((r) => cols.map((c) => escape(r[c])).join(','))].join('\n');
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
    if (window.downloadBlob) window.downloadBlob(blob, name + '.csv');
    else {
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a'); a.href = url; a.download = name + '.csv'; a.click();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    }
  }

  window.MMCharts = {
    Sparkline, TrendDelta, KPICard,
    TimeseriesChart, BarChart, DonutChart,
    HealthBadge, downloadCsv,
  };
})();
