/* global React */
const { useState, useEffect, useRef, useMemo, createContext, useContext } = React;

// ===== Shared context =====
const AppCtx = createContext(null);
const useApp = () => useContext(AppCtx);

// ===== Utility atoms =====
const Rule = ({ v, w, style }) => (
  <div style={{
    width: w || '100%',
    height: v ? (style || 1) : 0,
    borderTop: v ? `${style || 1}px solid var(--rule)` : 'none',
    borderLeft: !v ? `${style || 1}px solid var(--rule)` : 'none',
    flex: !v && !w ? 1 : 'none',
    alignSelf: 'stretch',
  }} />
);

const Dot = ({ c, size = 6 }) => (
  <span style={{
    display: 'inline-block',
    width: size, height: size, borderRadius: '50%',
    background: c || 'var(--ink)',
    verticalAlign: 'middle',
  }} />
);

// Small caps label
const Label = ({ children, color, spacing }) => (
  <span style={{
    fontFamily: 'var(--mono)',
    fontSize: 10,
    letterSpacing: spacing || '0.22em',
    textTransform: 'uppercase',
    color: color || 'var(--ink-mute)',
    fontWeight: 400,
  }}>{children}</span>
);

// Decorative diamond
const Diamond = ({ size = 4, c }) => (
  <span style={{
    display: 'inline-block',
    width: size, height: size,
    background: c || 'var(--ink-mute)',
    transform: 'rotate(45deg)',
    verticalAlign: 'middle',
  }} />
);

// Ornamental divider with center diamond
const Ornament = ({ label }) => (
  <div style={{
    display: 'flex', alignItems: 'center', gap: 14,
    color: 'var(--ink-mute)',
    margin: '4px 0',
  }}>
    <div style={{ flex: 1, borderTop: '1px solid var(--rule)' }} />
    <Diamond size={5} c="var(--accent)" />
    {label && <span style={{
      fontFamily: 'var(--serif-en)',
      fontStyle: 'italic', fontSize: 13,
      color: 'var(--ink-soft)',
      letterSpacing: '0.04em',
    }}>{label}</span>}
    <Diamond size={5} c="var(--accent)" />
    <div style={{ flex: 1, borderTop: '1px solid var(--rule)' }} />
  </div>
);

// Placeholder portrait — subtle striped SVG
const Portrait = ({ seed = 'a', tint = 'var(--her)', label }) => {
  const pattern = `portrait-${seed}`;
  return (
    <svg viewBox="0 0 100 120" style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <pattern id={pattern} width="3" height="3" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
          <line x1="0" y1="0" x2="0" y2="3" stroke={tint} strokeWidth="0.4" opacity="0.35" />
        </pattern>
        <linearGradient id={`g-${seed}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={tint} stopOpacity="0.25" />
          <stop offset="100%" stopColor={tint} stopOpacity="0.05" />
        </linearGradient>
      </defs>
      <rect width="100" height="120" fill={`url(#g-${seed})`} />
      <rect width="100" height="120" fill={`url(#${pattern})`} />
      {/* shoulders */}
      <path d={seed === 'her'
        ? 'M 10 120 Q 20 85, 50 82 Q 80 85, 90 120 Z'
        : 'M 8 120 Q 18 82, 50 80 Q 82 82, 92 120 Z'}
        fill={tint} opacity="0.18" stroke={tint} strokeOpacity="0.4" strokeWidth="0.5" />
      {/* head */}
      <circle cx="50" cy={seed === 'her' ? 48 : 46} r={seed === 'her' ? 22 : 23}
        fill={tint} opacity="0.15" stroke={tint} strokeOpacity="0.5" strokeWidth="0.6" />
      {/* crosshair */}
      <line x1="50" y1="20" x2="50" y2="24" stroke={tint} strokeOpacity="0.6" strokeWidth="0.4" />
      <line x1="20" y1="60" x2="24" y2="60" stroke={tint} strokeOpacity="0.6" strokeWidth="0.4" />
      <line x1="76" y1="60" x2="80" y2="60" stroke={tint} strokeOpacity="0.6" strokeWidth="0.4" />
      <text x="50" y="115" textAnchor="middle" fill={tint} fontSize="4"
        fontFamily="var(--mono)" opacity="0.5" letterSpacing="0.5">{label}</text>
    </svg>
  );
};

// Pulse waveform — ECG-style for her, token-bar for him
const Waveform = ({ kind = 'ecg', color, bpm = 72, animate = true }) => {
  const canvasRef = useRef(null);
  const rafRef = useRef(0);
  useEffect(() => {
    const c = canvasRef.current;
    if (!c) return;
    const ctx = c.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    const w = c.clientWidth, h = c.clientHeight;
    c.width = w * dpr; c.height = h * dpr;
    ctx.scale(dpr, dpr);
    let offset = 0;
    const speed = animate ? (bpm / 60) * 0.9 : 0;

    const ecgShape = (x) => {
      // x in [0, 1)
      const t = x % 1;
      if (t < 0.3) return Math.sin(t * Math.PI / 0.3) * 0.04;
      if (t < 0.36) return 0;
      if (t < 0.40) return -0.12;            // Q
      if (t < 0.44) return 0.85;             // R (spike up)
      if (t < 0.48) return -0.35;            // S
      if (t < 0.55) return 0;
      if (t < 0.75) return Math.sin((t - 0.55) * Math.PI / 0.2) * 0.18; // T
      return 0;
    };

    const render = () => {
      ctx.clearRect(0, 0, w, h);
      ctx.strokeStyle = color;
      ctx.lineWidth = 1.2;
      ctx.beginPath();
      const mid = h / 2;
      const cycles = 2.4;
      for (let px = 0; px < w; px++) {
        const u = (px / w) * cycles + offset;
        let y;
        if (kind === 'ecg') {
          y = mid - ecgShape(u) * h * 0.45;
        } else {
          // token stream — quantized bars
          const seed = Math.floor(u * 14);
          const jitter = Math.sin(seed * 12.9898) * 43758.5453;
          const v = (jitter - Math.floor(jitter));
          const local = (u * 14) % 1;
          const envelope = Math.sin(local * Math.PI);
          y = mid - (v * 0.6 + 0.05) * envelope * h * 0.4;
        }
        if (px === 0) ctx.moveTo(px, y); else ctx.lineTo(px, y);
      }
      ctx.stroke();

      // faint baseline
      ctx.strokeStyle = color;
      ctx.globalAlpha = 0.15;
      ctx.beginPath();
      ctx.moveTo(0, mid); ctx.lineTo(w, mid);
      ctx.stroke();
      ctx.globalAlpha = 1;

      offset += 0.004 * speed;
      rafRef.current = requestAnimationFrame(render);
    };
    render();
    return () => cancelAnimationFrame(rafRef.current);
  }, [color, bpm, kind, animate]);
  return <canvas ref={canvasRef} style={{ width: '100%', height: '100%', display: 'block' }} />;
};

// Live-ticking number
const TickerNum = ({ value, min = -1, max = 1, decimals = 0, interval = 1200, suffix = '' }) => {
  const [v, setV] = useState(value);
  useEffect(() => {
    const id = setInterval(() => {
      const delta = (Math.random() - 0.5) * 2;
      const range = Math.max(1, Math.abs(value) * 0.02);
      setV(Math.max(value + min, Math.min(value + max, value + delta * range)));
    }, interval);
    return () => clearInterval(id);
  }, [value, interval, min, max]);
  const shown = decimals > 0 ? v.toFixed(decimals) : Math.round(v);
  return <span>{shown}{suffix}</span>;
};

Object.assign(window, {
  AppCtx, useApp,
  Rule, Dot, Label, Diamond, Ornament,
  Portrait, Waveform, TickerNum,
});
