// Shared React components: Hypnogram, Sparkline, VitalsLine, icons, etc.
const { useState, useEffect, useRef, useMemo } = React;

// ---------- Icons (minimal line icons) ----------
function Icon({ name, size = 16 }) {
  const s = size;
  const p = { width: s, height: s, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' };
  switch (name) {
    case 'moon': return <svg {...p}><path d="M20 15.5A8 8 0 0 1 8.5 4a8 8 0 1 0 11.5 11.5z"/></svg>;
    case 'pulse': return <svg {...p}><path d="M3 12h3l3-8 4 16 3-8h5"/></svg>;
    case 'trend': return <svg {...p}><path d="M3 17l6-6 4 4 8-8"/><path d="M14 7h7v7"/></svg>;
    case 'device': return <svg {...p}><rect x="4" y="6" width="16" height="12" rx="2"/><path d="M8 20v-2M16 20v-2M12 10h.01"/><circle cx="12" cy="14" r="2"/></svg>;
    case 'bed': return <svg {...p}><path d="M3 18v-6a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v6"/><path d="M3 15h18M7 10V8a2 2 0 0 1 2-2h2"/></svg>;
    case 'heart': return <svg {...p}><path d="M20.84 4.6a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.07a5.5 5.5 0 0 0-7.78 7.78l1.06 1.07L12 21.23l7.78-7.78 1.06-1.07a5.5 5.5 0 0 0 0-7.78z"/></svg>;
    case 'lung': return <svg {...p}><path d="M6.5 4C5 4 4 5 4 7c0 3 0 6 2 9 1.5 2 4 2 4-1V4z"/><path d="M17.5 4c1.5 0 2.5 1 2.5 3 0 3 0 6-2 9-1.5 2-4 2-4-1V4z"/><path d="M12 4v10"/></svg>;
    case 'settings': return <svg {...p}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>;
    case 'chevron-right': return <svg {...p}><path d="M9 6l6 6-6 6"/></svg>;
    case 'chevron-down': return <svg {...p}><path d="M6 9l6 6 6-6"/></svg>;
    case 'download': return <svg {...p}><path d="M12 3v12m-4-4l4 4 4-4M5 21h14"/></svg>;
    case 'calendar': return <svg {...p}><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 10h18M8 3v4M16 3v4"/></svg>;
    case 'wifi': return <svg {...p}><path d="M5 12a10 10 0 0 1 14 0M8.5 15.5a5 5 0 0 1 7 0"/><circle cx="12" cy="19" r="1" fill="currentColor"/></svg>;
    case 'activity': return <svg {...p}><path d="M22 12h-4l-3 9-6-18-3 9H2"/></svg>;
    case 'home': return <svg {...p}><path d="M3 12l9-9 9 9v9a2 2 0 0 1-2 2h-3v-7h-8v7H5a2 2 0 0 1-2-2z"/></svg>;
  }
  return null;
}

// ---------- Hypnogram ----------
// Displays sleep stages over time. Stepped / area style.
function Hypnogram({ data, durationMin, height = 140, compact = false, onsetLabel, wakeLabel }) {
  const STAGES = ['awake', 'rem', 'light', 'deep'];
  const rowH = height / STAGES.length;
  const width = 1000; // viewBox width

  const segments = [];
  for (let i = 0; i < data.length - 1; i++) {
    const s = data[i];
    const next = data[i + 1];
    const x1 = (s.t / durationMin) * width;
    const x2 = (next.t / durationMin) * width;
    const yi = STAGES.indexOf(s.stage);
    segments.push({ x1, x2, y: yi * rowH, stage: s.stage, h: rowH });
  }

  return (
    <div style={{ width: '100%' }}>
      <svg viewBox={`0 0 ${width} ${height + 20}`} preserveAspectRatio="none" style={{ width: '100%', height: height + 20 }}>
        {/* Row guides */}
        {!compact && STAGES.map((s, i) => (
          <line key={s} x1={0} x2={width} y1={i * rowH + rowH / 2} y2={i * rowH + rowH / 2}
            stroke="var(--line)" strokeDasharray="2 4" strokeWidth={1} />
        ))}
        {/* Bars */}
        {segments.map((seg, i) => (
          <rect key={i}
            x={seg.x1} y={seg.y + 2}
            width={Math.max(1, seg.x2 - seg.x1)} height={seg.h - 4}
            fill={`var(--stage-${seg.stage})`}
            rx={2}
          />
        ))}
        {/* Row labels */}
        {!compact && STAGES.map((s, i) => (
          <text key={s} x={-8} y={i * rowH + rowH / 2 + 3}
            fontSize="11" fill="var(--ink-3)" textAnchor="end"
            style={{ fontFamily: 'var(--font-sans)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>
            {s}
          </text>
        ))}
        {/* Time labels */}
        {!compact && (
          <>
            <text x={0} y={height + 14} fontSize="10" fill="var(--ink-3)">{onsetLabel}</text>
            <text x={width} y={height + 14} fontSize="10" fill="var(--ink-3)" textAnchor="end">{wakeLabel}</text>
          </>
        )}
      </svg>
    </div>
  );
}

// ---------- Sparkline ----------
function Sparkline({ data, height = 40, color = 'currentColor', fill = false, strokeWidth = 1.5 }) {
  if (!data || data.length === 0) return null;
  const w = 200;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const step = w / (data.length - 1);
  const points = data.map((v, i) => [i * step, 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 areaPath = path + ` L${w} ${height} L0 ${height} Z`;
  return (
    <svg viewBox={`0 0 ${w} ${height}`} preserveAspectRatio="none" style={{ width: '100%', height }}>
      {fill && <path d={areaPath} fill={color} opacity={0.12} />}
      <path d={path} stroke={color} strokeWidth={strokeWidth} fill="none" strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}

// ---------- Circular score ----------
function ScoreRing({ score, size = 160, label = 'Sleep Score' }) {
  const r = size / 2 - 8;
  const c = 2 * Math.PI * r;
  const pct = Math.max(0, Math.min(100, score)) / 100;
  const quality = score >= 85 ? 'Excellent' : score >= 75 ? 'Good' : score >= 65 ? 'Fair' : 'Poor';
  return (
    <div style={{ position: 'relative', width: size, height: size }}>
      <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
        <circle cx={size/2} cy={size/2} r={r} stroke="var(--line)" strokeWidth={8} fill="none" />
        <circle cx={size/2} cy={size/2} r={r} stroke="var(--accent)" strokeWidth={8} fill="none"
          strokeDasharray={`${c * pct} ${c}`} strokeLinecap="round" />
      </svg>
      <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
        <div className="num" style={{ fontSize: size * 0.32, fontWeight: 600, letterSpacing: '-0.04em', lineHeight: 1 }}>{score}</div>
        <div className="text-xs text-ink-3" style={{ marginTop: 4, textTransform: 'uppercase', letterSpacing: '0.1em' }}>{quality}</div>
      </div>
    </div>
  );
}

// ---------- Metric cell ----------
function Metric({ label, value, unit, sub, delta, trend }) {
  return (
    <div className="col gap-4">
      <div className="text-xs text-ink-3" style={{ textTransform: 'uppercase', letterSpacing: '0.1em', fontWeight: 600 }}>{label}</div>
      <div className="row items-baseline gap-4">
        <span className="num" style={{ fontSize: 28, fontWeight: 600, letterSpacing: '-0.02em' }}>{value}</span>
        {unit && <span className="text-sm text-ink-3">{unit}</span>}
      </div>
      {(sub || delta) && (
        <div className="row items-center gap-8 text-xs text-ink-3">
          {delta && <span style={{ color: delta.startsWith('+') ? 'oklch(0.55 0.15 145)' : 'oklch(0.55 0.18 25)' }}>{delta}</span>}
          {sub && <span>{sub}</span>}
        </div>
      )}
    </div>
  );
}

// ---------- Format helpers ----------
function fmtDur(min) {
  const h = Math.floor(min / 60);
  const m = min % 60;
  return `${h}h ${m}m`;
}
function fmtTime(s) { return s; }

// Export to window for cross-script use
Object.assign(window, { Icon, Hypnogram, Sparkline, ScoreRing, Metric, fmtDur, fmtTime });
