import { useState, useRef, useEffect, useCallback, useMemo } from "react";
import { T } from "./theme.js";

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(`${path}`, opts);
  if (!res.ok) throw new Error(`API ${method} ${path}: ${res.status}`);
  return res.json();
}

const SETTINGS = {
  "Audio": [
    { key: "master_volume", label: "Master Volume", type: "slider", min: 0, max: 100, def: 72, unit: "%", icon: "🔊",
      via: "volume" },
    { key: "sample_rate",  label: "Sample Rate",   type: "select", def: "48000",
      options: [
        { value: "22050", label: "22.05 kHz" },
        { value: "44100", label: "44.1 kHz" },
        { value: "48000", label: "48 kHz" },
        { value: "88200", label: "88.2 kHz" },
        { value: "96000", label: "96 kHz" },
      ], icon: "📊", via: "audio_profile" },
    { key: "buffer_size",  label: "Buffer Size",   type: "select", def: "512",
      options: [
        { value: "64",  label: "64 (1.3ms)" },
        { value: "128", label: "128 (2.7ms)" },
        { value: "256", label: "256 (5.3ms)" },
        { value: "512", label: "512 (10.6ms)" },
        { value: "1024",label: "1024 (21.3ms)" },
      ], icon: "📦", via: "audio_profile" },
    { key: "input_pad",    label: "Input Pad",     type: "select", def: "off",
      options: [
        { value: "off", label: "Off (0 dB)" },
        { value: "-6",  label: "-6 dB" },
        { value: "-12", label: "-12 dB" },
        { value: "-18", label: "-18 dB" },
      ], icon: "📥", via: "settings" },
    { key: "input_impedance", label: "Input Impedance", type: "select", def: "1m",
      options: [
        { value: "1m",  label: "1 MΩ" },
        { value: "500k", label: "500 kΩ" },
        { value: "250k", label: "250 kΩ" },
        { value: "150k", label: "150 kΩ" },
        { value: "68k",  label: "68 kΩ" },
      ], icon: "⚡", via: "settings" },
  ],
  "Routing": [
    { key: "routing_mode", label: "Routing Mode", type: "select", def: "mono",
      options: [
        { value: "mono", label: "Mono" },
        { value: "4cm",  label: "4CM (Stereo FX Loop)" },
      ], icon: "🔀", via: "routing" },
    { key: "routing_breakpoint", label: "FX Loop Breakpoint", type: "select", def: "7",
      options: [
        { value: "1", label: "After block 1" },
        { value: "2", label: "After block 2" },
        { value: "3", label: "After block 3" },
        { value: "4", label: "After block 4" },
        { value: "5", label: "After block 5" },
        { value: "6", label: "After block 6" },
        { value: "7", label: "After block 7" },
        { value: "8", label: "After block 8" },
      ], icon: "↔", via: "routing" },
  ],
  "Audio Interface": [
    { key: "input_device", label: "Input Device", type: "select", def: "hw:0,0",
      options: [
        { value: "hw:0,0", label: "Default (hw:0,0)" },
        { value: "hw:1,0", label: "USB Audio (hw:1,0)" },
        { value: "hw:2,0", label: "HDMI Audio (hw:2,0)" },
      ], icon: "🎤", via: "settings" },
    { key: "output_device", label: "Output Device", type: "select", def: "hw:0,0",
      options: [
        { value: "hw:0,0", label: "Default (hw:0,0)" },
        { value: "hw:1,0", label: "USB Audio (hw:1,0)" },
        { value: "hw:2,0", label: "HDMI Audio (hw:2,0)" },
      ], icon: "🔈", via: "settings" },
    { key: "channel_mode", label: "Channel Mode", type: "select", def: "mono",
      options: [
        { value: "mono",       label: "Mono" },
        { value: "dual-mono",  label: "Dual Mono" },
        { value: "stereo_4cm", label: "Stereo (4CM)" },
      ], icon: "🔊", via: "settings" },
  ],
  "MIDI": [
    { key: "midi_channel", label: "MIDI Channel",   type: "select", def: "omni",
      options: [
        { value: "omni", label: "Omni" },
        ...Array.from({ length: 16 }, (_, i) => ({
          value: String(i + 1), label: `Channel ${i + 1}`
        })),
      ], icon: "🎹", via: "settings" },
    { key: "midi_clock",   label: "MIDI Clock",     type: "toggle", def: false, via: "settings", icon: "⏱" },
    { key: "midi_thru",    label: "MIDI Thru",      type: "toggle", def: true, via: "settings", icon: "↔" },
  ],
  "Display": [
    { key: "brightness",   label: "Display Brightness", type: "slider", min: 1, max: 10, def: 8, unit: "", icon: "☀️", via: "settings" },
    { key: "auto_dim",     label: "Auto Dim",          type: "toggle", def: true, via: "settings", icon: "🌙" },
    { key: "screen_saver", label: "Screen Saver",      type: "select", def: "5m",
      options: [
        { value: "off", label: "Off" },
        { value: "1m",  label: "1 min" },
        { value: "5m",  label: "5 min" },
        { value: "15m", label: "15 min" },
        { value: "30m", label: "30 min" },
      ], icon: "💤", via: "settings" },
  ],
  "Tempo": [
    { key: "tap_tempo",        label: "Tap Tempo",       type: "bpm", def: 120, via: "settings", icon: "🔄" },
    { key: "tempo_division",   label: "Tempo Division",  type: "select", def: "quarter",
      options: [
        { value: "half",      label: "½ Note" },
        { value: "quarter",   label: "¼ Note" },
        { value: "eighth",    label: "⅛ Note" },
        { value: "triplet",   label: "⅛ Triplet" },
        { value: "sixteenth", label: "¹⁄₁₆ Note" },
        { value: "dotted_eighth", label: "Dotted ⅛" },
      ], icon: "🎶", via: "settings" },
    { key: "tap_tempo_mute",   label: "Mute on Tap",     type: "toggle", def: false, via: "settings", icon: "🔇" },
  ],
};

function buildDefaults(overrides = {}) {
  const init = {};
  for (const group of Object.values(SETTINGS)) {
    for (const s of group) {
      init[s.key] = s.def;
    }
  }
  return { ...init, ...overrides };
}

// ── Widgets ────────────────────────────────────────────────────────────────

function SettingsSlider({ label, value, min, max, unit, onChange }) {
  const trackRef = useRef(null);
  const dragRef = useRef(false);
  const pct = ((value - min) / (max - min)) * 100;

  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(); onChange?.(valFromEvent(e)); };
    const up = () => { 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, onChange]);

  const handleStart = (e) => { e.preventDefault(); dragRef.current = true; onChange?.(valFromEvent(e)); };
  const quick = (delta) => (e) => { e.preventDefault(); e.stopPropagation(); onChange?.(Math.max(min, Math.min(max, value + delta))); };

  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 4 }}>
        <span style={{ fontSize: 11, fontWeight: 500, color: T.textSec }}>{label}</span>
        <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
          <button style={{
            width: 44, height: 44, borderRadius: "50%", border: `1px solid ${T.border}`,
            background: T.panel, color: T.textPrimary, fontSize: 14, fontWeight: 700,
            display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", lineHeight: 1,
          }} onMouseDown={quick(-1)} onTouchStart={quick(-1)}>−</button>
          <span style={{
            fontFamily: "'JetBrains Mono', monospace", fontSize: 14, fontWeight: 700, color: T.amber,
            minWidth: 36, textAlign: "center",
          }}>{value}{unit}</span>
          <button style={{
            width: 44, height: 44, borderRadius: "50%", border: `1px solid ${T.border}`,
            background: T.panel, color: T.textPrimary, fontSize: 14, fontWeight: 700,
            display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", lineHeight: 1,
          }} onMouseDown={quick(1)} onTouchStart={quick(1)}>+</button>
        </div>
      </div>
      <div ref={trackRef}
        onMouseDown={handleStart} onTouchStart={handleStart}
        style={{
          position: "relative", height: 32, display: "flex", alignItems: "center",
          cursor: "pointer", touchAction: "none",
        }}>
        <div style={{ width: "100%", height: 6, background: T.border, borderRadius: 3, position: "relative", overflow: "hidden" }}>
          <div style={{ height: "100%", borderRadius: 3, position: "absolute", top: 0, left: 0, width: "100%", background: T.amber, transform: `scaleX(${pct/100})`, transformOrigin: "left center", transition: "transform .03s" }} />
        </div>
        <div style={{ width: 24, height: 24, borderRadius: "50%", position: "absolute", top: "50%", left: `${pct}%`, transform: "translate(-50%,-50%)", background: T.amber, border: `3px solid ${T.bg}`, boxShadow: `0 1px 6px rgba(0,0,0,.5)`, pointerEvents: "none", transition: "left .03s" }} />
      </div>
    </div>
  );
}

function SettingsSelect({ label, value, options, onChange }) {
  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
        <span style={{ fontSize: 11, fontWeight: 500, color: T.textSec }}>{label}</span>
      </div>
      <div style={{ display: "flex", gap: 4, flexWrap: "wrap", background: T.panel, borderRadius: 6, padding: 4 }}>
        {options.map(opt => (
          <button key={opt.value}
            onClick={() => onChange?.(opt.value)}
            style={{
              flex: 1, padding: "6px 8px", borderRadius: 4, border: "none",
              fontSize: 10, fontWeight: 600, cursor: "pointer",
              background: value === opt.value ? T.amber : "transparent",
              color: value === opt.value ? "#000" : T.textSec,
              transition: "all .1s", letterSpacing: ".03em",
              whiteSpace: "nowrap", minWidth: 0,
            }}
          >{opt.label}</button>
        ))}
      </div>
    </div>
  );
}

function SettingsToggle({ label, value, onChange }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 0" }}>
      <span style={{ fontSize: 11, fontWeight: 500, color: T.textSec }}>{label}</span>
      <button
        onClick={() => onChange?.(!value)}
        style={{
          width: 44, height: 24, borderRadius: 12,
          background: value ? T.amber : T.border,
          border: "none", cursor: "pointer", position: "relative",
          transition: "background .15s",
        }}
      >
        <div style={{
          width: 18, height: 18, borderRadius: "50%",
          background: "#fff",
          position: "absolute", top: 3, left: value ? 23 : 3,
          transition: "left .15s",
          boxShadow: "0 1px 3px rgba(0,0,0,.3)",
        }} />
      </button>
    </div>
  );
}

function BpmWidget({ label, value, onChange }) {
  const tapTimes = useRef([]);

  const handleTap = useCallback(() => {
    const now = Date.now();
    tapTimes.current.push(now);
    if (tapTimes.current.length > 4) tapTimes.current.shift();
    if (tapTimes.current.length < 2) return;
    const intervals = [];
    for (let i = 1; i < tapTimes.current.length; i++) {
      intervals.push(tapTimes.current[i] - tapTimes.current[i - 1]);
    }
    const avg = intervals.reduce((a, b) => a + b, 0) / intervals.length;
    const bpm = Math.max(20, Math.min(300, Math.round(60000 / avg)));
    onChange?.(bpm);
  }, [onChange]);

  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
        <span style={{ fontSize: 11, fontWeight: 500, color: T.textSec }}>{label}</span>
      </div>
      <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
        <button onClick={handleTap} style={{ padding: "8px 20px", borderRadius: 8, border: `1px solid ${T.amber}66`, background: `${T.amber}15`, color: T.amber, fontSize: 12, fontWeight: 700, cursor: "pointer", letterSpacing: ".05em" }}>TAP</button>
        <div style={{ flex: 1, display: "flex", alignItems: "center", gap: 4, background: T.surface, borderRadius: 6, border: `1px solid ${T.border}`, padding: "6px 10px" }}>
          <button onClick={() => onChange?.(Math.max(20, value - 1))} style={{ background: "none", border: "none", color: T.textDim, fontSize: 14, cursor: "pointer", padding: "0 4px" }}>−</button>
          <span style={{ flex: 1, textAlign: "center", fontFamily: "'JetBrains Mono', monospace", fontSize: 16, fontWeight: 700, color: T.amber }}>{value} BPM</span>
          <button onClick={() => onChange?.(Math.min(300, value + 1))} style={{ background: "none", border: "none", color: T.textDim, fontSize: 14, cursor: "pointer", padding: "0 4px" }}>+</button>
        </div>
      </div>
    </div>
  );
}

// ── Global Settings ─────────────────────────────────────────────────────────

export default function GlobalSettings({ onClose, onMasterVolume, onAudioProfile, onRoutingChange, onSaveSettings }) {
  const [values, setValues] = useState(buildDefaults());
  const [savedValues, setSavedValues] = useState(buildDefaults());
  const [loaded, setLoaded] = useState(false);
  const [saving, setSaving] = useState(false);
  const [saveMsg, setSaveMsg] = useState(null); // "saved" | "error" | null

  useEffect(() => {
    let cancelled = false;
    Promise.all([
      api("GET", "/api/state").catch(() => null),
      api("GET", "/api/audio/profile").catch(() => null),
      api("GET", "/api/routing").catch(() => null),
    ]).then(([state, audioProfile, routing]) => {
      if (cancelled) return;
      const overrides = {};
      if (state) {
        if (state.master_volume != null) overrides.master_volume = Math.round(state.master_volume * 100);
        if (state.sample_rate != null) overrides.sample_rate = String(state.sample_rate);
        if (state.channel_mode) overrides.channel_mode = state.channel_mode;
      }
      if (audioProfile) {
        if (audioProfile.rate != null) overrides.sample_rate = String(audioProfile.rate);
        if (audioProfile.period != null) overrides.buffer_size = String(audioProfile.period);
      }
      if (routing) {
        if (routing.routing_mode) overrides.routing_mode = routing.routing_mode;
        if (routing.routing_breakpoint != null) overrides.routing_breakpoint = String(routing.routing_breakpoint);
      }
      const merged = { ...buildDefaults(), ...overrides };
      setValues(merged);
      setSavedValues(merged);
      setLoaded(true);
    });
    return () => { cancelled = true; };
  }, []);

  const hasChanges = useMemo(() => {
    return Object.keys(values).some(k => values[k] !== savedValues[k]);
  }, [values, savedValues]);

  const groups = Object.entries(SETTINGS);

  const update = useCallback((key, val) => {
    setValues(prev => ({ ...prev, [key]: val }));
    setSaveMsg(null);
  }, []);

  const handleSave = useCallback(async () => {
    setSaving(true);
    setSaveMsg(null);
    const changed = {};
    for (const k of Object.keys(values)) {
      if (values[k] !== savedValues[k]) {
        changed[k] = values[k];
      }
    }
    // Apply instant-effect settings via dedicated endpoints first
    const promises = [];
    if (changed.master_volume != null) {
      onMasterVolume?.(changed.master_volume);
    }
    if (changed.sample_rate != null || changed.buffer_size != null) {
      const rate = changed.sample_rate || values.sample_rate || "48000";
      const period = changed.buffer_size || values.buffer_size || "512";
      onAudioProfile?.(parseInt(rate), parseInt(period));
    }
    if (changed.routing_mode != null || changed.routing_breakpoint != null) {
      const mode = changed.routing_mode || values.routing_mode || "mono";
      const bp = changed.routing_breakpoint || values.routing_breakpoint || "7";
      onRoutingChange?.(mode, parseInt(bp));
    }

    // Everything else → bulk POST /api/settings
    const settingsChanged = Object.fromEntries(
      Object.entries(changed).filter(([k]) =>
        !["master_volume", "sample_rate", "buffer_size", "routing_mode", "routing_breakpoint"].includes(k)
      )
    );
    if (Object.keys(settingsChanged).length > 0) {
      promises.push(
        api("POST", "/api/settings", settingsChanged)
          .then(() => {})
          .catch(() => { throw new Error("Save failed"); })
      );
    }

    try {
      await Promise.all(promises);
      setSavedValues({ ...values });
      setSaveMsg("saved");
      setTimeout(() => { setSaveMsg(null); }, 2000);
    } catch {
      setSaveMsg("error");
    }
    setSaving(false);
  }, [values, savedValues, onMasterVolume, onAudioProfile, onRoutingChange]);

  const handleDiscard = useCallback(() => {
    setValues({ ...savedValues });
    setSaveMsg(null);
  }, [savedValues]);

  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 998, display: "flex", flexDirection: "column", background: T.bg, color: T.textPrimary, fontFamily: "'Space Grotesk', sans-serif" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "10px 14px", borderBottom: `1px solid ${T.border}`, background: T.panel, flexShrink: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <button onClick={onClose} style={{ width: 44, height: 44, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", background: T.surface, border: `1px solid ${T.border}`, color: T.textPrimary, fontSize: 16, cursor: "pointer" }}>✕</button>
          <div>
            <div style={{ fontSize: 15, fontWeight: 600 }}>Settings</div>
            {!loaded && <span style={{ fontSize: 10, color: T.textDim }}>Loading…</span>}
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          {saveMsg === "saved" && (
            <span style={{ fontSize: 10, fontWeight: 600, color: T.green }}>✓ Saved</span>
          )}
          {saveMsg === "error" && (
            <span style={{ fontSize: 10, fontWeight: 600, color: T.red }}>✗ Save failed</span>
          )}
          {hasChanges && (
            <span style={{ fontSize: 10, color: T.amber, fontWeight: 600 }}>Unsaved</span>
          )}
          {hasChanges && (
            <button onClick={handleDiscard}
              style={{ padding: "6px 12px", borderRadius: 6, border: `1px solid ${T.border}`, background: T.surface, color: T.textSec, fontSize: 11, fontWeight: 600, cursor: "pointer" }}>
              Discard
            </button>
          )}
          <button onClick={handleSave} disabled={!hasChanges || saving}
            style={{
              padding: "6px 14px", borderRadius: 6, border: "none",
              background: hasChanges ? T.amber : T.border, color: hasChanges ? "#000" : T.textDim,
              fontSize: 11, fontWeight: 700, cursor: hasChanges ? "pointer" : "default",
              opacity: saving ? 0.6 : 1,
            }}>
            {saving ? "Saving…" : "Save"}
          </button>
        </div>
      </div>

      <div style={{ flex: 1, overflowY: "auto", overflowX: "hidden", padding: "6px 14px 20px" }}>
        {groups.map(([groupName, settings]) => (
          <div key={groupName}>
            <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: ".1em", textTransform: "uppercase", color: T.textDim, padding: "14px 0 8px" }}>{groupName}</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 12, background: T.surface, borderRadius: 8, border: `1px solid ${T.border}`, padding: "12px 14px" }}>
              {settings.map(s => (
                <div key={s.key} style={{ display: "flex", alignItems: "flex-start", gap: 10 }}>
                  <div style={{ width: 28, height: 28, borderRadius: 6, flexShrink: 0, display: "flex", alignItems: "center", justifyContent: "center", background: T.panel, fontSize: 13 }}>{s.icon}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    {s.type === "slider" && <SettingsSlider label={s.label} value={values[s.key] ?? s.def} min={s.min} max={s.max} unit={s.unit} onChange={v => update(s.key, v)} />}
                    {s.type === "select" && <SettingsSelect label={s.label} value={values[s.key] ?? s.def} options={s.options} onChange={v => update(s.key, v)} />}
                    {s.type === "toggle" && <SettingsToggle label={s.label} value={values[s.key] ?? s.def} onChange={v => update(s.key, v)} />}
                    {s.type === "bpm" && <BpmWidget label={s.label} value={values[s.key] ?? s.def} onChange={v => update(s.key, v)} />}
                  </div>
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}
