// Shared UI primitives

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

// ─── Icons ─────────────────────────────────────────────
const Icon = {
  Play: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="currentColor"><path d="M4 2l10 6-10 6V2z"/></svg>,
  Pause: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="currentColor"><rect x="3" y="2" width="4" height="12" rx="1"/><rect x="9" y="2" width="4" height="12" rx="1"/></svg>,
  Home: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.65" strokeLinecap="round" strokeLinejoin="round"><path d="M2.5 7.4 8 2.8l5.5 4.6"/><path d="M4.2 6.5v6.1h7.6V6.5"/><path d="M6.6 12.6V9.4h2.8v3.2"/></svg>,
  Player: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.65" strokeLinecap="round" strokeLinejoin="round"><rect x="2.4" y="3" width="11.2" height="10" rx="1.8"/><path d="M6.7 6.1v3.8L10 8 6.7 6.1z" fill="currentColor" stroke="none"/><path d="M4.5 11h7"/></svg>,
  Music: ({size=18}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>,
  Upload: ({size=24}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>,
  Wand: ({size=16}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M15 4V2M15 16v-2M8 9h2M20 9h2M17.8 11.8L19 13M15 9h0M17.8 6.2L19 5M3 21l9-9M12.2 6.2L11 5"/></svg>,
  Sparkle: ({size=14}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l2.5 7.5L22 12l-7.5 2.5L12 22l-2.5-7.5L2 12l7.5-2.5L12 2z"/></svg>,
  Download: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M8 2v9M4 7l4 4 4-4M2 13h12"/></svg>,
  Plus: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M8 3v10M3 8h10"/></svg>,
  Expand: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M6 2.5H2.5V6M10 2.5h3.5V6M6 13.5H2.5V10M10 13.5h3.5V10"/></svg>,
  ChevronUp: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3.5 10 8 5.5 12.5 10"/></svg>,
  ChevronDown: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="m3.5 6 4.5 4.5L12.5 6"/></svg>,
  ArrowRight: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg>,
  ArrowLeft: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M13 8H3M7 4l-4 4 4 4"/></svg>,
  Check: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8l3 3 7-7"/></svg>,
  Loader: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M8 2.2a5.8 5.8 0 105.3 3.5" opacity="0.35"/><path d="M13.3 5.7A5.8 5.8 0 008 2.2"/></svg>,
  Album: ({size=16}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="3"/></svg>,
  Film: ({size=16}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 3v18M17 3v18M3 12h18M3 7h4M17 7h4M3 17h4M17 17h4"/></svg>,
  Refresh: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M14 8a6 6 0 11-2-4.47M14 3v3h-3"/></svg>,
  X: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M3 3l10 10M13 3L3 13"/></svg>,
  Trash: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M2.5 4h11M6.5 2.5h3L10.5 4h-5l1-1.5zM5 6v7M8 6v7M11 6v7M4 4l.5 10h7L12 4"/></svg>,
  Cog: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="8" cy="8" r="2.2"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2M3 3l1.4 1.4M11.6 11.6L13 13M3 13l1.4-1.4M11.6 4.4L13 3"/></svg>,
  Edit: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M11 3l2 2-7 7H4v-2z"/></svg>,
  GripVertical: ({size=14}) => <svg width={size} height={size} viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="5.5" cy="3.5" r="1.1"/><circle cx="10.5" cy="3.5" r="1.1"/><circle cx="5.5" cy="8" r="1.1"/><circle cx="10.5" cy="8" r="1.1"/><circle cx="5.5" cy="12.5" r="1.1"/><circle cx="10.5" cy="12.5" r="1.1"/></svg>,
};

// ─── Waveform ─────────────────────────────────────────
function Waveform({ seed = 7, bars = 64, progress = 0, playing = false, level = 0, height = 44, samples = null }) {
  const fallbackHeights = useMemo(() => {
    const arr = [];
    let x = seed;
    for (let i = 0; i < bars; i++) {
      x = (x * 9301 + 49297) % 233280;
      const base = Math.sin(i * 0.32 + seed) * 0.38 + 0.52;
      const noise = (x / 233280) * 0.42;
      const env = Math.min(1, i / 10) * Math.min(1, (bars - i) / 10);
      arr.push(Math.max(0.08, (base + noise * 0.28) * env));
    }
    return arr;
  }, [seed, bars]);

  const baseHeights = useMemo(() => {
    if (Array.isArray(samples) && samples.length > 0) {
      return samples.map((value) => Math.max(0.04, Math.min(1, Number(value) || 0)));
    }
    return fallbackHeights;
  }, [fallbackHeights, samples]);

  const liveHeights = useMemo(() => {
    return baseHeights.map((value, index) => {
      if (!playing) return value;
      const shimmer = (Math.sin(index * 0.7 + seed * 0.45) + 1) / 2;
      const pulse = Math.max(0, level) * (0.75 + shimmer * 1.35);
      return Math.min(1, value * (0.72 + pulse * 1.8));
    });
  }, [baseHeights, level, playing, seed]);

  const waveformAreaPath = useMemo(() => {
    const values = liveHeights;
    if (!values.length) return "";

    const centerY = 50;
    const amplitude = 34;
    const pointsTop = values.map((value, index) => ({
      x: values.length === 1 ? 50 : (index / (values.length - 1)) * 100,
      y: centerY - value * amplitude,
    }));
    const pointsBottom = [...pointsTop].reverse().map((point, index) => ({
      x: point.x,
      y: centerY + values[values.length - 1 - index] * amplitude,
    }));

    const toSmoothPath = (points) => {
      if (!points.length) return "";
      if (points.length === 1) return `M ${points[0].x} ${points[0].y}`;
      let path = `M ${points[0].x} ${points[0].y}`;
      for (let index = 1; index < points.length; index += 1) {
        const prev = points[index - 1];
        const current = points[index];
        const midX = (prev.x + current.x) / 2;
        const midY = (prev.y + current.y) / 2;
        path += ` Q ${prev.x} ${prev.y} ${midX} ${midY}`;
      }
      const last = points[points.length - 1];
      path += ` T ${last.x} ${last.y}`;
      return path;
    };

    return `${toSmoothPath(pointsTop)} L ${pointsBottom[0].x} ${pointsBottom[0].y} ${toSmoothPath(pointsBottom).slice(1)} Z`;
  }, [liveHeights]);

  const waveformLinePath = useMemo(() => {
    const values = liveHeights;
    if (!values.length) return "";

    const points = values.map((value, index) => ({
      x: values.length === 1 ? 50 : (index / (values.length - 1)) * 100,
      y: 50 - value * 26,
    }));

    if (points.length === 1) return `M ${points[0].x} ${points[0].y}`;

    let path = `M ${points[0].x} ${points[0].y}`;
    for (let index = 1; index < points.length; index += 1) {
      const prev = points[index - 1];
      const current = points[index];
      const midX = (prev.x + current.x) / 2;
      const midY = (prev.y + current.y) / 2;
      path += ` Q ${prev.x} ${prev.y} ${midX} ${midY}`;
    }
    const last = points[points.length - 1];
    path += ` T ${last.x} ${last.y}`;
    return path;
  }, [liveHeights]);

  return (
    <div className={`waveform ${playing ? "live" : ""}`} style={{ height }} data-progress={progress}>
      <svg className="waveform-svg" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
        <path className="waveform-area" d={waveformAreaPath} />
        <path className="waveform-line" d={waveformLinePath} />
      </svg>
    </div>
  );
}

function Spectrum({ seed = 7, bars = 40, bins = null, playing = false, height = 44 }) {
  const fallbackBins = useMemo(() => {
    const values = [];
    let x = seed;
    for (let index = 0; index < bars; index += 1) {
      x = (x * 9301 + 49297) % 233280;
      const base = (Math.sin(index * 0.34 + seed) + 1) / 2;
      const noise = x / 233280;
      const envelope = Math.sin((index / Math.max(1, bars - 1)) * Math.PI);
      values.push(Math.max(0.04, (0.2 + base * 0.32 + noise * 0.12) * envelope));
    }
    return values;
  }, [bars, seed]);

  const values = Array.isArray(bins) && bins.length
    ? bins.map((value) => Math.max(0.02, Math.min(1, Number(value) || 0)))
    : fallbackBins;

  return (
    <div className={`spectrum ${playing ? "live" : ""}`} style={{ height }}>
      {values.map((value, index) => (
        <div
          key={index}
          className="spectrum-bar"
          style={{ height: `${Math.max(8, value * 100)}%` }}
        />
      ))}
    </div>
  );
}

// ─── Toast ─────────────────────────────────────────────
let _toastId = 0;
const _toastListeners = new Set();
function toast(msg) {
  const id = _toastId++;
  _toastListeners.forEach(fn => fn({ id, msg }));
}
function ToastHost() {
  const [toasts, setToasts] = useState([]);
  useEffect(() => {
    const fn = (t) => {
      setToasts(prev => [...prev, t]);
      setTimeout(() => setToasts(prev => prev.filter(x => x.id !== t.id)), 2600);
    };
    _toastListeners.add(fn);
    return () => _toastListeners.delete(fn);
  }, []);
  return toasts.map(t => <div key={t.id} className="toast">{t.msg}</div>);
}

// ─── Modal ─────────────────────────────────────────────
function Modal({ title, body, onCancel, onConfirm, confirmLabel = "확인", children }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onCancel?.(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onCancel]);
  return (
    <div className="modal-overlay" onClick={onCancel}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <h3>{title}</h3>
        {body && <p>{body}</p>}
        {children}
        <div className="modal-actions">
          <button className="pill-btn" onClick={onCancel}>취소</button>
          <button className="pill-btn primary" onClick={onConfirm}>{confirmLabel}</button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { Icon, Waveform, Spectrum, toast, ToastHost, Modal, useState, useEffect, useRef, useMemo, useCallback });
