import { useState, useRef, useEffect, useCallback, useMemo } from "react";
import BlockChain from "./BlockChain.jsx";
import FootswitchBar from "./FootswitchBar.jsx";

// ── API Layer ──────────────────────────────────────────────────────
const API_BASE = "";

async function api(method, path, body) {
  const opts = { method, headers: { "Content-Type": "application/json" } };
  if (body) opts.body = JSON.stringify(body);
  const res = await fetch(`${API_BASE}${path}`, opts);
  if (!res.ok) throw new Error(`API ${method} ${path}: ${res.status}`);
  return res.json();
}

const API = {
  getState:      ()                              => api("GET",  "/api/state"),
  getVu:         ()                              => api("GET",  "/api/vu"),
  updateBlock:   (id, params)                    => api("PATCH", "/api/block-params", { id, ...params }),
  toggleBlock:   (id, enabled)                   => api("PATCH", "/api/blocks", { id, enabled }),
  bypassToggle:  ()                              => api("POST", "/api/bypass/toggle"),
  masterVolume:  (v)                             => api("POST", "/api/volume", { volume: v / 100 }),
  listModels:    ()                              => api("GET",  "/api/models"),
  loadModel:     (path)                          => api("POST", "/api/models/load", { path }),
  listPresets:   ()                              => api("GET",  "/api/presets"),
  loadPreset:    (bank, program)                 => api("GET",  "/api/presets", { bank, program }),
  savePreset:    (bank, program, name, tags)     => api("PUT",  "/api/presets", { bank, program, name, tags }),
  searchModels:  (q)                             => api("GET",  `/api/tonehub/search?q=${encodeURIComponent(q)}`),
  searchIrs:     (q)                             => api("GET",  `/api/tonehub/search/irs?q=${encodeURIComponent(q)}`),
  installModel:  (url, name)                     => api("POST", "/api/models/tonedownload/install", { download_url: url, name }),
  installIr:     (url, name)                     => api("POST", "/api/irs/tonedownload/install", { download_url: url, name }),
};

// ── Design tokens ──────────────────────────────────────────────
const T = {
  bg: "#0A0A0C",
  panel: "#141418",
  surface: "#1C1C22",
  border: "#2A2A32",
  amber: "#E8A030",
  amberDim: "#7A5218",
  blue: "#3A7BA8",
  blueDim: "#1E4060",
  green: "#3AB87A",
  red: "#C84040",
  textPrimary: "#F0EDE6",
  textSec: "#8888A0",
  textDim: "#444458",
};

// ── Block parameter definitions ─────────────────────────────────
const BLOCK_PARAMS = {
  overdrive:    [{ key:'drive', label:'Drive', min:0, max:100, def:30 }, { key:'tone', label:'Tone', min:0, max:100, def:50 }, { key:'level', label:'Level', min:0, max:100, def:70 }],
  distortion:   [{ key:'drive', label:'Drive', min:0, max:100, def:50 }, { key:'tone', label:'Tone', min:0, max:100, def:40 }, { key:'level', label:'Level', min:0, max:100, def:60 }],
  fuzz:         [{ key:'fuzz', label:'Fuzz', min:0, max:100, def:60 }, { key:'tone', label:'Tone', min:0, max:100, def:30 }, { key:'level', label:'Level', min:0, max:100, def:60 }],
  delay:        [{ key:'time', label:'Time', min:0, max:100, def:40 }, { key:'feedback', label:'Feedback', min:0, max:100, def:30 }, { key:'mix', label:'Mix', min:0, max:100, def:35 }],
  echo:         [{ key:'time', label:'Time', min:0, max:100, def:40 }, { key:'feedback', label:'Feedback', min:0, max:100, def:30 }, { key:'mix', label:'Mix', min:0, max:100, def:35 }],
  reverb:       [{ key:'decay', label:'Decay', min:0, max:100, def:50 }, { key:'mix', label:'Mix', min:0, max:100, def:30 }, { key:'tone', label:'Tone', min:0, max:100, def:50 }],
  chorus:       [{ key:'rate', label:'Rate', min:0, max:100, def:40 }, { key:'depth', label:'Depth', min:0, max:100, def:40 }, { key:'mix', label:'Mix', min:0, max:100, def:30 }],
  flanger:      [{ key:'rate', label:'Rate', min:0, max:100, def:30 }, { key:'depth', label:'Depth', min:0, max:100, def:50 }, { key:'feedback', label:'Feedback', min:0, max:100, def:40 }, { key:'mix', label:'Mix', min:0, max:100, def:30 }],
  phaser:       [{ key:'rate', label:'Rate', min:0, max:100, def:30 }, { key:'depth', label:'Depth', min:0, max:100, def:50 }, { key:'feedback', label:'Feedback', min:0, max:100, def:40 }, { key:'mix', label:'Mix', min:0, max:100, def:30 }],
  compressor:   [{ key:'threshold', label:'Thresh', min:0, max:100, def:50 }, { key:'ratio', label:'Ratio', min:1, max:20, def:4 }, { key:'gain', label:'Gain', min:0, max:100, def:50 }],
  gate:         [{ key:'threshold', label:'Thresh', min:0, max:100, def:30 }, { key:'release', label:'Release', min:0, max:100, def:50 }],
  eq:           [{ key:'low', label:'Low', min:-12, max:12, def:0 }, { key:'mid', label:'Mid', min:-12, max:12, def:0 }, { key:'high', label:'High', min:-12, max:12, def:0 }, { key:'level', label:'Level', min:0, max:100, def:70 }],
  cabinet:      [{ key:'level', label:'Level', min:0, max:100, def:75 }],
  ir:           [{ key:'level', label:'Level', min:0, max:100, def:75 }],
};

const BLOCK_COLORS = {
  overdrive: T.amber, distortion: T.red, fuzz: '#C07030',
  delay: T.blue, echo: T.blue, reverb: '#60B0D0',
  chorus: T.green, flanger: '#50C080', phaser: '#80C050',
  compressor: '#D09040', gate: T.textSec, eq: '#7080D0',
  cabinet: '#D07090', ir: '#D07090',
};

function getBlockParams(type) {
  return BLOCK_PARAMS[(type || '').toLowerCase()] || [{ key:'level', label:'Level', min:0, max:100, def:50 }];
}
function getBlockColor(type) {
  return BLOCK_COLORS[(type || '').toLowerCase()] || T.amber;
}

// ── CSS (Helix Stadium theme) ─────────────────────────────────
const css = `
  @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Inter:wght@300;400;500;600&display=swap');
  * { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; user-select: none; }
  body { background: ${T.bg}; color: ${T.textPrimary}; font-family: 'Inter', sans-serif; }

  :root { --amber: ${T.amber}; --blue: ${T.blue}; --green: ${T.green}; }
  .mono { font-family: 'JetBrains Mono', monospace; }

  ::-webkit-scrollbar { width: 4px; height: 4px; }
  ::-webkit-scrollbar-track { background: ${T.panel}; }
  ::-webkit-scrollbar-thumb { background: ${T.border}; border-radius: 2px; }

  /* ── Knob ── */
  .knob-wrap { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; }
  .knob { border-radius: 50%; position: relative; touch-action: none; }
  .knob-inner { border-radius: 50%; width: 100%; height: 100%; position: relative;
    background: radial-gradient(circle at 35% 30%, #3A3A48, #1A1A22);
    box-shadow: 0 2px 8px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.07); }
  .knob-dot { position: absolute; width: 3px; border-radius: 2px; background: var(--amber);
    left: 50%; transform-origin: bottom center; box-shadow: 0 0 6px var(--amber); }
  .knob-label { font-size: 10px; color: ${T.textSec}; letter-spacing: .06em; text-transform: uppercase; text-align: center; }
  .knob-value { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: ${T.amber}; }

  /* ── Toggle ── */
  .toggle { display: flex; background: ${T.surface}; border-radius: 6px; border: 1px solid ${T.border}; overflow: hidden; }
  .toggle-btn { flex: 1; padding: 7px 12px; font-size: 12px; font-weight: 500; letter-spacing: .04em;
    background: none; border: none; color: ${T.textSec}; cursor: pointer; transition: all .15s; }
  .toggle-btn.active { background: ${T.amber}; color: #000; }

  /* ── Buttons ── */
  .btn { padding: 10px 18px; border-radius: 7px; font-size: 13px; font-weight: 600;
    letter-spacing: .04em; border: none; cursor: pointer; transition: all .12s; }
  .btn-primary { background: ${T.amber}; color: #000; }
  .btn-primary:active { filter: brightness(.85); transform: scale(.97); }
  .btn-ghost { background: ${T.surface}; color: ${T.textPrimary}; border: 1px solid ${T.border}; }
  .btn-ghost:active { background: ${T.border}; }
  .btn-danger { background: rgba(200,64,64,.2); color: ${T.red}; border: 1px solid rgba(200,64,64,.3); }
  .btn-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center;
    justify-content: center; background: ${T.surface}; border: 1px solid ${T.border}; cursor: pointer;
    font-size: 16px; transition: all .12s; }
  .btn-icon:active { transform: scale(.93); }
  .btn-icon.active { background: rgba(232,160,48,.15); border-color: ${T.amber}; color: ${T.amber}; }

  /* ── Badge ── */
  .badge { display: inline-flex; align-items: center; justify-content: center;
    padding: 2px 7px; border-radius: 4px; font-size: 10px; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase; }
  .badge-amber { background: rgba(232,160,48,.2); color: ${T.amber}; }
  .badge-green { background: rgba(58,184,122,.2); color: ${T.green}; }
  .badge-blue { background: rgba(58,123,168,.2); color: ${T.blue}; }
  .badge-red { background: rgba(200,64,64,.2); color: ${T.red}; }

  /* ── Screen header (for overlay screens) ── */
  .screen-header { padding: 14px 16px 10px; display: flex; align-items: center;
    justify-content: space-between; border-bottom: 1px solid ${T.border}; }
  .screen-title { font-size: 13px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: ${T.textSec}; }
  .screen-body { flex: 1; overflow-y: auto; padding: 14px 12px; display: flex;
    flex-direction: column; gap: 14px; }

  /* ── Param slider ── */
  .param-panel { background: ${T.surface}; border-radius: 12px 12px 0 0; padding: 10px 14px 8px;
    flex-shrink: 0; overflow-y: auto; border-top: 1px solid ${T.border}; }
  .param-panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
  .param-panel-title { font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: ${T.textSec}; }
  .param-row { display: flex; flex-direction: column; gap: 2px; padding: 2px 0; }
  .param-header { display: flex; justify-content: space-between; align-items: center; }
  .param-label { font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: .06em; color: ${T.textSec}; }
  .param-val { font-family: 'JetBrains Mono',monospace; font-size: 12px; font-weight: 600; }
  .param-track-wrap { position: relative; height: 38px; display: flex; align-items: center; cursor: pointer; touch-action: none; }
  .param-track { width: 100%; height: 8px; background: ${T.border}; border-radius: 4px; position: relative; overflow: hidden; }
  .param-fill { height: 100%; border-radius: 4px; position: absolute; top: 0; left: 0; transition: width .04s; }
  .param-thumb { width: 26px; height: 26px; border-radius: 50%; position: absolute; top: 50%;
    transform: translate(-50%,-50%); box-shadow: 0 1px 6px rgba(0,0,0,.6); pointer-events: none;
    border: 3px solid ${T.bg}; }
  .param-btn { width: 32px; height: 32px; border-radius: 50%; border: 1px solid ${T.border};
    background: ${T.panel}; color: ${T.textPrimary}; font-size: 15px; font-weight: 700;
    display: flex; align-items: center; justify-content: center; cursor: pointer; line-height: 1; }
  .param-btn:active { background: ${T.border}; transform: scale(.92); }
  .param-knobs { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; padding: 8px 0; }

  /* ── Preset chip ── */
  .preset-chip { padding: 5px 8px; border-radius: 6px; background: ${T.surface}; border: 1px solid ${T.border};
    display: flex; flex-direction: column; align-items: center; cursor: pointer; transition: all .12s; flex: 1;
    min-width: 0; }
  .preset-chip.active { border-color: ${T.amber}; background: rgba(232,160,48,.08); }
  .preset-chip .pc-name { font-size: 10px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  .preset-chip .pc-num { font-family: 'JetBrains Mono', monospace; font-size: 8px; color: ${T.textDim}; margin-top: 1px; }

  /* ── Preset row (captures screen) ── */
  .preset-row { display: flex; align-items: center; gap: 12px; padding: 11px 14px;
    border-radius: 8px; cursor: pointer; transition: background .12s; border: 1px solid transparent; }
  .preset-row:active, .preset-row.active { background: rgba(232,160,48,.08); border-color: rgba(232,160,48,.25); }
  .section-label { font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
    color: ${T.textDim}; font-weight: 600; margin-bottom: 10px; }

  /* ── Card ── */
  .card { background: ${T.panel}; border: 1px solid ${T.border}; border-radius: 10px; padding: 16px; }
  .card-sm { background: ${T.surface}; border: 1px solid ${T.border}; border-radius: 8px; padding: 12px; }
`;

// ── Knob ──────────────────────────────────────────────────────
function Knob({ label, value = 50, onChange, size = 52, color = T.amber, min = 0, max = 100 }) {
  const startRef = useRef(null);
  const norm = (value - min) / (max - min);
  const angle = -140 + norm * 280;
  const dotH = size * 0.38;
  const dotTop = size * 0.5 - dotH;

  const handleStart = (e) => {
    e.preventDefault();
    const clientY = e.touches ? e.touches[0].clientY : e.clientY;
    startRef.current = { y: clientY, val: value };
    const move = (ev) => {
      const cy = ev.touches ? ev.touches[0].clientY : ev.clientY;
      const delta = (startRef.current.y - cy) / 120;
      const next = Math.max(min, Math.min(max, startRef.current.val + delta * (max - min)));
      onChange?.(Math.round(next));
    };
    const up = () => { window.removeEventListener("mousemove", move); window.removeEventListener("mouseup", up);
      window.removeEventListener("touchmove", move); window.removeEventListener("touchend", up); };
    window.addEventListener("mousemove", move); window.addEventListener("mouseup", up);
    window.addEventListener("touchmove", move, { passive: false });
    window.addEventListener("touchend", up);
  };

  const svgSize = size + 12;
  const cx = svgSize / 2, cy = svgSize / 2, r = size / 2 + 3;
  const lx = cx + r * Math.cos((-140 - 90) * (Math.PI / 180));
  const ly = cy + r * Math.sin((-140 - 90) * (Math.PI / 180));
  const ex = cx + r * Math.cos((angle - 90) * (Math.PI / 180));
  const ey = cy + r * Math.sin((angle - 90) * (Math.PI / 180));
  const large = norm * 280 > 180 ? 1 : 0;

  return (
    <div className="knob-wrap" style={{ width: size + 12 }}>
      <div className="knob" style={{ width: size, height: size }} onMouseDown={handleStart} onTouchStart={handleStart}>
        <svg style={{ position: "absolute", inset: -6, width: svgSize, height: svgSize, overflow: "visible" }}>
          <circle cx={cx} cy={cy} r={r} fill="none" stroke={T.border} strokeWidth="2.5" strokeLinecap="round" opacity=".5" />
          {norm > 0 && <path d={`M ${lx} ${ly} A ${r} ${r} 0 ${large} 1 ${ex} ${ey}`}
            fill="none" stroke={color} strokeWidth="2.5" strokeLinecap="round"
            style={{ filter: `drop-shadow(0 0 3px ${color})` }} />}
        </svg>
        <div className="knob-inner" style={{ "--amber": color }}>
          <div className="knob-dot" style={{
            height: dotH, top: dotTop, bottom: "auto",
            transform: `translateX(-50%) rotate(${angle}deg)`,
            transformOrigin: `50% ${dotH}px`,
          }} />
        </div>
      </div>
      <div className="knob-value mono" style={{ color }}>{value}</div>
      <div className="knob-label">{label}</div>
    </div>
  );
}

// ── Param Slider (big touch-friendly) ─────────────────────────
function ParamSlider({ label, value = 50, onChange, onChangeEnd, min = 0, max = 100, color = T.amber }) {
  const trackRef = useRef(null);
  const dragRef = useRef(false);
  const lastValRef = useRef(value);
  const onChangeRef = useRef(onChange);
  const onChangeEndRef = useRef(onChangeEnd);
  const pct = ((value - min) / (max - min)) * 100;

  useEffect(() => { onChangeRef.current = onChange; }, [onChange]);
  useEffect(() => { onChangeEndRef.current = onChangeEnd; }, [onChangeEnd]);

  const valFromEvent = useCallback((e) => {
    const t = trackRef.current;
    if (!t) return value;
    const r = t.getBoundingClientRect();
    const x = (e.touches ? e.touches[0].clientX : e.clientX) - r.left;
    return Math.round(min + Math.max(0, Math.min(1, x / r.width)) * (max - min));
  }, [min, max, value]);

  useEffect(() => {
    const move = (e) => {
      if (!dragRef.current) return;
      e.preventDefault();
      const v = valFromEvent(e);
      lastValRef.current = v;
      onChangeRef.current?.(v);
    };
    const up = () => {
      if (dragRef.current) {
        onChangeEndRef.current?.(lastValRef.current);
        dragRef.current = false;
      }
    };
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', up);
    window.addEventListener('touchmove', move, { passive: false });
    window.addEventListener('touchend', up);
    return () => {
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', up);
      window.removeEventListener('touchmove', move);
      window.removeEventListener('touchend', up);
    };
  }, [valFromEvent]);

  const handleStart = (e) => {
    e.preventDefault();
    const v = valFromEvent(e);
    lastValRef.current = v;
    dragRef.current = true;
    onChangeRef.current?.(v);
  };

  const quick = (delta) => (e) => {
    e.preventDefault();
    e.stopPropagation();
    const next = Math.max(min, Math.min(max, value + delta));
    onChangeRef.current?.(next);
    onChangeEndRef.current?.(next);
  };

  return (
    <div className="param-row">
      <div className="param-header">
        <span className="param-label" style={{ color }}>{label}</span>
        <div style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
          <button className="param-btn" onMouseDown={quick(-5)} onTouchStart={quick(-5)}>−</button>
          <span className="param-val" style={{ color }}>{value}</span>
          <button className="param-btn" onMouseDown={quick(5)} onTouchStart={quick(5)}>+</button>
        </div>
      </div>
      <div className="param-track-wrap" ref={trackRef}
        onMouseDown={handleStart} onTouchStart={handleStart}>
        <div className="param-track">
          <div className="param-fill" style={{ width: `${pct}%`, background: color }} />
          <div className="param-thumb" style={{ left: `${pct}%`, background: color }} />
        </div>
      </div>
    </div>
  );
}

// ── Parameter Panel ──────────────────────────────────────────
function ParameterPanel({ block, params, onParamChange, onParamChangeEnd, viewMode, onToggleView }) {
  const defs = getBlockParams(block.type);
  const color = getBlockColor(block.type);
  const blockName = block.name || (block.type || '').toUpperCase();

  return (
    <div className="param-panel" style={{ maxHeight: '50%', flexShrink: 0 }}>
      <div className="param-panel-header">
        <span className="param-panel-title" style={{ color: T.textSec }}>
          <span style={{ color }}>●</span> {blockName}
        </span>
        <button className="btn-icon" style={{ width: 28, height: 28, fontSize: 12, flexShrink: 0 }}
          onClick={onToggleView} title={viewMode === 'slider' ? 'Switch to knobs' : 'Switch to sliders'}>
          {viewMode === 'slider' ? '◉' : '▦'}
        </button>
      </div>
      {viewMode === 'slider' ? (
        defs.map(p => (
          <ParamSlider key={p.key}
            label={p.label}
            value={params[p.key] != null ? params[p.key] : p.def}
            min={p.min} max={p.max}
            color={color}
            onChange={v => onParamChange?.(p.key, v)}
            onChangeEnd={v => onParamChangeEnd?.(p.key, v)}
          />
        ))
      ) : (
        <div className="param-knobs">
          {defs.map(p => (
            <Knob key={p.key}
              label={p.label}
              value={params[p.key] != null ? params[p.key] : p.def}
              min={p.min} max={p.max}
              size={60}
              color={color}
              onChange={v => {
                onParamChange?.(p.key, v);
                onParamChangeEnd?.(p.key, v);
              }}
            />
          ))}
        </div>
      )}
    </div>
  );
}

// ── VU Meter ──────────────────────────────────────────────────
function VUMeter({ level = 0, height = 28, vertical = false }) {
  const segs = 12;
  const active = Math.round((level / 100) * segs);
  const segColor = (i) => {
    if (i >= 10) return T.red;
    if (i >= 8) return "#E8C030";
    return T.green;
  };
  if (vertical) return (
    <div style={{ display: "flex", flexDirection: "column-reverse", gap: 1, height, alignItems: "center" }}>
      {Array.from({ length: segs }).map((_, i) => (
        <div key={i} style={{ width: 4, height: (height - segs) / segs,
          borderRadius: 1, background: i < active ? segColor(i) : T.border,
          boxShadow: i < active ? `0 0 4px ${segColor(i)}` : "none",
          transition: "background .05s, box-shadow .05s" }} />
      ))}
    </div>
  );
  return (
    <div style={{ display: "flex", gap: 2, height: 6, alignItems: "center", width: height }}>
      {Array.from({ length: segs }).map((_, i) => (
        <div key={i} style={{ flex: 1, height: "100%", borderRadius: 1,
          background: i < active ? segColor(i) : T.border,
          transition: "background .05s" }} />
      ))}
    </div>
  );
}

// ── Scribble Info Bar ──────────────────────────────────────
function ScribbleBar({ state }) {
  const vol = state.master_volume != null ? Math.round(state.master_volume * 100) : 0;
  return (
    <div style={{
      display: "flex", gap: 4, padding: "4px 10px",
      background: T.surface, borderBottom: `1px solid ${T.border}`,
      flexShrink: 0,
    }}>
      {[
        { label: "Preset", value: state.current_preset?.name || "—" },
        { label: "Routing", value: (state.routing_mode || "MONO").toUpperCase() },
        { label: "Volume", value: `${vol}%` },
        { label: "CPU", value: state.cpu_percent != null ? `${state.cpu_percent}%` : "—" },
      ].map(s => (
        <div key={s.label} style={{ flex: 1, textAlign: "center", padding: "3px 6px",
          borderRadius: 4, background: T.panel, border: `1px solid ${T.border}` }}>
          <div style={{ fontSize: 8, color: T.textDim, letterSpacing: ".06em", textTransform: "uppercase" }}>{s.label}</div>
          <div className="mono" style={{ fontSize: 11, fontWeight: 600, color: T.amber, marginTop: 1 }}>{s.value}</div>
        </div>
      ))}
    </div>
  );
}

// ── Captures Screen (Model/IR downloads) ──────────────────────
function CapturesScreen({ onClose }) {
  const [tab, setTab] = useState("models");
  const [query, setQuery] = useState("");
  const [results, setResults] = useState(null);
  const [models, setModels] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    API.listModels().then(d => setModels(d.models || [])).catch(()=>{});
  }, []);

  const doSearch = async () => {
    if (!query.trim()) return;
    setLoading(true);
    try {
      const data = tab === "models"
        ? await API.searchModels(query)
        : await API.searchIrs(query);
      setResults(data.results || []);
    } catch { setResults([]); }
    setLoading(false);
  };

  const install = async (item) => {
    try {
      if (tab === "models") {
        await API.installModel(item.download_url, item.name);
      } else {
        await API.installIr(item.download_url, item.name);
      }
      API.listModels().then(d => setModels(d.models || [])).catch(()=>{});
    } catch(e) { alert("Install failed: " + e.message); }
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%", background: T.bg }}>
      <div className="screen-header">
        <div className="screen-title">Downloads</div>
        <button className="btn-icon" style={{ width: 30, height: 30, fontSize: 14 }}
          onClick={onClose}>✕</button>
      </div>
      <div style={{ padding: "8px 12px", borderBottom: `1px solid ${T.border}` }}>
        <div className="toggle">
          <button className={`toggle-btn ${tab === "models" ? "active" : ""}`} onClick={() => setTab("models")}>NAM Models</button>
          <button className={`toggle-btn ${tab === "irs" ? "active" : ""}`} onClick={() => setTab("irs")}>IR Cabs</button>
        </div>
      </div>
      <div style={{ padding: "6px 12px", display: "flex", gap: 6, borderBottom: `1px solid ${T.border}` }}>
        <div style={{ flex: 1, display: "flex", background: T.surface, borderRadius: 7, border: `1px solid ${T.border}`,
          padding: "7px 10px", gap: 8, alignItems: "center" }}>
          <span style={{ color: T.textDim, fontSize: 12 }}>🔍</span>
          <input placeholder={`Search ${tab === "models" ? "NAM models" : "IR cabs"}...`}
            value={query} onChange={e => setQuery(e.target.value)}
            onKeyDown={e => e.key === "Enter" && doSearch()}
            style={{ flex: 1, background: "none", border: "none", color: T.textPrimary, fontSize: 12, outline: "none" }} />
        </div>
        <button className="btn btn-primary" style={{ padding: "7px 12px", fontSize: 11 }} onClick={doSearch}>
          {loading ? "..." : "Search"}
        </button>
      </div>
      <div className="screen-body">
        {results === null && models.length > 0 && (
          <>
            <div className="section-label">Installed Models</div>
            {models.map((m, i) => (
              <div key={i} className="preset-row" style={{ cursor: "default" }}>
                <div style={{ width: 28, height: 28, borderRadius: 6, display: "flex",
                  alignItems: "center", justifyContent: "center", fontSize: 12,
                  background: m.loaded ? `${T.green}22` : T.surface,
                  color: m.loaded ? T.green : T.textDim }}>🎛</div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 12, fontWeight: 500 }}>{m.name}</div>
                  <div className="mono" style={{ fontSize: 10, color: T.textDim }}>
                    {m.architecture} · {m.size_mb}MB {m.loaded ? "· CURRENT" : ""}
                  </div>
                </div>
              </div>
            ))}
          </>
        )}
        {results !== null && results.length === 0 && (
          <div className="card-sm" style={{ textAlign: "center", color: T.textDim, padding: 24 }}>No results found</div>
        )}
        {results !== null && results.map((r, i) => (
          <div key={i} className="preset-row" style={{ cursor: "default" }}>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 12, fontWeight: 500 }}>{r.name}</div>
              <div className="mono" style={{ fontSize: 10, color: T.textDim }}>
                {r.author} · {r.size_display}
              </div>
            </div>
            <button className="btn btn-ghost" style={{ padding: "5px 10px", fontSize: 11 }}
              onClick={() => install(r)}>Get</button>
          </div>
        ))}
      </div>
    </div>
  );
}

// ── Presets Screen ─────────────────────────────────────────
function PresetsScreen({ state, onClose, onLoadPreset }) {
  const [activeIdx, setActiveIdx] = useState(0);
  const samplePresets = [
    { num: 1, name: "Plexi Crunch", tags: ["Crunch", "Vintage"], color: T.amber },
    { num: 2, name: "Modern High Gain", tags: ["High Gain", "Lead"], color: T.red },
    { num: 3, name: "Clean Chimey", tags: ["Clean", "Sparkle"], color: T.blue },
    { num: 4, name: "Southern Blues", tags: ["Crunch", "Blues"], color: T.green },
    { num: 5, name: "Metal Djent", tags: ["High Gain", "Djent"], color: T.red },
    { num: 6, name: "Studio Direct", tags: ["Clean", "Studio"], color: T.blue },
    { num: 7, name: "Acoustic Sim", tags: ["Acoustic", "Clean"], color: "#A060E0" },
    { num: 8, name: "Nashville Twang", tags: ["Country", "Clean"], color: T.green },
  ];

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%", background: T.bg }}>
      <div className="screen-header">
        <div className="screen-title">Presets</div>
        <button className="btn-icon" style={{ width: 30, height: 30, fontSize: 14 }}
          onClick={onClose}>✕</button>
      </div>
      <div className="screen-body">
        <div className="toggle" style={{ marginBottom: 4 }}>
          {["All", "Live", "Studio", "Practice"].map(t => (
            <button key={t} className={`toggle-btn ${t === "All" ? "active" : ""}`}>{t}</button>
          ))}
        </div>
        {samplePresets.map((p, i) => (
          <div key={i} className={`preset-row ${activeIdx === i ? "active" : ""}`}
            onClick={() => { setActiveIdx(i); onLoadPreset?.(p.num); }}>
            <div className="mono" style={{ width: 28, height: 28, borderRadius: 6, display: "flex",
              alignItems: "center", justifyContent: "center", fontSize: 12, fontWeight: 700,
              background: activeIdx === i ? `${p.color}22` : T.surface,
              color: activeIdx === i ? p.color : T.textDim,
              border: `1px solid ${activeIdx === i ? p.color + "60" : T.border}` }}>{p.num}</div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 3 }}>{p.name}</div>
              <div style={{ display: "flex", gap: 4 }}>
                {p.tags.map(t => (
                  <span key={t} className="badge" style={{ background: `${p.color}18`, color: p.color, fontSize: 9 }}>{t}</span>
                ))}
              </div>
            </div>
            {activeIdx === i && <span style={{ color: T.amber, fontSize: 18 }}>●</span>}
          </div>
        ))}
      </div>
    </div>
  );
}

// ── App Shell — Helix Stadium Layout ──────────────────────────
const MOCK_BLOCKS = [
  { id: "gate", type: "gate", name: "Noise Gate", bypassed: false, params: { threshold: 40, release: 25, depth: 70, mix: 100 }},
  { id: "drive", type: "overdrive", name: "OD-808", bypassed: false, params: { drive: 65, tone: 55, level: 70 }},
  { id: "dist", type: "distortion", name: "RAT", bypassed: true, params: { drive: 75, filter: 50, level: 65 }},
  { id: "mod", type: "phaser", name: "Phase 90", bypassed: false, params: { rate: 35, depth: 60, feedback: 40, mix: 50 }},
  { id: "delay", type: "delay", name: "Digital Delay", bypassed: false, params: { time: 45, feedback: 25, mix: 40 }},
  { id: "reverb", type: "reverb", name: "Hall Reverb", bypassed: false, params: { decay: 60, mix: 35, tone: 50 }},
];

const MOCK_PRESETS = [
  { num: 1, name: "Plexi Crunch" },
  { num: 2, name: "Modern Hi-Gain" },
  { num: 3, name: "Clean Chime" },
  { num: 4, name: "Blues Drive" },
];

export default function App() {
  // ── State ──
  const [view, setView] = useState("main"); // "main" | "captures" | "presets"
  const [state, setState] = useState({ connected: false, master_volume: 0.72, routing_mode: "mono", cpu_percent: 0 });
  const [blocks, setBlocks] = useState(MOCK_BLOCKS);
  const [selectedBlockId, setSelectedBlockId] = useState("gate");
  const [blockParams, setBlockParams] = useState({});
  const [paramView, setParamView] = useState("slider");
  const [vuLevel, setVuLevel] = useState(0);
  const [currentPreset, setCurrentPreset] = useState(1);
  const [currentBank, setCurrentBank] = useState(1);

  // Poll pedal state
  useEffect(() => {
    const poll = async () => {
      try {
        const s = await API.getState();
        setState(s);
        if (s.cpu_percent != null) setState(prev => ({ ...prev, cpu_percent: s.cpu_percent }));
        if (s.input_level != null) setVuLevel(s.input_level);
      } catch {}
    };
    poll();
    const id = setInterval(poll, 3000);
    return () => clearInterval(id);
  }, []);

  // Mock VU animation when no real data
  useEffect(() => {
    const id = setInterval(() => {
      setVuLevel(prev => Math.max(0, Math.min(100, prev + (Math.random() - 0.45) * 8)));
    }, 150);
    return () => clearInterval(id);
  }, []);

  // Selected block
  const selectedBlock = useMemo(() => blocks.find(b => b.id === selectedBlockId), [blocks, selectedBlockId]);

  // Initialize params when block is selected
  useEffect(() => {
    if (selectedBlock && blockParams[selectedBlockId] == null) {
      const defs = getBlockParams(selectedBlock.type);
      const init = {};
      defs.forEach(p => {
        init[p.key] = (selectedBlock.params?.[p.key] != null) ? selectedBlock.params[p.key] : p.def;
      });
      setBlockParams(prev => ({ ...prev, [selectedBlockId]: init }));
    }
  }, [selectedBlockId, selectedBlock, blockParams]);

  const handleSelectBlock = useCallback((id) => {
    setSelectedBlockId(prev => prev === id ? null : id);
  }, []);

  const handleToggleBlock = useCallback((id) => {
    setBlocks(prev => prev.map(b => b.id === id ? { ...b, bypassed: !b.bypassed } : b));
    API.toggleBlock(id, false).catch(() => {});
  }, []);

  const handleParamChange = useCallback((id, key, value) => {
    setBlockParams(prev => ({ ...prev, [id]: { ...(prev[id] || {}), [key]: value } }));
  }, []);

  const handleParamChangeEnd = useCallback((id, key, value) => {
    API.updateBlock(id, { [key]: value }).catch(() => {});
  }, []);

  const handleReorder = useCallback((fromIdx, toIdx) => {
    setBlocks(prev => {
      const arr = [...prev];
      const [moved] = arr.splice(fromIdx, 1);
      arr.splice(toIdx, 0, moved);
      return arr;
    });
  }, []);

  const handleAddBlock = useCallback(() => {
    const id = "block_" + Date.now();
    setBlocks(prev => [...prev, { id, type: "overdrive", name: "New Drive", bypassed: false, params: { drive: 30, tone: 50, level: 70 } }]);
  }, []);

  const handleRemoveBlock = useCallback((id) => {
    setBlocks(prev => prev.filter(b => b.id !== id));
    if (selectedBlockId === id) setSelectedBlockId(null);
  }, [selectedBlockId]);

  const handleBankUp = useCallback(() => {
    setCurrentBank(prev => Math.min(prev + 1, 8));
  }, []);

  const handleBankDown = useCallback(() => {
    setCurrentBank(prev => Math.max(prev - 1, 1));
  }, []);

  // Convert bank number to letter (1=A, 2=B, etc.)
  const bankLetter = String.fromCharCode(64 + currentBank);

  // ── Render ──
  return (
    <>
      <style>{css}</style>
      <div style={{
        width: "100%", maxWidth: 480, margin: "0 auto", height: "100vh",
        display: "flex", flexDirection: "column", background: T.bg, overflow: "hidden",
        fontFamily: "'Inter', sans-serif",
      }}>
        {view === "main" && (
          <>
            {/* ── Status Bar ── */}
            <div style={{
              display: "flex", justifyContent: "space-between", alignItems: "center",
              padding: "6px 12px 4px", background: T.panel, borderBottom: `1px solid ${T.border}`,
              flexShrink: 0,
            }}>
              <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
                <div style={{ width: 6, height: 6, borderRadius: "50%",
                  background: state.connected ? T.green : T.red,
                  boxShadow: `0 0 6px ${state.connected ? T.green : T.red}` }} />
                <span className="mono" style={{ fontSize: 9, color: T.textSec }}>
                  {state.connected ? "CONNECTED" : "OFFLINE"}
                </span>
                <span className="mono" style={{ fontSize: 9, color: T.textDim }}>
                  {state.current_preset?.name || "Plexi Crunch"}
                </span>
              </div>
              <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
                <VUMeter level={vuLevel} height={28} />
                <button className="btn-icon" style={{ width: 26, height: 26, fontSize: 11 }}
                  onClick={() => setView("captures")} title="Downloads">📁</button>
                <button className="btn-icon" style={{ width: 26, height: 26, fontSize: 11 }}
                  onClick={() => setView("presets")} title="Presets">⭐</button>
              </div>
            </div>

            {/* ── Scribble Info Bar ── */}
            <ScribbleBar state={state} />

            {/* ── Preset Strip ── */}
            <div style={{
              display: "flex", gap: 4, padding: "5px 10px",
              flexShrink: 0, borderBottom: `1px solid ${T.border}`,
            }}>
              {MOCK_PRESETS.map(p => (
                <div key={p.num}
                  className={`preset-chip ${p.num === currentPreset ? "active" : ""}`}
                  onClick={() => setCurrentPreset(p.num)}>
                  <div className="pc-name">{p.name}</div>
                  <div className="pc-num">0{p.num}</div>
                </div>
              ))}
            </div>

            {/* ── Block Chain + Params ── */}
            <div style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
              <div style={{ flex: selectedBlock ? "0 0 auto" : 1, overflow: "hidden" }}>
                <BlockChain
                  blocks={blocks}
                  selectedBlockId={selectedBlockId}
                  onSelectBlock={handleSelectBlock}
                  onToggleBlock={handleToggleBlock}
                  onReorder={handleReorder}
                  onAddBlock={handleAddBlock}
                  onRemoveBlock={handleRemoveBlock}
                />
              </div>

              {/* ── Parameter Panel ── */}
              {selectedBlock && blockParams[selectedBlockId] && (
                <ParameterPanel
                  block={selectedBlock}
                  params={blockParams[selectedBlockId] || {}}
                  viewMode={paramView}
                  onToggleView={() => setParamView(p => p === 'slider' ? 'knob' : 'slider')}
                  onParamChange={(key, value) => handleParamChange(selectedBlockId, key, value)}
                  onParamChangeEnd={(key, value) => handleParamChangeEnd(selectedBlockId, key, value)}
                />
              )}
              {!selectedBlock && (
                <div style={{
                  flex: 1, display: "flex", alignItems: "center", justifyContent: "center",
                  background: T.bg, color: T.textDim, fontSize: 12,
                }}>
                  Tap a block to edit its parameters
                </div>
              )}
            </div>

            {/* ── Footswitch Bar ── */}
            <FootswitchBar
              blocks={blocks}
              selectedBlockId={selectedBlockId}
              currentBank="A"
              onSelectBlock={handleSelectBlock}
              onToggleBlock={handleToggleBlock}
              onBankUp={() => {}}
              onBankDown={() => {}}
              onGlobalBypass={() => setState(prev => ({ ...prev, bypass: !prev.bypass }))}
              globalBypass={state.bypass}
            />
          </>
        )}

        {/* ── Overlay screens ── */}
        {view === "captures" && (
          <CapturesScreen onClose={() => setView("main")} />
        )}
        {view === "presets" && (
          <PresetsScreen
            state={state}
            onClose={() => setView("main")}
            onLoadPreset={(num) => setCurrentPreset(num)}
          />
        )}
      </div>
    </>
  );
}
