/* global React */
// Hand-rolled SVG chart primitives for the manager portal — no chart libs,
// no build step. Pure presentational: every component takes data + optional
// color overrides so they read on both white cards (forest ink) and dark
// hero bands (cream strokes). All viewBox-based so they scale in grid cells.

const CHART_MONO = { fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.04em' };

// ─── Sparkline — a compact trend line with optional area fill ─────────
function Sparkline({ points = [], width = 220, height = 48, stroke = 'var(--forest)', fill = null, strokeWidth = 2 }) {
  if (!points.length) return null;
  const min = Math.min(...points), max = Math.max(...points);
  const span = max - min || 1;
  const px = (i) => (i / Math.max(1, points.length - 1)) * (width - 4) + 2;
  const py = (v) => height - 4 - ((v - min) / span) * (height - 8);
  const line = points.map((v, i) => `${px(i)},${py(v)}`).join(' ');
  return (
    <svg viewBox={`0 0 ${width} ${height}`} style={{ width: '100%', height: 'auto', display: 'block' }} aria-hidden="true">
      {fill && (
        <polygon points={`2,${height - 2} ${line} ${width - 2},${height - 2}`} fill={fill} stroke="none"/>
      )}
      <polyline points={line} fill="none" stroke={stroke} strokeWidth={strokeWidth} strokeLinejoin="round" strokeLinecap="round"/>
      <circle cx={px(points.length - 1)} cy={py(points[points.length - 1])} r={3} fill={stroke}/>
    </svg>
  );
}

// ─── Bars — vertical bars with mono labels underneath ─────────────────
function Bars({ data = [], height = 120, format = (v) => v, color = 'var(--forest)', mutedColor = 'rgba(28,73,42,0.18)', labelColor = 'currentColor' }) {
  if (!data.length) return null;
  const max = Math.max(...data.map(d => d.value), 1);
  return (
    <div style={{ display: 'flex', alignItems: 'flex-end', gap: 8, height: height + 34 }}>
      {data.map((d, i) => (
        <div key={d.label || i} style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4 }}
          title={d.hint || `${d.label}: ${format(d.value)}`}>
          <span style={{ ...CHART_MONO, fontWeight: 700, opacity: 0.85, color: labelColor }}>{format(d.value)}</span>
          <div style={{
            width: '100%', maxWidth: 44, borderRadius: '7px 7px 3px 3px',
            height: Math.max(4, (d.value / max) * height),
            background: d.highlight === false ? mutedColor : color,
            transition: 'height 0.5s cubic-bezier(0.32, 1.12, 0.35, 1)',
          }}/>
          <span style={{ ...CHART_MONO, opacity: 0.55, textTransform: 'uppercase', whiteSpace: 'nowrap', overflow: 'hidden', maxWidth: '100%', textOverflow: 'ellipsis', color: labelColor }}>{d.label}</span>
        </div>
      ))}
    </div>
  );
}

// ─── HBar — one horizontal fill row (sell-through style) ──────────────
function HBar({ label, value, max = 1, rightLabel, highlight = false, color = 'var(--forest)', track = 'rgba(28,73,42,0.1)' }) {
  const pct = Math.min(100, Math.max(0, (value / (max || 1)) * 100));
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '6px 0' }}>
      <span style={{ ...CHART_MONO, fontWeight: 700, textTransform: 'uppercase', width: 70, flexShrink: 0, opacity: highlight ? 1 : 0.6 }}>{label}</span>
      <div style={{ flex: 1, height: 10, borderRadius: 99, background: track, overflow: 'hidden' }}>
        <div style={{ width: `${pct}%`, height: '100%', borderRadius: 99, background: color, opacity: highlight ? 1 : 0.55, transition: 'width 0.6s cubic-bezier(0.32, 1.12, 0.35, 1)' }}/>
      </div>
      <span style={{ ...CHART_MONO, fontWeight: 700, width: 58, textAlign: 'right', flexShrink: 0 }}>{rightLabel != null ? rightLabel : `${Math.round(pct)}%`}</span>
    </div>
  );
}

// ─── PairedBars — this year vs last year, per month (the floor tracker) ─
function PairedBars({ data = [], aLabel = 'Last Year', bLabel = 'This Year', format = (v) => v, height = 140 }) {
  if (!data.length) return null;
  const max = Math.max(...data.flatMap(d => [d.a || 0, d.b || 0]), 1);
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 10, height: height + 26 }}>
        {data.map((d, i) => (
          <div key={d.label || i} style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4 }}
            title={`${d.label}: ${aLabel} ${format(d.a)} · ${bLabel} ${format(d.b)}`}>
            <div style={{ display: 'flex', alignItems: 'flex-end', gap: 3, width: '100%', maxWidth: 46, justifyContent: 'center', height }}>
              <div style={{ flex: 1, borderRadius: '5px 5px 2px 2px', height: Math.max(3, ((d.a || 0) / max) * height), background: 'var(--cream-warm, #E0D6BE)', border: '1px solid rgba(14,28,19,0.12)', boxSizing: 'border-box' }}/>
              <div style={{ flex: 1, borderRadius: '5px 5px 2px 2px', height: Math.max(3, ((d.b || 0) / max) * height), background: d.below ? 'var(--loss)' : 'var(--paper)', transition: 'height 0.5s cubic-bezier(0.32, 1.12, 0.35, 1)' }}/>
            </div>
            <span style={{ ...CHART_MONO, opacity: 0.55, textTransform: 'uppercase' }}>{d.label}</span>
          </div>
        ))}
      </div>
      <div style={{ display: 'flex', gap: 14, marginTop: 8, justifyContent: 'center' }}>
        <span style={{ ...CHART_MONO, display: 'inline-flex', alignItems: 'center', gap: 5, opacity: 0.7 }}>
          <span style={{ width: 10, height: 10, borderRadius: 3, background: 'var(--cream-warm, #E0D6BE)', border: '1px solid rgba(14,28,19,0.15)', display: 'inline-block' }}/> {aLabel}
        </span>
        <span style={{ ...CHART_MONO, display: 'inline-flex', alignItems: 'center', gap: 5, opacity: 0.7 }}>
          <span style={{ width: 10, height: 10, borderRadius: 3, background: 'var(--paper)', display: 'inline-block' }}/> {bLabel}
        </span>
        <span style={{ ...CHART_MONO, display: 'inline-flex', alignItems: 'center', gap: 5, opacity: 0.7 }}>
          <span style={{ width: 10, height: 10, borderRadius: 3, background: 'var(--loss)', display: 'inline-block' }}/> Below floor
        </span>
      </div>
    </div>
  );
}

// ─── AreaCurve — demand curve over the day (step interpolation) ───────
function AreaCurve({ points = [], xTicks = [], height = 120, color = 'var(--forest)', width = 480 }) {
  if (!points.length) return null;
  const maxY = Math.max(...points.map(p => p.y), 1);
  const minX = Math.min(...points.map(p => p.x));
  const maxX = Math.max(...points.map(p => p.x));
  const spanX = maxX - minX || 1;
  const px = (x) => ((x - minX) / spanX) * (width - 8) + 4;
  const py = (y) => height - 18 - (y / maxY) * (height - 30);
  let d = `M ${px(points[0].x)} ${py(points[0].y)}`;
  for (let i = 1; i < points.length; i++) {
    d += ` L ${px(points[i].x)} ${py(points[i - 1].y)} L ${px(points[i].x)} ${py(points[i].y)}`;
  }
  const area = `${d} L ${px(maxX)} ${height - 18} L ${px(minX)} ${height - 18} Z`;
  return (
    <svg viewBox={`0 0 ${width} ${height}`} style={{ width: '100%', height: 'auto', display: 'block' }} aria-hidden="true">
      <path d={area} fill={color} opacity={0.14} stroke="none"/>
      <path d={d} fill="none" stroke={color} strokeWidth={2} strokeLinejoin="round"/>
      <line x1={4} x2={width - 4} y1={height - 18} y2={height - 18} stroke="rgba(14,28,19,0.15)" strokeWidth={1}/>
      {xTicks.map(t => (
        <text key={t.x} x={px(t.x)} y={height - 5} textAnchor="middle"
          style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 9, opacity: 0.55 }} fill="currentColor">{t.label}</text>
      ))}
    </svg>
  );
}

// ─── Dial — semicircle gauge for a 0..1 rate ──────────────────────────
// Built with stroke-dasharray on a full <circle> rather than a hand-rolled
// elliptical-arc <path> — the previous version computed its own arc
// endpoint/large-arc-flag via trig and rendered as a broken/fragmented arc
// once a nonzero value was drawn on top of the track. A circle's dash
// pattern always starts at its own 3-o'clock point and runs clockwise, by
// spec, in every browser — rotating the whole circle 180° turns that into
// "start at 9 o'clock (left), sweep over the top, end at 3 o'clock
// (right)", which is exactly the semicircle-gauge behavior we want, with
// no per-value math that can go wrong.
function Dial({ value = 0, label, size = 120, color = 'var(--forest)', track = 'rgba(28,73,42,0.12)', textColor = 'currentColor' }) {
  const v = Math.min(1, Math.max(0, Number(value) || 0));
  const strokeW = Math.max(6, size * 0.08);
  const pad = strokeW / 2 + 2;
  const r = size / 2 - pad;
  const cx = size / 2, cy = size / 2 + pad * 0.4;
  const vbHeight = size / 2 + pad * 2 + 6;
  const circumference = 2 * Math.PI * r;
  const half = circumference / 2;
  const rotate = `rotate(180 ${cx} ${cy})`;
  return (
    // Fixed pixel width, not width:100% — a percentage width here depends on
    // the parent flex/grid item already having a resolved width, which isn't
    // guaranteed (a flex item with no explicit width/basis sizes off ITS
    // content, which is this 100%-wide div — a circular reference some
    // browsers resolve very wrong). Explicit width+height on the <svg>
    // itself makes this immune to whatever container it's dropped into.
    <div style={{ textAlign: 'center', width: size, flexShrink: 0 }}>
      <svg width={size} height={vbHeight} viewBox={`0 0 ${size} ${vbHeight}`} style={{ display: 'block', margin: '0 auto' }} aria-hidden="true">
        <circle cx={cx} cy={cy} r={r} fill="none" stroke={track} strokeWidth={strokeW} strokeLinecap="round"
          strokeDasharray={`${half} ${circumference}`} transform={rotate}/>
        {v > 0.004 && (
          <circle cx={cx} cy={cy} r={r} fill="none" stroke={color} strokeWidth={strokeW} strokeLinecap="round"
            strokeDasharray={`${v * half} ${circumference}`} transform={rotate}/>
        )}
        <text x={cx} y={cy - r * 0.28} textAnchor="middle" dominantBaseline="middle" fill={textColor}
          style={{ fontFamily: 'Bagel Fat One, cursive', fontSize: size * 0.19 }}>{Math.round(v * 100)}%</text>
      </svg>
      {label && (
        <div style={{ ...CHART_MONO, textTransform: 'uppercase', opacity: 0.6, marginTop: 6, lineHeight: 1.3, whiteSpace: 'normal' }}>{label}</div>
      )}
    </div>
  );
}

// ─── InteractiveChart — line/bar chart with hover crosshair + in-SVG
// tooltip and legend toggle buttons for multi-series data. Everything is
// drawn inside the same viewBox (no absolute-positioned tooltip div to
// keep in sync with a responsive SVG) — same "stay inside the SVG" bias
// that fixed the Dial gauge, applied up front here instead of after a bug.
function InteractiveChart({ series = [], categories = [], type = 'line', height = 140, format = (v) => v, legend = true }) {
  const [hidden, setHidden] = React.useState(() => new Set());
  const [hoverI, setHoverI] = React.useState(null);
  const svgRef = React.useRef(null);
  if (!series.length || !categories.length) return null;

  const visible = series.filter(s => !hidden.has(s.key));
  const width = 480;
  const padL = 4, padR = 4, padT = 10, padB = 22;
  const innerW = width - padL - padR;
  const innerH = height - padT - padB;
  const n = categories.length;
  const px = (i) => n <= 1 ? padL + innerW / 2 : padL + (i / (n - 1)) * innerW;
  const maxY = Math.max(1, ...visible.flatMap(s => s.values));
  const py = (v) => padT + innerH - (v / maxY) * innerH;
  const barGroupW = innerW / n;

  function handleMove(e) {
    const svg = svgRef.current;
    if (!svg) return;
    const rect = svg.getBoundingClientRect();
    const relX = ((e.clientX - rect.left) / rect.width) * width;
    let idx = Math.round(((relX - padL) / innerW) * (n - 1));
    setHoverI(Math.min(n - 1, Math.max(0, idx)));
  }
  function toggle(key) {
    setHidden(h => {
      const next = new Set(h);
      if (next.has(key)) next.delete(key); else next.add(key);
      return next.size === series.length ? h : next; // never hide every series
    });
  }

  let tooltip = null;
  if (hoverI !== null) {
    const vals = visible.map(s => ({ label: s.label, v: s.values[hoverI] || 0 }));
    const lines = [categories[hoverI], ...vals.map(v => `${v.label}: ${format(v.v)}`)];
    const boxW = Math.min(220, Math.max(84, Math.max(...lines.map(l => String(l).length)) * 5.3 + 16));
    const boxH = 18 + vals.length * 13;
    const tx = px(hoverI);
    let bx = tx + 10;
    if (bx + boxW > width - 4) bx = tx - boxW - 10;
    const by = padT;
    tooltip = (
      <g style={{ pointerEvents: 'none' }}>
        <rect x={bx} y={by} width={boxW} height={boxH} rx={6} fill="var(--forest-deep)" opacity={0.95}/>
        <text x={bx + 8} y={by + 12} style={{ fontFamily: 'var(--font-mono)', fontSize: 8.5, fontWeight: 700 }} fill="var(--cream)" opacity={0.65}>{categories[hoverI]}</text>
        {vals.map((v, i) => (
          <text key={i} x={bx + 8} y={by + 26 + i * 13} style={{ fontFamily: 'var(--font-mono)', fontSize: 9.5, fontWeight: 700 }} fill="var(--paper)">{v.label}: {format(v.v)}</text>
        ))}
      </g>
    );
  }

  return (
    <div>
      <svg ref={svgRef} viewBox={`0 0 ${width} ${height}`} style={{ width: '100%', height: 'auto', display: 'block', cursor: 'crosshair' }}
        onMouseMove={handleMove} onMouseLeave={() => setHoverI(null)}>
        <line x1={padL} x2={width - padR} y1={padT + innerH} y2={padT + innerH} stroke="rgba(14,28,19,0.15)" strokeWidth={1}/>

        {type === 'bar' && visible.map((s, si) => (
          <g key={s.key}>
            {categories.map((c, i) => {
              const bw = (barGroupW * 0.62) / visible.length;
              const gx = padL + i * barGroupW + barGroupW * 0.19 + si * bw;
              const v = s.values[i] || 0;
              const h = Math.max(1, (v / maxY) * innerH);
              return <rect key={i} x={gx} y={padT + innerH - h} width={Math.max(1, bw - 2)} height={h} rx={2}
                fill={s.color} opacity={hoverI === null || hoverI === i ? 1 : 0.35} style={{ transition: 'opacity 0.15s' }}/>;
            })}
          </g>
        ))}

        {type === 'line' && visible.map(s => (
          <g key={s.key}>
            <polyline points={s.values.map((v, i) => `${px(i)},${py(v)}`).join(' ')} fill="none" stroke={s.color} strokeWidth={2} strokeLinejoin="round" strokeLinecap="round"/>
            {s.values.map((v, i) => (hoverI === i ? <circle key={i} cx={px(i)} cy={py(v)} r={3.5} fill={s.color}/> : null))}
          </g>
        ))}

        {hoverI !== null && (
          <line x1={px(hoverI)} x2={px(hoverI)} y1={padT} y2={padT + innerH} stroke="rgba(14,28,19,0.25)" strokeWidth={1} strokeDasharray="3 3"/>
        )}

        {categories.map((c, i) => (
          (i === 0 || i === n - 1 || i === hoverI) ? (
            <text key={i} x={px(i)} y={height - 6} textAnchor={i === 0 ? 'start' : i === n - 1 ? 'end' : 'middle'}
              style={{ fontFamily: 'var(--font-mono)', fontSize: 9 }} opacity={hoverI === i ? 0.85 : 0.45} fill="currentColor">{c}</text>
          ) : null
        ))}

        {tooltip}
      </svg>

      {legend && series.length > 1 && (
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
          {series.map(s => {
            const off = hidden.has(s.key);
            return (
              <button key={s.key} onClick={() => toggle(s.key)} style={{
                display: 'inline-flex', alignItems: 'center', gap: 5, border: '1px solid var(--line-strong)',
                borderRadius: 999, padding: '3px 9px 3px 7px', background: off ? 'transparent' : 'var(--surface-sunken)',
                cursor: 'pointer', fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 700,
                color: off ? 'var(--ink-faint)' : 'var(--paper)', opacity: off ? 0.55 : 1,
              }}>
                <span style={{ width: 8, height: 8, borderRadius: 3, background: off ? 'var(--line-strong)' : s.color, display: 'inline-block' }}/>
                {s.label}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ─── TrendChart — one value against a real time axis ──────────────────
// InteractiveChart plots evenly-spaced categories from a zero baseline,
// which is right for "revenue per month" and wrong for anything that moves
// inside a narrow band or goes negative: an SBX rating drifting between
// 4.1 and 4.6, or a win-loss differential at -3, is either a flat line
// pinned to the top of the chart or off the canvas entirely.
//
// So this one scales to the data's own min..max (padded), spaces points by
// their actual timestamp rather than their index — a gap of six months and
// a gap of a day should not look the same — and can draw a zero rule when
// the sign of the value is the thing being read.
function TrendChart({ points = [], height = 190, color = 'var(--cream)', format = (v) => String(v), zeroLine = false, label = '' }) {
  const [hoverI, setHoverI] = React.useState(null);
  const svgRef = React.useRef(null);
  if (!points.length) return null;

  // A wide viewBox on purpose. These scale to their container's width, so
  // the ratio of viewBox units to on-screen pixels sets BOTH how tall the
  // chart ends up and how large its type renders. At 480 units across a
  // full-width card the thing was a third of a screen tall with axis labels
  // at ~19px; 760 lands it near 250px with 12px labels.
  const width = 760;
  const padL = 6, padR = 6, padT = 14, padB = 24;
  const innerW = width - padL - padR;
  const innerH = height - padT - padB;

  const ys = points.map(p => p.y);
  let minY = Math.min(...ys), maxY = Math.max(...ys);
  if (zeroLine) { minY = Math.min(minY, 0); maxY = Math.max(maxY, 0); }
  if (minY === maxY) { minY -= 1; maxY += 1; }          // a flat series still needs a band
  const padY = (maxY - minY) * 0.12;
  minY -= padY; maxY += padY;

  const xs = points.map(p => p.at);
  const minX = Math.min(...xs), maxX = Math.max(...xs);
  const spanX = maxX - minX;
  // One point, or several all at the same instant: centre them rather than
  // dividing by zero.
  const px = (at) => (spanX <= 0 ? padL + innerW / 2 : padL + ((at - minX) / spanX) * innerW);
  const py = (v) => padT + innerH - ((v - minY) / (maxY - minY)) * innerH;

  const line = points.map(p => `${px(p.at)},${py(p.y)}`).join(' ');
  const area = `M ${px(points[0].at)} ${padT + innerH} L ${points.map(p => `${px(p.at)} ${py(p.y)}`).join(' L ')} L ${px(points[points.length - 1].at)} ${padT + innerH} Z`;

  function handleMove(e) {
    const svg = svgRef.current;
    if (!svg) return;
    const rect = svg.getBoundingClientRect();
    const relX = ((e.clientX - rect.left) / rect.width) * width;
    // Nearest by actual x position, since points aren't evenly spaced.
    let best = 0, bestD = Infinity;
    points.forEach((p, i) => {
      const d = Math.abs(px(p.at) - relX);
      if (d < bestD) { bestD = d; best = i; }
    });
    setHoverI(best);
  }

  const dayLabel = (ms) => new Date(ms).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: '2-digit' });

  let tooltip = null;
  if (hoverI !== null && points[hoverI]) {
    const p = points[hoverI];
    const lines = [dayLabel(p.at), `${label ? `${label}: ` : ''}${format(p.y)}`];
    const boxW = Math.min(220, Math.max(90, Math.max(...lines.map(l => l.length)) * 5.4 + 16));
    const boxH = 32;
    let bx = px(p.at) + 10;
    if (bx + boxW > width - 4) bx = px(p.at) - boxW - 10;
    if (bx < 4) bx = 4;
    const by = Math.max(padT, Math.min(py(p.y) - boxH - 8, padT + innerH - boxH));
    tooltip = (
      <g style={{ pointerEvents: 'none' }}>
        <rect x={bx} y={by} width={boxW} height={boxH} rx={6} fill="var(--forest-deep, #0E1C13)" opacity={0.96}/>
        <text x={bx + 8} y={by + 12} style={{ fontFamily: 'var(--font-mono)', fontSize: 8.5, fontWeight: 700 }} fill="var(--cream)" opacity={0.65}>{lines[0]}</text>
        <text x={bx + 8} y={by + 25} style={{ fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 700 }} fill="var(--paper)">{lines[1]}</text>
      </g>
    );
  }

  return (
    <svg ref={svgRef} viewBox={`0 0 ${width} ${height}`}
      style={{ width: '100%', height: 'auto', display: 'block', cursor: 'crosshair' }}
      onMouseMove={handleMove} onMouseLeave={() => setHoverI(null)}>
      {zeroLine && minY < 0 && maxY > 0 && (
        <line x1={padL} x2={width - padR} y1={py(0)} y2={py(0)}
          stroke="currentColor" strokeWidth={1} opacity={0.28} strokeDasharray="3 3"/>
      )}
      <line x1={padL} x2={width - padR} y1={padT + innerH} y2={padT + innerH} stroke="currentColor" strokeWidth={1} opacity={0.15}/>

      {points.length > 1 && <path d={area} fill={color} opacity={0.1} stroke="none"/>}
      {points.length > 1
        ? <polyline points={line} fill="none" stroke={color} strokeWidth={2} strokeLinejoin="round" strokeLinecap="round"/>
        : <circle cx={px(points[0].at)} cy={py(points[0].y)} r={4} fill={color}/>}

      {hoverI !== null && points[hoverI] && (<>
        <line x1={px(points[hoverI].at)} x2={px(points[hoverI].at)} y1={padT} y2={padT + innerH}
          stroke="currentColor" strokeWidth={1} opacity={0.3} strokeDasharray="3 3"/>
        <circle cx={px(points[hoverI].at)} cy={py(points[hoverI].y)} r={4} fill={color}/>
      </>)}

      <text x={padL} y={height - 7} textAnchor="start" style={{ fontFamily: 'var(--font-mono)', fontSize: 9 }} opacity={0.45} fill="currentColor">{dayLabel(minX)}</text>
      {spanX > 0 && (
        <text x={width - padR} y={height - 7} textAnchor="end" style={{ fontFamily: 'var(--font-mono)', fontSize: 9 }} opacity={0.45} fill="currentColor">{dayLabel(maxX)}</text>
      )}

      {tooltip}
    </svg>
  );
}

Object.assign(window, { Sparkline, Bars, HBar, PairedBars, AreaCurve, Dial, InteractiveChart, TrendChart });
