// studio-charts.jsx — small chart components (Sparkline, AreaChart, BarRow, Donut, Heatgrid)
// All editorial-minimal: thin lines, no axes, tabular numerals, monochrome by default.

const { useState: useStateC, useRef: useRefC, useEffect: useEffectC } = React;

// ─── Formatters ────────────────────────────────────────────────────────────
function fmtNum(n) {
  if (n >= 1_000_000) return (n / 1_000_000).toFixed(n >= 10_000_000 ? 1 : 2) + 'M';
  if (n >= 1_000)     return (n / 1_000).toFixed(n >= 10_000 ? 0 : 1) + 'K';
  return n.toLocaleString();
}
function fmtMoney(n, frac = 0) {
  return '$' + n.toLocaleString(undefined, { minimumFractionDigits: frac, maximumFractionDigits: frac });
}
function fmtDelta(d) {
  const sign = d > 0 ? '+' : d < 0 ? '−' : '';
  return `${sign}${Math.abs(d).toFixed(d % 1 === 0 ? 0 : 1)}%`;
}

// ─── Sparkline ─────────────────────────────────────────────────────────────
function Sparkline({ data, color = '#1a1a18', fill = true, height = 28, width = 96 }) {
  if (!data || data.length === 0) return null;
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const stepX = width / (data.length - 1);
  const points = data.map((v, i) => [i * stepX, height - ((v - min) / range) * (height - 4) - 2]);
  const path = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(' ');
  const area = `${path} L${width} ${height} L0 ${height} Z`;
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} style={{ display: 'block', overflow: 'visible' }}>
      {fill && <path d={area} fill={color} opacity="0.06"/>}
      <path d={path} fill="none" stroke={color} strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/>
      <circle cx={points[points.length - 1][0]} cy={points[points.length - 1][1]} r="2" fill={color}/>
    </svg>
  );
}

// ─── Area / Line chart with hover crosshair ────────────────────────────────
function AreaChart({ data, height = 220, valueFmt = fmtNum, labelFmt = (d) => d.label }) {
  const ref = useRefC(null);
  const [w, setW] = useStateC(640);
  const [hover, setHover] = useStateC(null);
  useEffectC(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);

  const pad = { l: 12, r: 12, t: 18, b: 28 };
  const innerW = Math.max(1, w - pad.l - pad.r);
  const innerH = height - pad.t - pad.b;
  const vals = data.map(d => d.value);
  const min = Math.min(...vals, 0), max = Math.max(...vals);
  const range = max - min || 1;
  const stepX = data.length > 1 ? innerW / (data.length - 1) : innerW;
  const points = data.map((d, i) => ({
    x: pad.l + i * stepX,
    y: pad.t + innerH - ((d.value - min) / range) * innerH,
    d,
  }));
  const line = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(' ');
  const area = `${line} L${points[points.length - 1].x} ${pad.t + innerH} L${points[0].x} ${pad.t + innerH} Z`;

  // gridlines: 4
  const grid = Array.from({ length: 5 }, (_, i) => pad.t + (innerH / 4) * i);

  // hover
  const onMove = (e) => {
    const rect = ref.current.getBoundingClientRect();
    const x = e.clientX - rect.left;
    let idx = Math.round((x - pad.l) / stepX);
    idx = Math.max(0, Math.min(points.length - 1, idx));
    setHover(idx);
  };

  return (
    <div ref={ref} style={{ position: 'relative', width: '100%' }} onMouseLeave={() => setHover(null)} onMouseMove={onMove}>
      <svg width={w} height={height} viewBox={`0 0 ${w} ${height}`} style={{ display: 'block' }}>
        {grid.map((y, i) => (
          <line key={i} x1={pad.l} x2={w - pad.r} y1={y} y2={y} stroke="rgba(20,20,18,0.06)" strokeWidth="0.6"/>
        ))}
        <path d={area} fill="#1a1a18" opacity="0.05"/>
        <path d={line} fill="none" stroke="#1a1a18" strokeWidth="1.6" strokeLinecap="round"/>
        {data.length <= 16 && points.map((p, i) => (
          <circle key={i} cx={p.x} cy={p.y} r="2" fill="#1a1a18"/>
        ))}
        {/* x labels (sparse) */}
        {points.map((p, i) => (
          i === 0 || i === points.length - 1 || i === Math.floor(points.length / 2) ? (
            <text key={i} x={p.x} y={height - 6} fontSize="10" fill="#8a8580" textAnchor={i === 0 ? 'start' : i === points.length - 1 ? 'end' : 'middle'}>
              {labelFmt(p.d)}
            </text>
          ) : null
        ))}
        {/* hover */}
        {hover !== null && (
          <g>
            <line x1={points[hover].x} x2={points[hover].x} y1={pad.t} y2={pad.t + innerH} stroke="#1a1a18" strokeWidth="0.6" strokeDasharray="2 3"/>
            <circle cx={points[hover].x} cy={points[hover].y} r="5" fill="#FAFAF7" stroke="#1a1a18" strokeWidth="1.6"/>
          </g>
        )}
      </svg>
      {hover !== null && (
        <div style={{
          position: 'absolute',
          left: Math.max(8, Math.min(w - 140, points[hover].x - 60)),
          top: Math.max(0, points[hover].y - 56),
          padding: '8px 10px', borderRadius: 8,
          background: '#1a1a18', color: '#FAFAF7', fontSize: 11.5,
          pointerEvents: 'none', whiteSpace: 'nowrap',
          boxShadow: '0 8px 24px rgba(20,20,18,0.18)',
        }}>
          <div style={{ color: 'rgba(250,249,247,0.6)', fontSize: 10, letterSpacing: '0.12em', textTransform: 'uppercase' }}>{labelFmt(data[hover])}</div>
          <div style={{ marginTop: 2, fontWeight: 500, fontVariantNumeric: 'tabular-nums' }}>{valueFmt(data[hover].value)}</div>
        </div>
      )}
    </div>
  );
}

// ─── Bar row (horizontal bars, ranked list) ────────────────────────────────
function BarRow({ items, max, valueFmt = (v) => v + '%', barColor = '#1a1a18' }) {
  const m = max || Math.max(...items.map(i => i.value));
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
      {items.map((it, i) => (
        <div key={i}>
          <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12.5, marginBottom: 6 }}>
            <span>{it.label}</span>
            <span style={{ color: '#5a5a54', fontVariantNumeric: 'tabular-nums' }}>{valueFmt(it.value)}</span>
          </div>
          <div style={{ height: 4, background: 'rgba(20,20,18,0.06)', borderRadius: 999, overflow: 'hidden' }}>
            <div style={{
              width: `${(it.value / m) * 100}%`, height: '100%',
              background: barColor, borderRadius: 999,
              transition: 'width .4s ease',
            }}/>
          </div>
        </div>
      ))}
    </div>
  );
}

// ─── Donut ─────────────────────────────────────────────────────────────────
function Donut({ segments, size = 160, thickness = 18, centerLabel, centerValue }) {
  const total = segments.reduce((s, x) => s + x.value, 0);
  const r = (size - thickness) / 2;
  const c = 2 * Math.PI * r;
  let offset = 0;
  return (
    <div style={{ position: 'relative', width: size, height: size }}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="rgba(20,20,18,0.06)" strokeWidth={thickness}/>
        {segments.map((s, i) => {
          const len = (s.value / total) * c;
          const dash = `${len} ${c - len}`;
          const el = (
            <circle key={i}
              cx={size/2} cy={size/2} r={r}
              fill="none" stroke={s.color}
              strokeWidth={thickness}
              strokeDasharray={dash}
              strokeDashoffset={-offset}
              transform={`rotate(-90 ${size/2} ${size/2})`}/>
          );
          offset += len;
          return el;
        })}
      </svg>
      {centerLabel !== undefined && (
        <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column', textAlign: 'center', pointerEvents: 'none' }}>
          <div style={{ fontSize: 11, letterSpacing: '0.12em', textTransform: 'uppercase', color: '#8a8580' }}>{centerLabel}</div>
          {centerValue !== undefined && <div style={{ fontSize: 24, fontWeight: 500, marginTop: 2, letterSpacing: '-0.02em' }}>{centerValue}</div>}
        </div>
      )}
    </div>
  );
}

// ─── Heatgrid (24h active hours) ───────────────────────────────────────────
function HeatStrip({ data, max }) {
  const m = max || Math.max(...data);
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(24, 1fr)', gap: 2 }}>
      {data.map((v, h) => {
        const alpha = v / m;
        return (
          <div key={h} title={`${h}:00 — ${v}`} style={{
            height: 38, borderRadius: 4,
            background: `rgba(20,20,18,${0.05 + alpha * 0.85})`,
            position: 'relative',
          }}>
            {(h === 0 || h === 6 || h === 12 || h === 18) && (
              <div style={{ position: 'absolute', bottom: -16, left: 0, fontSize: 9, color: '#8a8580' }}>
                {h === 0 ? '12a' : h === 12 ? '12p' : h < 12 ? h + 'a' : (h - 12) + 'p'}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

// ─── Stat card (used on overview + page headers) ──────────────────────────
function StatCard({ label, value, delta, currency, unit, spark, accent = '#1a1a18', sublabel }) {
  const formatted = currency ? fmtMoney(value, value % 1 ? 2 : 0) : (typeof value === 'number' ? (unit === '%' ? value.toFixed(1) : fmtNum(value)) : value);
  const positive = delta >= 0;
  return (
    <div style={{
      padding: 20, borderRadius: 14,
      background: '#FAFAF7', border: '1px solid rgba(20,20,18,0.06)',
      display: 'flex', flexDirection: 'column', gap: 12, justifyContent: 'space-between',
      minHeight: 132,
    }}>
      <div style={{ fontSize: 11, letterSpacing: '0.16em', textTransform: 'uppercase', color: '#8a8580' }}>{label}</div>
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 8 }}>
        <div>
          <div style={{ fontSize: 32, letterSpacing: '-0.02em', fontVariantNumeric: 'tabular-nums', fontWeight: 500, lineHeight: 1 }}>
            {formatted}{unit ? <span style={{ fontSize: 18, color: '#8a8580', marginLeft: 2 }}>{unit}</span> : null}
          </div>
          {delta !== undefined && (
            <div style={{
              marginTop: 8, fontSize: 11.5, letterSpacing: '0.04em',
              color: positive ? '#1a6b3a' : '#a8331a', fontVariantNumeric: 'tabular-nums',
              display: 'flex', alignItems: 'center', gap: 4,
            }}>
              <span>{positive ? '↑' : '↓'}</span>
              <span>{fmtDelta(delta)}</span>
              {sublabel && <span style={{ color: '#8a8580', marginLeft: 4 }}>· {sublabel}</span>}
            </div>
          )}
          {delta === undefined && sublabel && (
            <div style={{ marginTop: 8, fontSize: 11.5, letterSpacing: '0.04em', color: '#8a8580' }}>{sublabel}</div>
          )}
        </div>
        {spark && <Sparkline data={spark} color={accent}/>}
      </div>
    </div>
  );
}

// ─── Trend pill ────────────────────────────────────────────────────────────
function TrendPill({ value }) {
  const positive = value >= 0;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 3,
      fontSize: 11, fontVariantNumeric: 'tabular-nums',
      color: positive ? '#1a6b3a' : '#a8331a',
    }}>
      <span>{positive ? '↑' : '↓'}</span>
      <span>{fmtDelta(value)}</span>
    </span>
  );
}

// ─── Daily trend (small multiples) ─────────────────────────────────────────
// Views, product clicks and clicks-to-store over time.
//
// Three facets rather than three coloured lines on one plot: modaBoard's
// palette is a single ink, so there is no categorical ramp to tell three
// series apart, and inventing one would fight the rest of the Studio. Each
// facet carries one series and names it, so identity never rests on colour.
//
// They share ONE y-scale. Independent scales would let 2 views and 200 clicks
// draw the same peak — the small-multiples version of the dual-axis mistake.

// mb_events_by_day() only returns days that have events. Drawing that directly
// would join across gaps and imply activity that never happened, so fill the
// range with explicit zeros first.
function fillDailySeries(rows, from, to) {
  const byDay = new Map();
  (rows || []).forEach(r => {
    const key = String(r.day).slice(0, 10);
    byDay.set(key, {
      views: Number(r.views || 0),
      productClicks: Number(r.product_clicks || 0),
      outboundClicks: Number(r.outbound_clicks || 0),
    });
  });
  const out = [];
  const cur = new Date(Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), from.getUTCDate()));
  const end = new Date(Date.UTC(to.getUTCFullYear(), to.getUTCMonth(), to.getUTCDate()));
  // Guard against a pathological range producing an unbounded loop.
  let guard = 0;
  while (cur <= end && guard++ < 800) {
    const key = cur.toISOString().slice(0, 10);
    out.push(Object.assign({ day: key }, byDay.get(key) || { views: 0, productClicks: 0, outboundClicks: 0 }));
    cur.setUTCDate(cur.getUTCDate() + 1);
  }
  return out;
}

function DailyTrend({ days, height = 68 }) {
  const ref = useRefC(null);
  const [w, setW] = useStateC(640);
  const [hover, setHover] = useStateC(null);

  useEffectC(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);

  const SERIES = [
    { key: 'views',          label: 'Outfit views' },
    { key: 'productClicks',  label: 'Product clicks' },
    { key: 'outboundClicks', label: 'Clicks to store' },
  ];

  const pad = { l: 10, r: 10, t: 8, b: 6 };
  const innerW = Math.max(1, w - pad.l - pad.r);
  const innerH = Math.max(1, height - pad.t - pad.b);
  const n = Math.max(1, days.length);
  const stepX = n > 1 ? innerW / (n - 1) : innerW;
  // Shared across all three facets so the panels are comparable.
  const max = Math.max(1, ...days.map(d => Math.max(d.views, d.productClicks, d.outboundClicks)));

  const xAt = (i) => pad.l + i * stepX;
  const yAt = (v) => pad.t + innerH - (v / max) * innerH;

  const onMove = (e) => {
    const rect = ref.current.getBoundingClientRect();
    let idx = Math.round((e.clientX - rect.left - pad.l) / stepX);
    setHover(Math.max(0, Math.min(n - 1, idx)));
  };

  const fmtDay = (key) => {
    const d = new Date(key + 'T00:00:00Z');
    return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', timeZone: 'UTC' });
  };

  return (
    <div ref={ref} data-chart="daily-trend" style={{ position: 'relative', width: '100%' }}
         onMouseLeave={() => setHover(null)} onMouseMove={onMove}>
      {SERIES.map((s, si) => {
        const pts = days.map((d, i) => ({ x: xAt(i), y: yAt(d[s.key]), v: d[s.key] }));
        const line = pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(' ');
        const area = pts.length
          ? `${line} L${pts[pts.length - 1].x.toFixed(1)} ${pad.t + innerH} L${pts[0].x.toFixed(1)} ${pad.t + innerH} Z`
          : '';
        const total = days.reduce((a, d) => a + d[s.key], 0);
        return (
          <div key={s.key} style={{ marginTop: si === 0 ? 0 : 14 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 2 }}>
              <span style={{ fontSize: 10.5, letterSpacing: '0.14em', textTransform: 'uppercase', color: '#8a8580' }}>{s.label}</span>
              <span style={{ fontSize: 12, fontVariantNumeric: 'tabular-nums', color: '#5a5a54' }}>{fmtNum(total)}</span>
            </div>
            <svg width={w} height={height} viewBox={`0 0 ${w} ${height}`} style={{ display: 'block' }}>
              {/* Recessive baseline + midline, hairline and solid */}
              {[pad.t, pad.t + innerH / 2, pad.t + innerH].map((y, i) => (
                <line key={i} x1={pad.l} x2={w - pad.r} y1={y} y2={y}
                      stroke="rgba(20,20,18,0.06)" strokeWidth="1"/>
              ))}
              {area && <path d={area} fill="#1a1a18" opacity="0.05"/>}
              {line && <path d={line} fill="none" stroke="#1a1a18" strokeWidth="2"
                             strokeLinecap="round" strokeLinejoin="round"/>}
              {hover !== null && pts[hover] && (
                <g>
                  <line x1={pts[hover].x} x2={pts[hover].x} y1={pad.t} y2={pad.t + innerH}
                        stroke="rgba(20,20,18,0.35)" strokeWidth="1"/>
                  <circle cx={pts[hover].x} cy={pts[hover].y} r="4"
                          fill="#FAFAF7" stroke="#1a1a18" strokeWidth="2"/>
                </g>
              )}
            </svg>
          </div>
        );
      })}

      {/* One x-axis for all three facets */}
      <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 4, fontSize: 10, color: '#8a8580' }}>
        <span>{days.length ? fmtDay(days[0].day) : ''}</span>
        <span>{days.length ? fmtDay(days[days.length - 1].day) : ''}</span>
      </div>

      {/* One tooltip covering all three series for the hovered day */}
      {hover !== null && days[hover] && (
        <div style={{
          position: 'absolute',
          left: Math.max(4, Math.min(w - 168, xAt(hover) - 80)),
          top: 0,
          padding: '9px 11px', borderRadius: 8, width: 160,
          background: '#1a1a18', color: '#FAFAF7', fontSize: 11.5,
          pointerEvents: 'none', boxShadow: '0 8px 24px rgba(20,20,18,0.18)',
        }}>
          <div style={{ color: 'rgba(250,249,247,0.6)', fontSize: 10, letterSpacing: '0.12em', textTransform: 'uppercase' }}>
            {fmtDay(days[hover].day)}
          </div>
          {SERIES.map(s => (
            <div key={s.key} style={{ marginTop: 3, display: 'flex', justifyContent: 'space-between', gap: 10 }}>
              <span style={{ color: 'rgba(250,249,247,0.75)' }}>{s.label}</span>
              <span style={{ fontWeight: 500, fontVariantNumeric: 'tabular-nums' }}>{fmtNum(days[hover][s.key])}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

Object.assign(window, {
  fmtNum, fmtMoney, fmtDelta,
  Sparkline, AreaChart, BarRow, Donut, HeatStrip, StatCard, TrendPill,
  DailyTrend, fillDailySeries,
});
