// SVG chart primitives - all hand-rolled, no library

const Sparkline = ({ data, color = 'currentColor', height = 36, fill = true, animated = true }) => {
  if (!data || !data.length) return null;
  const w = 180, h = height;
  const min = Math.min(...data), max = Math.max(...data);
  const pad = (max - min) * 0.08 || 1;
  const lo = min - pad, hi = max + pad;
  const x = i => (i / (data.length - 1)) * w;
  const y = v => h - ((v - lo) / (hi - lo)) * h;
  const d = data.map((v, i) => `${i === 0 ? 'M' : 'L'} ${x(i).toFixed(2)} ${y(v).toFixed(2)}`).join(' ');
  const area = `${d} L ${w} ${h} L 0 ${h} Z`;
  const gradId = React.useMemo(() => `g${Math.random().toString(36).slice(2, 8)}`, []);
  return (
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ width: '100%', height: h, display: 'block' }}>
      <defs>
        <linearGradient id={gradId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.22" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      {fill && <path d={area} fill={`url(#${gradId})`} />}
      <path d={d} fill="none" stroke={color} strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
    </svg>
  );
};

// Multi-series chart with grid + hover scrubber
const MultiSeriesChart = ({ series, labels, height = 260, hours = 168 }) => {
  const wrap = React.useRef(null);
  const [w, setW] = React.useState(900);
  const [hover, setHover] = React.useState(null);

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

  const padL = 40, padR = 16, padT = 14, padB = 28;
  const innerW = Math.max(100, w - padL - padR);
  const innerH = height - padT - padB;

  const active = series.filter(s => s.on);
  const allVals = active.flatMap(s => s.data);
  const minV = active.length ? Math.min(...allVals) : 0;
  const maxV = active.length ? Math.max(...allVals) : 1;
  const pad = (maxV - minV) * 0.1 || 1;
  const lo = minV - pad, hi = maxV + pad;

  const N = series[0]?.data.length || 1;
  const x = i => padL + (i / (N - 1)) * innerW;
  const y = v => padT + (1 - (v - lo) / (hi - lo)) * innerH;

  const ticks = 4;
  const yTicks = Array.from({ length: ticks + 1 }, (_, i) => lo + ((hi - lo) * i) / ticks);
  const dayTicks = labels;

  const onMove = e => {
    const rect = wrap.current.getBoundingClientRect();
    const relX = e.clientX - rect.left;
    const t = (relX - padL) / innerW;
    const i = Math.round(Math.max(0, Math.min(1, t)) * (N - 1));
    setHover(i);
  };

  return (
    <div ref={wrap} style={{ position: 'relative', width: '100%' }}>
      <svg viewBox={`0 0 ${w} ${height}`} style={{ width: '100%', height }} onMouseMove={onMove} onMouseLeave={() => setHover(null)}>
        {/* y grid */}
        {yTicks.map((v, i) => (
          <g key={i}>
            <line x1={padL} x2={w - padR} y1={y(v)} y2={y(v)} stroke="var(--line)" strokeWidth="1" strokeDasharray={i === 0 ? '0' : '2 4'} />
            <text x={padL - 8} y={y(v) + 3} fontSize="10" textAnchor="end" fill="var(--ink-4)" fontFamily="Geist Mono">
              {v >= 100 ? v.toFixed(0) : v.toFixed(1)}
            </text>
          </g>
        ))}
        {/* x labels */}
        {dayTicks.map((label, i) => {
          const px = padL + (i / (dayTicks.length - 1)) * innerW;
          return (
            <text key={i} x={px} y={height - 8} fontSize="10" textAnchor="middle" fill="var(--ink-4)" fontFamily="Geist Mono">
              {label}
            </text>
          );
        })}
        {/* series */}
        {active.map(s => {
          const d = s.data.map((v, i) => `${i === 0 ? 'M' : 'L'} ${x(i).toFixed(1)} ${y(v).toFixed(1)}`).join(' ');
          return (
            <g key={s.key}>
              <path d={d} stroke={s.color} fill="none" strokeWidth="1.6" strokeLinejoin="round" strokeLinecap="round" />
            </g>
          );
        })}
        {/* hover */}
        {hover != null && (
          <g>
            <line x1={x(hover)} x2={x(hover)} y1={padT} y2={padT + innerH} stroke="var(--ink-2)" strokeWidth="1" strokeDasharray="2 3" opacity="0.4" />
            {active.map(s => (
              <circle key={s.key} cx={x(hover)} cy={y(s.data[hover])} r="3.5" fill="var(--bg-elev)" stroke={s.color} strokeWidth="1.6" />
            ))}
          </g>
        )}
      </svg>
      {hover != null && (
        <div style={{
          position: 'absolute',
          left: Math.min(w - 180, Math.max(0, ((hover / (N - 1)) * innerW + padL) - 80)),
          top: 8,
          background: 'var(--bg-elev)',
          border: '1px solid var(--line-strong)',
          borderRadius: 10,
          padding: '8px 10px',
          minWidth: 160,
          boxShadow: 'var(--shadow-md)',
          pointerEvents: 'none',
          fontSize: 12,
        }}>
          <div style={{ color: 'var(--ink-3)', fontSize: 11, marginBottom: 4, fontFamily: 'Geist Mono' }}>
            t-{Math.round((N - hover) * (hours / N))}h
          </div>
          {active.map(s => (
            <div key={s.key} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '2px 0' }}>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                <span style={{ width: 8, height: 8, borderRadius: 2, background: s.color }} />
                <span style={{ color: 'var(--ink-3)' }}>{s.label}</span>
              </span>
              <span className="mono" style={{ fontVariantNumeric: 'tabular-nums' }}>{s.data[hover].toFixed(s.dp ?? 1)}{s.unit ? ` ${s.unit}` : ''}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

// Radial AQI gauge
const AQIGauge = ({ value, max = 200, size = 220 }) => {
  const stroke = 12;
  const r = (size - stroke) / 2 - 4;
  const cx = size / 2, cy = size / 2;
  const startAngle = -210, endAngle = 30;
  const sweep = endAngle - startAngle; // 240°
  const pct = Math.max(0, Math.min(1, value / max));
  const valAngle = startAngle + sweep * pct;

  const polar = (a) => {
    const rad = (a * Math.PI) / 180;
    return [cx + r * Math.cos(rad), cy + r * Math.sin(rad)];
  };
  const arc = (a1, a2) => {
    const [x1, y1] = polar(a1);
    const [x2, y2] = polar(a2);
    const large = Math.abs(a2 - a1) > 180 ? 1 : 0;
    return `M ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2}`;
  };

  // bands: 0-50 good, 51-100 moderate, 101-150 sensitive, 151-200 unhealthy
  const bands = [
    { from: 0,   to: 50,  color: 'oklch(72% 0.14 160)' },
    { from: 50,  to: 100, color: 'oklch(82% 0.16 92)' },
    { from: 100, to: 150, color: 'oklch(74% 0.16 60)' },
    { from: 150, to: 200, color: 'oklch(64% 0.18 30)' },
  ];

  return (
    <svg viewBox={`0 0 ${size} ${size}`} style={{ width: size, height: size }}>
      {/* track */}
      <path d={arc(startAngle, endAngle)} stroke="var(--line)" strokeWidth={stroke} fill="none" strokeLinecap="round" />
      {/* bands */}
      {bands.map((b, i) => {
        const a1 = startAngle + sweep * (b.from / max);
        const a2 = startAngle + sweep * (b.to / max);
        return <path key={i} d={arc(a1, a2)} stroke={b.color} strokeWidth={stroke} fill="none" strokeLinecap="butt" opacity="0.9" />;
      })}
      {/* needle */}
      {(() => {
        const [nx, ny] = polar(valAngle);
        return (
          <g>
            <circle cx={cx} cy={cy} r="6" fill="var(--ink)" />
            <line x1={cx} y1={cy} x2={nx} y2={ny} stroke="var(--ink)" strokeWidth="2.5" strokeLinecap="round" />
            <circle cx={nx} cy={ny} r="4" fill="var(--bg-elev)" stroke="var(--ink)" strokeWidth="2" />
          </g>
        );
      })()}
      {/* tick labels */}
      {[0, 50, 100, 150, 200].map(t => {
        const a = startAngle + sweep * (t / max);
        const [tx, ty] = polar(a);
        const [lx, ly] = (() => { const rr = r + 16; const rad = a * Math.PI / 180; return [cx + rr * Math.cos(rad), cy + rr * Math.sin(rad)]; })();
        return <text key={t} x={lx} y={ly + 3} textAnchor="middle" fontSize="10" fill="var(--ink-4)" fontFamily="Geist Mono">{t}</text>;
      })}
    </svg>
  );
};

// Bar / column chart for forecast
const ForecastBars = ({ data, height = 100 }) => {
  const w = 480, h = height;
  const max = Math.max(...data) * 1.15;
  const bw = w / data.length;
  const colorFor = v => {
    if (v < 50) return 'oklch(72% 0.14 160)';
    if (v < 100) return 'oklch(82% 0.16 92)';
    if (v < 150) return 'oklch(74% 0.16 60)';
    return 'oklch(64% 0.18 30)';
  };
  return (
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ width: '100%', height: h, display: 'block' }}>
      {data.map((v, i) => {
        const bh = (v / max) * (h - 14);
        return (
          <g key={i}>
            <rect x={i * bw + 1} y={h - bh - 12} width={bw - 2} height={bh} fill={colorFor(v)} rx="2" opacity="0.85" />
            {(i % 3 === 0) && (
              <text x={i * bw + bw / 2} y={h - 2} fontSize="8" textAnchor="middle" fill="var(--ink-4)" fontFamily="Geist Mono">
                {i === 0 ? 'now' : `+${i}h`}
              </text>
            )}
          </g>
        );
      })}
    </svg>
  );
};

// Tiny radial percent ring
const PercentRing = ({ value, size = 56, stroke = 5, color = 'var(--accent-deep)' }) => {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const pct = Math.max(0, Math.min(100, value));
  const off = c - (c * pct) / 100;
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      <circle cx={size/2} cy={size/2} r={r} stroke="var(--line)" strokeWidth={stroke} fill="none" />
      <circle cx={size/2} cy={size/2} r={r} stroke={color} strokeWidth={stroke} fill="none"
        strokeDasharray={c} strokeDashoffset={off} strokeLinecap="round"
        transform={`rotate(-90 ${size/2} ${size/2})`} />
      <text x={size/2} y={size/2 + 4} textAnchor="middle" fontSize="13" fontWeight="500" fill="var(--ink)" fontFamily="Geist Mono">{pct}</text>
    </svg>
  );
};

// Heatmap (zones × hours)
const Heatmap = ({ rows, cols = 24, seed = 1 }) => {
  const r = mulberry32(seed);
  const grid = rows.map((_, ri) => Array.from({ length: cols }, (_, ci) => {
    return Math.round(15 + r() * 65 + Math.sin(ci / 4 + ri) * 15);
  }));
  const colorFor = v => {
    if (v < 30) return 'oklch(86% 0.10 160)';
    if (v < 55) return 'oklch(86% 0.13 110)';
    if (v < 80) return 'oklch(82% 0.16 70)';
    return 'oklch(70% 0.16 30)';
  };
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '110px 1fr', gap: 10, alignItems: 'center' }}>
      <div />
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)`, fontSize: 9, color: 'var(--ink-4)', fontFamily: 'Geist Mono' }}>
        {Array.from({ length: cols }).map((_, i) => (
          <div key={i} style={{ textAlign: 'center', visibility: i % 4 === 0 ? 'visible' : 'hidden' }}>{i.toString().padStart(2,'0')}</div>
        ))}
      </div>
      {rows.map((row, ri) => (
        <React.Fragment key={ri}>
          <div style={{ fontSize: 12, color: 'var(--ink-2)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{row}</div>
          <div style={{ display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)`, gap: 2 }}>
            {grid[ri].map((v, ci) => (
              <div key={ci} title={`${row} · ${ci}:00 → ${v}`} style={{
                background: colorFor(v),
                height: 18,
                borderRadius: 3,
              }} />
            ))}
          </div>
        </React.Fragment>
      ))}
    </div>
  );
};

function mulberry32(seed) {
  return function () {
    seed |= 0; seed = (seed + 0x6D2B79F5) | 0;
    let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

Object.assign(window, { Sparkline, MultiSeriesChart, AQIGauge, ForecastBars, PercentRing, Heatmap });
window.__chartsReady = true;
