// ============ HELPERS / ICONS ============
const { useState, useEffect, useRef, useCallback, useMemo } = React;

const Icon = ({ name, size = 16 }) => {
  const s = size;
  const common = { width: s, height: s, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (name) {
    case 'play': return <svg {...common}><polygon points="6 3 20 12 6 21 6 3" fill="currentColor"/></svg>;
    case 'pause': return <svg {...common}><rect x="6" y="4" width="4" height="16" fill="currentColor" stroke="none"/><rect x="14" y="4" width="4" height="16" fill="currentColor" stroke="none"/></svg>;
    case 'cue': return <svg {...common}><circle cx="12" cy="12" r="3" fill="currentColor" stroke="none"/><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/></svg>;
    case 'sync': return <svg {...common}><path d="M21 12a9 9 0 0 1-9 9 9 9 0 0 1-6.36-2.64"/><path d="M3 12a9 9 0 0 1 9-9 9 9 0 0 1 6.36 2.64"/><polyline points="21 3 21 9 15 9"/><polyline points="3 21 3 15 9 15"/></svg>;
    case 'load': return <svg {...common}><path d="M12 3v14"/><polyline points="6 11 12 17 18 11"/><line x1="4" y1="21" x2="20" y2="21"/></svg>;
    case 'youtube': return <svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor"><path d="M23 7.5s-.2-1.5-.9-2.2c-.8-.9-1.8-.9-2.2-1C17 4 12 4 12 4s-5 0-7.9.3c-.4 0-1.4.1-2.2 1C1.2 6 1 7.5 1 7.5S.8 9.3.8 11v1.5c0 1.7.2 3.5.2 3.5s.2 1.5.9 2.2c.8.9 1.9.9 2.4 1C6 19.5 12 19.6 12 19.6s5 0 7.9-.3c.4 0 1.4-.1 2.2-1 .7-.7.9-2.2.9-2.2s.2-1.8.2-3.5v-1.5C23.2 9.3 23 7.5 23 7.5z"/><polygon points="9.7 14.7 15.4 11.5 9.7 8.3" fill="#0a0a0a"/></svg>;
    case 'grip': return <svg {...common}><circle cx="9" cy="6" r="1" fill="currentColor"/><circle cx="9" cy="12" r="1" fill="currentColor"/><circle cx="9" cy="18" r="1" fill="currentColor"/><circle cx="15" cy="6" r="1" fill="currentColor"/><circle cx="15" cy="12" r="1" fill="currentColor"/><circle cx="15" cy="18" r="1" fill="currentColor"/></svg>;
    case 'upload': return <svg {...common}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>;
    case 'mic': return <svg {...common}><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/></svg>;
    case 'headphone': return <svg {...common}><path d="M3 18v-6a9 9 0 0 1 18 0v6"/><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"/></svg>;
    case 'settings': return <svg {...common}><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 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 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 0 1-2.83 0 2 2 0 0 1 0-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 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33h0a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h0a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v0a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>;
    case 'chevron-down': return <svg {...common}><polyline points="6 9 12 15 18 9"/></svg>;
    case 'chevron-up': return <svg {...common}><polyline points="18 15 12 9 6 15"/></svg>;
    case 'music': return <svg {...common}><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>;
    case 'x': return <svg {...common}><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>;
    case 'sparkles': return <svg {...common}><path d="M12 3l1.9 5.7 5.7 1.9-5.7 1.9L12 18l-1.9-5.5-5.7-1.9 5.7-1.9z" fill="currentColor"/></svg>;
    case 'list': return <svg {...common}><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>;
    default: return null;
  }
};

// ============ KNOB ============
function Knob({ value, onChange, min = 0, max = 1, center = 0.5, label, displayValue, size = 56, className = "", colorHue = 295 }) {
  const ref = useRef(null);
  const dragStart = useRef(null);

  const angle = ((value - min) / (max - min)) * 270 - 135;
  const centerAngle = ((center - min) / (max - min)) * 270 - 135;

  const handlePointerDown = (e) => {
    e.preventDefault();
    dragStart.current = { y: e.clientY, v: value };
    const move = (ev) => {
      if (!dragStart.current) return;
      const dy = dragStart.current.y - ev.clientY;
      const range = max - min;
      let next = dragStart.current.v + (dy / 140) * range;
      next = Math.max(min, Math.min(max, next));
      onChange(next);
    };
    const up = () => {
      dragStart.current = null;
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };

  const handleDoubleClick = () => onChange(center);

  const active = Math.abs(value - center) > (max - min) * 0.02;

  // Arc path
  const r = size / 2 + 2;
  const cx = r;
  const cy = r;
  const a1 = centerAngle * Math.PI / 180;
  const a2 = angle * Math.PI / 180;
  const startA = Math.min(a1, a2);
  const endA = Math.max(a1, a2);
  const p1 = { x: cx + r * Math.sin(startA), y: cy - r * Math.cos(startA) };
  const p2 = { x: cx + r * Math.sin(endA), y: cy - r * Math.cos(endA) };
  const largeArc = (endA - startA) > Math.PI ? 1 : 0;
  const arcD = `M ${p1.x} ${p1.y} A ${r} ${r} 0 ${largeArc} 1 ${p2.x} ${p2.y}`;

  return (
    <div className="knob-wrap">
      <div
        ref={ref}
        className={`knob ${className} ${active ? 'active' : ''}`}
        style={{ width: size, height: size }}
        onPointerDown={handlePointerDown}
        onDoubleClick={handleDoubleClick}
        title="Drag to adjust, double-click to reset"
      >
        <svg className="knob-arc" width={size + 4} height={size + 4} viewBox={`0 0 ${size + 4} ${size + 4}`}>
          <circle cx={r} cy={r} r={r} fill="none" stroke="oklch(1 0 0 / 0.05)" strokeWidth="2" strokeDasharray={`${2 * Math.PI * r * 270 / 360} ${2 * Math.PI * r}`} transform={`rotate(-225 ${r} ${r})`} />
          <path d={arcD} fill="none" stroke={`oklch(0.78 0.16 ${colorHue})`} strokeWidth="2.5" strokeLinecap="round" style={{ filter: `drop-shadow(0 0 4px oklch(0.78 0.16 ${colorHue} / 0.8))` }} />
        </svg>
        <div className="knob-indicator" style={{ transform: `translateX(-50%) rotate(${angle}deg)` }} />
      </div>
      <div className="knob-label">{label}</div>
      <div className="knob-value">{displayValue}</div>
    </div>
  );
}

// ============ WAVEFORM (Rekordbox-style) ============
// Two views stacked:
//   1. Overview — static thumbnail of the whole track (scrubbable)
//   2. Phase    — zoomed, scrolling live view with centered playhead + beatgrid + freq-band color
function Waveform({ trackId, progress, onScrub, cue, loop, seed = 1, empty, time, duration, bpm, firstBeat }) {
  // Deterministic multi-band data
  const bands = useMemo(() => {
    if (!seed) return null;
    const N = 320; // resolution of the overview
    const rngL = mulberry32(seed * 9301);
    const rngM = mulberry32(seed * 7919 + 13);
    const rngH = mulberry32(seed * 2477 + 91);
    const low = [], mid = [], high = [];
    for (let i = 0; i < N; i++) {
      const t = i / N;
      // intro/build/drop/outro envelope
      const envMain = 0.35 + 0.65 * Math.sin(t * Math.PI) * Math.sin(t * Math.PI * 1.3);
      const drop = Math.max(0, 1 - Math.abs(t - 0.45) * 8) * 0.35; // first drop around 45%
      const drop2 = Math.max(0, 1 - Math.abs(t - 0.78) * 10) * 0.3; // second drop
      const env = Math.min(1, envMain + drop + drop2);
      // kick/bass — dominant in drops
      low.push(Math.max(0.08, env * (0.4 + rngL() * 0.6) * (0.7 + drop + drop2)));
      // mids — vocals/synths, more even
      mid.push(Math.max(0.06, env * (0.35 + rngM() * 0.55)));
      // highs — hats/cymbals, more uniform but spiky
      high.push(Math.max(0.04, env * (0.25 + rngH() * 0.6)));
    }
    return { low, mid, high };
  }, [seed]);

  const ref = useRef(null);
  const phaseRef = useRef(null);

  const handleScrub = (e) => {
    if (empty) return;
    const rect = ref.current.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width;
    onScrub(Math.max(0, Math.min(1, x)));
  };

  const handlePointerDown = (e) => {
    if (empty) return;
    handleScrub(e);
    const move = (ev) => handleScrub(ev);
    const up = () => {
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };

  // ----- Phase waveform: scrub by horizontal drag (1 sec per 40px) -----
  const handlePhasePointerDown = (e) => {
    if (empty || !duration) return;
    // Drag scrubs: dx px / pxPerSec -> seconds delta
    const pxPerSec = 40;
    const dur = typeof duration === 'string' ? null : duration; // fallback
    // We'll compute dur from the numeric duration prop if string ('mm:ss')
    let totalSec = 0;
    if (typeof duration === 'string' && duration.includes(':')) {
      const [m, s] = duration.split(':').map(Number);
      totalSec = (m || 0) * 60 + (s || 0);
    }
    if (!totalSec) return;
    const startX = e.clientX;
    const startProgress = progress;
    const move = (ev) => {
      const dx = ev.clientX - startX;
      const dSec = -dx / pxPerSec; // drag right -> rewind (like rekordbox)
      const next = Math.max(0, Math.min(1, startProgress + dSec / totalSec));
      onScrub(next);
    };
    const up = () => {
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };

  // ----- Phase view parameters -----
  // Show ~12 seconds of audio, 6 on each side of the playhead
  const PHASE_WINDOW_SEC = 12;
  let totalSec = 0;
  if (typeof duration === 'string' && duration.includes(':')) {
    const [m, s] = duration.split(':').map(Number);
    totalSec = (m || 0) * 60 + (s || 0);
  }
  const currentSec = progress * totalSec;
  const bpmVal = bpm || 120;
  const beatSec = 60 / bpmVal;

  return (
    <div className="waveform-stack">
      {/* ---------- PHASE (zoomed, scrolling) ---------- */}
      <div
        className="waveform-phase"
        ref={phaseRef}
        onPointerDown={handlePhasePointerDown}
      >
        {empty && <div className="waveform-empty">— load a track —</div>}
        {!empty && bands && totalSec > 0 && (
          <PhaseWaveform
            bands={bands}
            trackId={trackId}
            currentSec={currentSec}
            totalSec={totalSec}
            windowSec={PHASE_WINDOW_SEC}
            beatSec={beatSec}
            firstBeat={firstBeat || 0}
          />
        )}
        {/* Centered playhead */}
        {!empty && <div className="waveform-phase-playhead" />}
        {/* Time readout */}
        {!empty && <div className="waveform-time">{time} / {duration}</div>}
      </div>

      {/* ---------- OVERVIEW (whole track, scrubbable) ---------- */}
      <div className="waveform-wrap" ref={ref} onPointerDown={handlePointerDown}>
        <div className="waveform-bg" />
        {!empty && bands && (
          <svg className="waveform-svg" preserveAspectRatio="none" viewBox="0 0 320 44">
            <defs>
              {/* Played vs unplayed: we dim unplayed by opacity */}
            </defs>
            {bands.low.map((_, i) => {
              const lo = bands.low[i];
              const mi = bands.mid[i];
              const hi = bands.high[i];
              // stack bars from center: low downward + mid/high upward
              const centerY = 22;
              const maxH = 20;
              const loH = lo * maxH;
              const miH = mi * maxH * 0.8;
              const hiH = hi * maxH * 0.65;
              const played = (i / bands.low.length) <= progress;
              const op = played ? 1 : 0.32;
              return (
                <g key={i} opacity={op}>
                  {/* LOW: cyan, below center */}
                  <rect x={i} y={centerY} width={0.85} height={loH} fill="#1ee0ff" />
                  {/* MID: green, above center */}
                  <rect x={i} y={centerY - miH} width={0.85} height={miH} fill="#52ff7a" />
                  {/* HIGH: pink spikes, above mids */}
                  <rect x={i} y={centerY - miH - hiH} width={0.85} height={hiH} fill="#ff5abf" />
                </g>
              );
            })}
          </svg>
        )}
        {empty && <div className="waveform-empty">Drop a YouTube / SoundCloud link</div>}
        {!empty && (
          <>
            <div className="waveform-overlay" style={{ width: `${progress * 100}%` }} />
            <div className="waveform-playhead" style={{ left: `${progress * 100}%` }} />
            {cue != null && <div className="waveform-cue" style={{ left: `${cue * 100}%` }} />}
            {loop && <div className="waveform-loop" style={{ left: `${loop.start * 100}%`, width: `${(loop.end - loop.start) * 100}%` }} />}
          </>
        )}
      </div>
    </div>
  );
}

// Sub-component: the scrolling zoomed phase waveform (Rekordbox-style)
function PhaseWaveform({ bands, trackId, currentSec, totalSec, windowSec, beatSec, firstBeat = 0 }) {
  // Map each band sample to a time. bands has N entries spanning totalSec.
  const N = bands.low.length;
  const secPerSample = totalSec / N;
  // How many samples in the visible window?
  const sampleWindow = Math.ceil(windowSec / secPerSample);
  const startSample = Math.max(0, Math.floor((currentSec - windowSec / 2) / secPerSample));
  const endSample = Math.min(N, startSample + sampleWindow);

  // Fractional offset so the waveform scrolls smoothly between samples
  const subSample = ((currentSec - windowSec / 2) / secPerSample) - Math.floor((currentSec - windowSec / 2) / secPerSample);

  // SVG viewBox: 0..VB_W x 0..VB_H; each sample = VB_W / sampleWindow wide
  const VB_W = 600;
  const VB_H = 120;
  const pxPerSample = VB_W / sampleWindow;
  const centerY = VB_H / 2;
  const maxH = VB_H * 0.44;

  // Beatgrid ticks: anchored at firstBeat, spaced by beatSec.
  // beat index n is at time = firstBeat + n*beatSec; we render every beat in
  // the visible window and tag every 4th as a downbeat (bold yellow).
  const firstVisibleSec = currentSec - windowSec / 2;
  const lastVisibleSec = currentSec + windowSec / 2;
  const firstBeatIdx = Math.ceil((firstVisibleSec - firstBeat) / beatSec);
  const lastBeatIdx = Math.floor((lastVisibleSec - firstBeat) / beatSec);
  const ticks = [];
  for (let b = firstBeatIdx; b <= lastBeatIdx; b++) {
    const tSec = firstBeat + b * beatSec;
    const x = ((tSec - firstVisibleSec) / windowSec) * VB_W;
    // Downbeats: every 4th beat. Anchor "1" at the first beat after firstBeat
    // (b=0). Negative b values still mod correctly.
    ticks.push({ x, downbeat: ((b % 4) + 4) % 4 === 0, idx: b });
  }

  const bars = [];
  for (let i = startSample; i < endSample; i++) {
    const localIdx = i - startSample;
    const x = localIdx * pxPerSample - subSample * pxPerSample;
    const lo = bands.low[i] || 0;
    const mi = bands.mid[i] || 0;
    const hi = bands.high[i] || 0;
    const loH = lo * maxH;
    const miH = mi * maxH * 0.75;
    const hiH = hi * maxH * 0.55;
    bars.push({ x, loH, miH, hiH });
  }

  return (
    <svg className="waveform-phase-svg" preserveAspectRatio="none" viewBox={`0 0 ${VB_W} ${VB_H}`}>
      {/* Beat grid (behind bars) */}
      {ticks.map(t => (
        <line
          key={t.idx}
          x1={t.x} x2={t.x}
          y1={0} y2={VB_H}
          stroke={t.downbeat ? '#ffe04d' : '#ffffff'}
          strokeOpacity={t.downbeat ? 0.55 : 0.12}
          strokeWidth={t.downbeat ? 1.4 : 0.8}
          strokeDasharray={t.downbeat ? '' : '2 3'}
        />
      ))}
      {/* Centerline */}
      <line x1={0} x2={VB_W} y1={centerY} y2={centerY} stroke="#ffffff" strokeOpacity="0.08" strokeWidth="0.5" />
      {/* Bars */}
      {bars.map((b, i) => (
        <g key={i}>
          <rect x={b.x} y={centerY} width={pxPerSample * 0.9} height={b.loH} fill="#1ee0ff" />
          <rect x={b.x} y={centerY - b.miH} width={pxPerSample * 0.9} height={b.miH} fill="#52ff7a" />
          <rect x={b.x} y={centerY - b.miH - b.hiH} width={pxPerSample * 0.9} height={b.hiH} fill="#ff5abf" />
        </g>
      ))}
    </svg>
  );
}

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

// ============ FADER ============
function Fader({ value, onChange, orientation = 'vertical', className = '' }) {
  const ref = useRef(null);
  const dragStart = useRef(null);

  const handlePointerDown = (e) => {
    e.preventDefault();
    const rect = ref.current.getBoundingClientRect();
    if (orientation === 'vertical') {
      const y = (e.clientY - rect.top) / rect.height;
      onChange(Math.max(0, Math.min(1, 1 - y)));
    } else {
      const x = (e.clientX - rect.left) / rect.width;
      onChange(Math.max(0, Math.min(1, x)));
    }
    const move = (ev) => {
      if (orientation === 'vertical') {
        const y = (ev.clientY - rect.top) / rect.height;
        onChange(Math.max(0, Math.min(1, 1 - y)));
      } else {
        const x = (ev.clientX - rect.left) / rect.width;
        onChange(Math.max(0, Math.min(1, x)));
      }
    };
    const up = () => {
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };

  if (orientation === 'vertical') {
    return (
      <div ref={ref} className={`fader ${className}`} onPointerDown={handlePointerDown} style={{ '--fader-gap': `${(1 - value) * 100}%` }}>
        <div className="fader-track" />
        <div className="fader-fill" style={{ height: `${value * 100}%` }} />
        <div className="fader-ticks">
          {Array.from({ length: 11 }).map((_, i) => (
            <div key={i} className="fader-tick" style={{ top: `${i * 10}%`, width: i % 5 === 0 ? 7 : 4 }} />
          ))}
        </div>
        <div className="fader-handle" style={{ top: `${(1 - value) * 100}%` }} />
      </div>
    );
  }
  return (
    <div ref={ref} className={`crossfader`} onPointerDown={handlePointerDown}>
      <div className="crossfader-track" />
      <div className="crossfader-center" />
      <div className="crossfader-handle" style={{ left: `${value * 100}%` }} />
    </div>
  );
}

// ============ EXPORT TO WINDOW ============
Object.assign(window, { Icon, Knob, Waveform, Fader, mulberry32 });
