// charts.jsx — lightweight SVG chart primitives, theme-aware & interactive
const { useState, useRef, useMemo, useId } = React;

const nf = (n, d = 0) => new Intl.NumberFormat('fr-CA', { maximumFractionDigits: d }).format(n);
const money = (n, d = 0) => '$' + nf(n, d);

// ── Sparkline (tiny, no axes) ────────────────────────────────────────
function Sparkline({ data, color = 'var(--accent)', w = 120, h = 36, fill = true, strokeWidth = 2 }) {
  const id = useId().replace(/:/g, '');
  const min = Math.min(...data), max = Math.max(...data);
  const rng = max - min || 1;
  const pts = data.map((v, i) => [(i / (data.length - 1)) * w, h - 4 - ((v - min) / rng) * (h - 8)]);
  const line = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ');
  const area = `${line} L ${w} ${h} L 0 ${h} Z`;
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: 'block', overflow: 'visible' }}>
      <defs>
        <linearGradient id={'sp' + id} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.28" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      {fill && <path d={area} fill={`url(#sp${id})`} />}
      <path d={line} fill="none" stroke={color} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" />
      <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="2.6" fill={color} />
    </svg>
  );
}

// ── Area / Line chart with hover ─────────────────────────────────────
// series: [{ name, data:[], color }]; labels optional
function LineChart({ series, labels, h = 240, area = true, yFmt = (v) => nf(v), valueFmt, height }) {
  const H = height || h;
  const wrapRef = useRef(null);
  const [hover, setHover] = useState(null);
  const id = useId().replace(/:/g, '');
  const pad = { l: 44, r: 14, t: 14, b: 26 };
  const VBW = 800;
  const n = series[0].data.length;
  const allVals = series.flatMap(s => s.data);
  let max = Math.max(...allVals), min = Math.min(0, ...allVals);
  max = max * 1.12 || 1;
  const innerW = VBW - pad.l - pad.r;
  const innerH = H - pad.t - pad.b;
  const x = (i) => pad.l + (i / (n - 1)) * innerW;
  const y = (v) => pad.t + innerH - ((v - min) / (max - min || 1)) * innerH;
  const ticks = 4;
  const fmt = valueFmt || yFmt;

  const onMove = (e) => {
    const rect = wrapRef.current.getBoundingClientRect();
    const px = (e.clientX - rect.left) / rect.width * VBW;
    let i = Math.round((px - pad.l) / innerW * (n - 1));
    i = Math.max(0, Math.min(n - 1, i));
    setHover(i);
  };
  return (
    <div ref={wrapRef} style={{ position: 'relative', width: '100%' }} onMouseMove={onMove} onMouseLeave={() => setHover(null)}>
      <svg viewBox={`0 0 ${VBW} ${H}`} width="100%" height={H} style={{ display: 'block', overflow: 'visible' }}>
        <defs>
          {series.map((s, si) => (
            <linearGradient key={si} id={`la${id}${si}`} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={s.color} stopOpacity="0.26" />
              <stop offset="100%" stopColor={s.color} stopOpacity="0" />
            </linearGradient>
          ))}
        </defs>
        {Array.from({ length: ticks + 1 }).map((_, t) => {
          const vv = min + (t / ticks) * (max - min);
          const yy = y(vv);
          return (
            <g key={t}>
              <line x1={pad.l} y1={yy} x2={VBW - pad.r} y2={yy} stroke="var(--grid)" strokeWidth="1" />
              <text x={pad.l - 8} y={yy + 4} textAnchor="end" fontSize="11" fill="var(--ink-3)">{fmt(vv)}</text>
            </g>
          );
        })}
        {labels && labels.map((l, i) => (
          (i % Math.ceil(n / 8) === 0 || i === n - 1) &&
          <text key={i} x={x(i)} y={H - 6} textAnchor="middle" fontSize="11" fill="var(--ink-3)">{l}</text>
        ))}
        {series.map((s, si) => {
          const pts = s.data.map((v, i) => [x(i), y(v)]);
          const line = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ');
          return (
            <g key={si}>
              {area && <path d={`${line} L ${x(n - 1)} ${y(min)} L ${x(0)} ${y(min)} Z`} fill={`url(#la${id}${si})`} />}
              <path d={line} fill="none" stroke={s.color} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
            </g>
          );
        })}
        {hover != null && (
          <g>
            <line x1={x(hover)} y1={pad.t} x2={x(hover)} y2={pad.t + innerH} stroke="var(--ink-3)" strokeWidth="1" strokeDasharray="3 3" />
            {series.map((s, si) => <circle key={si} cx={x(hover)} cy={y(s.data[hover])} r="4" fill="var(--card)" stroke={s.color} strokeWidth="2.5" />)}
          </g>
        )}
      </svg>
      {hover != null && (
        <div style={{
          position: 'absolute', top: 6, pointerEvents: 'none',
          left: `calc(${(x(hover) / VBW) * 100}% )`, transform: `translateX(${x(hover) > VBW * 0.7 ? '-105%' : '8px'})`,
          background: 'var(--card-2)', border: '1px solid var(--line-2)', borderRadius: 10, padding: '8px 10px',
          boxShadow: 'var(--shadow)', fontSize: 12, zIndex: 5, whiteSpace: 'nowrap',
        }}>
          {labels && <div style={{ color: 'var(--ink-3)', fontSize: 11, marginBottom: 4 }}>{labels[hover]}</div>}
          {series.map((s, si) => (
            <div key={si} style={{ display: 'flex', alignItems: 'center', gap: 6, color: 'var(--ink)', fontWeight: 600 }}>
              <span style={{ width: 8, height: 8, borderRadius: 3, background: s.color, display: 'inline-block' }}></span>
              {s.name ? <span style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{s.name}</span> : null}
              <span style={{ marginLeft: 'auto', paddingLeft: 10 }}>{fmt(s.data[hover])}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ── Vertical bar chart (grouped or single) with hover ────────────────
function BarChart({ data, labels, color = 'var(--accent)', colors, h = 220, valueFmt = (v) => nf(v), rounded = true }) {
  const [hover, setHover] = useState(null);
  const pad = { l: 40, r: 10, t: 12, b: 26 };
  const VBW = 800, H = h;
  const max = Math.max(...data) * 1.12 || 1;
  const innerW = VBW - pad.l - pad.r, innerH = H - pad.t - pad.b;
  const bw = innerW / data.length;
  const barW = Math.min(bw * 0.6, 46);
  const y = (v) => pad.t + innerH - (v / max) * innerH;
  return (
    <div style={{ position: 'relative', width: '100%' }}>
      <svg viewBox={`0 0 ${VBW} ${H}`} width="100%" height={H} style={{ display: 'block', overflow: 'visible' }}>
        {Array.from({ length: 4 }).map((_, t) => {
          const yy = pad.t + (t / 3) * innerH;
          const vv = max - (t / 3) * max;
          return <g key={t}><line x1={pad.l} y1={yy} x2={VBW - pad.r} y2={yy} stroke="var(--grid)" /><text x={pad.l - 8} y={yy + 4} textAnchor="end" fontSize="11" fill="var(--ink-3)">{valueFmt(vv)}</text></g>;
        })}
        {data.map((v, i) => {
          const cx = pad.l + bw * i + bw / 2;
          const bh = innerH - (y(v) - pad.t);
          const c = colors ? colors[i] : color;
          return (
            <g key={i} onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(null)} style={{ cursor: 'default' }}>
              <rect x={cx - barW / 2} y={pad.t} width={barW} height={innerH} fill="transparent" />
              <rect x={cx - barW / 2} y={y(v)} width={barW} height={Math.max(bh, 0)} rx={rounded ? 6 : 0} fill={c} opacity={hover == null || hover === i ? 1 : 0.45} style={{ transition: 'opacity .15s' }} />
              {labels && <text x={cx} y={H - 6} textAnchor="middle" fontSize="11" fill="var(--ink-3)">{labels[i]}</text>}
              {hover === i && <text x={cx} y={y(v) - 8} textAnchor="middle" fontSize="12" fontWeight="700" fill="var(--ink)">{valueFmt(v)}</text>}
            </g>
          );
        })}
      </svg>
    </div>
  );
}

// ── Horizontal ranking bars ──────────────────────────────────────────
function HBars({ items, valueFmt = (v) => nf(v), accent = 'var(--accent)', showEmoji = true, lang = 'fr', max: maxOverride }) {
  const max = maxOverride || Math.max(...items.map(i => i.value)) || 1;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
      {items.map((it, i) => (
        <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          {showEmoji && it.emoji && <span style={{ fontSize: 18, width: 22, textAlign: 'center' }}>{it.emoji}</span>}
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 5, fontSize: 13 }}>
              <span style={{ color: 'var(--ink)', fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {it.label ? (typeof it.label === 'string' ? it.label : it.label[lang]) : (typeof it.name === 'string' ? it.name : it.name[lang])}
              </span>
              <span style={{ color: 'var(--ink-2)', fontWeight: 700, fontVariantNumeric: 'tabular-nums', marginLeft: 10 }}>{valueFmt(it.value)}</span>
            </div>
            <div style={{ height: 8, background: 'var(--line-2)', borderRadius: 6, overflow: 'hidden' }}>
              <div style={{ width: (it.value / max * 100) + '%', height: '100%', background: it.color || accent, borderRadius: 6, transition: 'width .4s ease' }}></div>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

// ── Donut ────────────────────────────────────────────────────────────
function Donut({ data, size = 168, thickness = 22, centerLabel, centerSub, lang = 'fr' }) {
  const [hover, setHover] = useState(null);
  const total = data.reduce((a, b) => a + b.value, 0) || 1;
  const r = (size - thickness) / 2, cx = size / 2, cy = size / 2, C = 2 * Math.PI * r;
  let acc = 0;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 18, flexWrap: 'wrap' }}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ flexShrink: 0 }}>
        <circle cx={cx} cy={cy} r={r} fill="none" stroke="var(--line-2)" strokeWidth={thickness} />
        {data.map((d, i) => {
          const frac = d.value / total;
          const dash = frac * C;
          const off = acc * C;
          acc += frac;
          return (
            <circle key={i} cx={cx} cy={cy} r={r} fill="none" stroke={d.color} strokeWidth={hover === i ? thickness + 3 : thickness}
              strokeDasharray={`${dash} ${C - dash}`} strokeDashoffset={-off} transform={`rotate(-90 ${cx} ${cy})`}
              strokeLinecap="butt" style={{ transition: 'stroke-width .15s', cursor: 'default' }}
              opacity={hover == null || hover === i ? 1 : 0.5}
              onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(null)} />
          );
        })}
        {centerLabel !== undefined && (
          <>
            <text x={cx} y={cy - 2} textAnchor="middle" fontSize="26" fontWeight="800" fill="var(--ink)">{hover != null ? Math.round(data[hover].value / total * 100) + '%' : centerLabel}</text>
            <text x={cx} y={cy + 18} textAnchor="middle" fontSize="11.5" fill="var(--ink-3)">{hover != null ? (typeof data[hover].label === 'object' ? data[hover].label[lang] : data[hover].label) : centerSub}</text>
          </>
        )}
      </svg>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 9, minWidth: 120 }}>
        {data.map((d, i) => (
          <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 9, fontSize: 13, opacity: hover == null || hover === i ? 1 : 0.5 }}
            onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(null)}>
            <span style={{ width: 10, height: 10, borderRadius: 3, background: d.color }}></span>
            <span style={{ color: 'var(--ink-2)', flex: 1 }}>{typeof d.label === 'object' ? d.label[lang] : d.label}</span>
            <span style={{ color: 'var(--ink)', fontWeight: 700, fontVariantNumeric: 'tabular-nums' }}>{nf(d.value)}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ── Semi-circle gauge ────────────────────────────────────────────────
function Gauge({ value, max = 100, label, sub, color = 'var(--accent)', size = 200, suffix = '%' }) {
  const r = size / 2 - 18, cx = size / 2, cy = size / 2;
  const startA = Math.PI, endA = 0;
  const frac = Math.max(0, Math.min(1, value / max));
  const a = startA + (endA - startA) * frac;
  const polar = (ang) => [cx + r * Math.cos(ang), cy + r * Math.sin(ang) * -1 + 0];
  // arc path helper for semicircle (top half)
  const arc = (a0, a1) => {
    const p0 = [cx + r * Math.cos(a0), cy - r * Math.sin(a0)];
    const p1 = [cx + r * Math.cos(a1), cy - r * Math.sin(a1)];
    const large = Math.abs(a1 - a0) > Math.PI ? 1 : 0;
    const sweep = a1 > a0 ? 0 : 1;
    return `M ${p0[0]} ${p0[1]} A ${r} ${r} 0 ${large} ${sweep} ${p1[0]} ${p1[1]}`;
  };
  return (
    <svg width={size} height={size * 0.66} viewBox={`0 0 ${size} ${size * 0.66}`} style={{ overflow: 'visible' }}>
      <path d={arc(Math.PI, 0)} fill="none" stroke="var(--line-2)" strokeWidth="16" strokeLinecap="round" />
      <path d={arc(Math.PI, a)} fill="none" stroke={color} strokeWidth="16" strokeLinecap="round" />
      <text x={cx} y={cy - 6} textAnchor="middle" fontSize="34" fontWeight="800" fill="var(--ink)">{label}{suffix && <tspan fontSize="18" fill="var(--ink-3)">{suffix}</tspan>}</text>
      {sub && <text x={cx} y={cy + 16} textAnchor="middle" fontSize="12" fill="var(--ink-3)">{sub}</text>}
    </svg>
  );
}

// ── Cohort heatmap ───────────────────────────────────────────────────
function CohortHeatmap({ cohorts, lang = 'fr', cols = 8 }) {
  const colColor = (v) => {
    if (v == null) return 'transparent';
    const t = v / 100;
    return `color-mix(in oklab, var(--accent) ${Math.round(12 + t * 88)}%, var(--card-2))`;
  };
  return (
    <div style={{ overflowX: 'auto' }}>
      <table style={{ borderCollapse: 'separate', borderSpacing: 4, width: '100%' }}>
        <thead>
          <tr>
            <th style={{ fontSize: 11, color: 'var(--ink-3)', fontWeight: 600, textAlign: 'left', padding: '0 8px' }}></th>
            <th style={{ fontSize: 11, color: 'var(--ink-3)', fontWeight: 600 }}>n</th>
            {Array.from({ length: cols }).map((_, i) => <th key={i} style={{ fontSize: 11, color: 'var(--ink-3)', fontWeight: 600 }}>S{i}</th>)}
          </tr>
        </thead>
        <tbody>
          {cohorts.map((c, ri) => (
            <tr key={ri}>
              <td style={{ fontSize: 12, color: 'var(--ink-2)', fontWeight: 600, paddingRight: 8, whiteSpace: 'nowrap' }}>{c.label[lang]}</td>
              <td style={{ fontSize: 12, color: 'var(--ink-3)', textAlign: 'center', fontVariantNumeric: 'tabular-nums' }}>{c.size}</td>
              {c.vals.map((v, ci) => (
                <td key={ci} style={{
                  background: colColor(v), borderRadius: 7, textAlign: 'center', minWidth: 42, height: 34,
                  fontSize: 12, fontWeight: 600, fontVariantNumeric: 'tabular-nums',
                  color: v == null ? 'transparent' : (v > 55 ? 'var(--accent-ink)' : 'var(--ink)'),
                  border: v == null ? '1px dashed var(--line)' : 'none',
                }}>{v == null ? '' : v}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

// ── Stacked horizontal bar (single, segmented) ───────────────────────
function StackedBar({ data, lang = 'fr', h = 14 }) {
  const total = data.reduce((a, b) => a + b.value, 0) || 1;
  return (
    <div>
      <div style={{ display: 'flex', height: h, borderRadius: 8, overflow: 'hidden', gap: 2 }}>
        {data.map((d, i) => <div key={i} title={d.label?.[lang]} style={{ width: (d.value / total * 100) + '%', background: d.color }}></div>)}
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px 18px', marginTop: 12 }}>
        {data.map((d, i) => (
          <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 12.5 }}>
            <span style={{ width: 9, height: 9, borderRadius: 3, background: d.color }}></span>
            <span style={{ color: 'var(--ink-2)' }}>{typeof d.label === 'object' ? d.label[lang] : d.label}</span>
            <span style={{ color: 'var(--ink)', fontWeight: 700 }}>{nf(d.value)}</span>
            <span style={{ color: 'var(--ink-3)' }}>· {Math.round(d.value / total * 100)}%</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ── Funnel ───────────────────────────────────────────────────────────
function Funnel({ steps, lang = 'fr' }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      {steps.map((s, i) => (
        <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <div style={{ width: 150, fontSize: 13, color: 'var(--ink-2)', flexShrink: 0 }}>{s.label[lang]}</div>
          <div style={{ flex: 1, height: 30, background: 'var(--line-2)', borderRadius: 8, overflow: 'hidden', position: 'relative' }}>
            <div style={{
              width: s.pct + '%', height: '100%', borderRadius: 8,
              background: `linear-gradient(90deg, var(--accent), var(--accent-2))`,
              display: 'flex', alignItems: 'center', paddingLeft: 10, transition: 'width .5s ease',
            }}>
              <span style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--accent-ink)' }}>{nf(s.value)}</span>
            </div>
          </div>
          <div style={{ width: 42, textAlign: 'right', fontSize: 13, fontWeight: 700, color: 'var(--ink)', fontVariantNumeric: 'tabular-nums' }}>{s.pct}%</div>
        </div>
      ))}
    </div>
  );
}

Object.assign(window, { nf, money, Sparkline, LineChart, BarChart, HBars, Donut, Gauge, CohortHeatmap, StackedBar, Funnel });
