import { useState, useEffect, useRef } from "react";
import { useNavigate, Link } from "react-router-dom";
import api from "../../api/client";
import ConfirmDialog from "../../components/ConfirmDialog";
import PlaybackModal from "../PlaybackModal";
import { getLocalizedValue } from "../melodyUtils";
import MelodiesIcon from "../../assets/side-menu-icons/melodies.svg?react";
import ComposerIcon from "../../assets/side-menu-icons/composer.svg?react";

// ─── Helpers ────────────────────────────────────────────────────────────────

function fallbackCopy(text, onSuccess) {
  const ta = document.createElement("textarea");
  ta.value = text;
  ta.style.cssText = "position:fixed;top:0;left:0;opacity:0";
  document.body.appendChild(ta);
  ta.focus();
  ta.select();
  try { document.execCommand("copy"); onSuccess?.(); } catch (_) {}
  document.body.removeChild(ta);
}

function copyText(text, onSuccess) {
  if (navigator.clipboard) {
    navigator.clipboard.writeText(text).then(onSuccess).catch(() => fallbackCopy(text, onSuccess));
  } else {
    fallbackCopy(text, onSuccess);
  }
}

function stepValueToNotation(stepValue) {
  const v = Number(stepValue || 0) & 0xffff;
  if (!v) return "0";
  const bells = [];
  for (let bit = 0; bit < 16; bit++) {
    if (v & (1 << bit)) bells.push(String(bit + 1));
  }
  return bells.join("+");
}

async function decodeBinaryToStepsString(file) {
  const buf = await file.arrayBuffer();
  const view = new DataView(buf);
  const values = [];
  for (let i = 0; i + 1 < buf.byteLength; i += 2) {
    values.push(view.getUint16(i, false));
  }
  return values.map(stepValueToNotation).join(",");
}

// ─── Column Config ───────────────────────────────────────────────────────────

const ALL_COLUMNS = [
  { key: "name",       label: "Name",       defaultOn: true,  alwaysOn: true },
  { key: "pid",        label: "PID",        defaultOn: true  },
  { key: "steps",      label: "Steps",      defaultOn: true  },
  { key: "builtin",    label: "Built-in",   defaultOn: true  },
  { key: "binary",     label: "Binary",   defaultOn: true  },
  { key: "progmem",    label: "C++ Code",       defaultOn: true  },
  { key: "assigned",   label: "Assigned",   defaultOn: true  },
  { key: "updated",    label: "Updated",    defaultOn: true  },
  { key: "actions",    label: "Actions",    defaultOn: true,  alwaysOn: true },
];

const COLS_KEY   = "archetypeListColumns";
const ORDER_KEY  = "archetypeListColumnsOrder";

function loadColumnPrefs() {
  const vis   = JSON.parse(localStorage.getItem(COLS_KEY)  || "null");
  const order = JSON.parse(localStorage.getItem(ORDER_KEY) || "null");
  const visible = {};
  const orderedIds = order || ALL_COLUMNS.map((c) => c.key);
  ALL_COLUMNS.forEach((c) => {
    visible[c.key] = vis ? (vis[c.key] ?? c.defaultOn) : c.defaultOn;
  });
  return { visible, orderedIds };
}

function saveColumnPrefs(visible, orderedIds) {
  localStorage.setItem(COLS_KEY,  JSON.stringify(visible));
  localStorage.setItem(ORDER_KEY, JSON.stringify(orderedIds));
}

// ─── Modals ──────────────────────────────────────────────────────────────────

function UploadArchetypeModal({ open, existingArchetypes, onClose, onUploaded }) {
  const [file, setFile] = useState(null);
  const [name, setName] = useState("");
  const [pid, setPid] = useState("");
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState("");

  useEffect(() => {
    if (!open) { setFile(null); setName(""); setPid(""); setSubmitting(false); setError(""); }
  }, [open]);

  if (!open) return null;

  const submit = async () => {
    setError("");
    if (!file) { setError("Please choose a .bin or .bsm file."); return; }
    if (!name.trim()) { setError("Friendly name is required."); return; }
    if (!pid.trim()) { setError("PID is required."); return; }
    const dupName = (existingArchetypes || []).find((a) => a.name?.toLowerCase() === name.trim().toLowerCase());
    if (dupName) { setError(`An archetype with the name "${name.trim()}" already exists.`); return; }
    const dupPid = (existingArchetypes || []).find((a) => a.pid?.toLowerCase() === pid.trim().toLowerCase());
    if (dupPid) { setError(`An archetype with the PID "${pid.trim()}" already exists.`); return; }

    setSubmitting(true);
    try {
      const steps = await decodeBinaryToStepsString(file);
      await api.post("/builder/melodies", { name: name.trim(), pid: pid.trim(), steps });
      onUploaded?.();
      onClose?.();
    } catch (err) {
      setError(err.message || "Failed to upload archetype.");
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="fixed inset-0 flex items-center justify-center z-50" style={{ backgroundColor: "rgba(0,0,0,0.65)" }}
      onClick={(e) => e.target === e.currentTarget && !submitting && onClose()}>
      <div className="w-full max-w-md rounded-lg border shadow-xl"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}>
        <div className="flex items-center justify-between px-6 py-4 border-b" style={{ borderColor: "var(--border-primary)" }}>
          <div>
            <h2 className="text-base font-semibold" style={{ color: "var(--text-heading)" }}>Upload Archetype</h2>
            <p className="text-xs mt-0.5" style={{ color: "var(--text-muted)" }}>Upload .bin/.bsm and save as selectable archetype</p>
          </div>
          <button onClick={onClose} className="text-xl leading-none" style={{ color: "var(--text-muted)" }} disabled={submitting}>&times;</button>
        </div>
        <div className="px-6 py-5 space-y-4">
          {error && (
            <div className="text-sm rounded-md p-3 border" style={{ backgroundColor: "var(--danger-bg)", borderColor: "var(--danger)", color: "var(--danger-text)" }}>{error}</div>
          )}
          <div>
            <label className="block text-sm font-medium mb-1" style={{ color: "var(--text-secondary)" }}>Binary File (.bin/.bsm) *</label>
            <input type="file" accept=".bin,.bsm,application/octet-stream" onChange={(e) => setFile(e.target.files?.[0] || null)}
              className="w-full px-3 py-2 rounded-md text-sm border" disabled={submitting} />
            {file && <p className="text-xs mt-1" style={{ color: "var(--text-muted)" }}>{file.name}</p>}
          </div>
          <div>
            <label className="block text-sm font-medium mb-1" style={{ color: "var(--text-secondary)" }}>Friendly Name *</label>
            <input type="text" value={name} onChange={(e) => setName(e.target.value)}
              className="w-full px-3 py-2 rounded-md text-sm border" placeholder="e.g. Doxology Festive Upload" disabled={submitting} />
          </div>
          <div>
            <label className="block text-sm font-medium mb-1" style={{ color: "var(--text-secondary)" }}>PID *</label>
            <input type="text" value={pid} onChange={(e) => setPid(e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, ""))}
              className="w-full px-3 py-2 rounded-md text-sm border" placeholder="e.g. builtin_doxology_upload" disabled={submitting} />
          </div>
        </div>
        <div className="flex justify-end gap-3 px-6 py-4 border-t" style={{ borderColor: "var(--border-primary)" }}>
          <button onClick={onClose} disabled={submitting} className="px-4 py-2 text-sm rounded-md transition-colors disabled:opacity-50"
            style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)" }}>Cancel</button>
          <button onClick={submit} disabled={submitting} className="px-4 py-2 text-sm rounded-md transition-colors disabled:opacity-50"
            style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}>
            {submitting ? "Uploading..." : "Upload Archetype"}
          </button>
        </div>
      </div>
    </div>
  );
}

function CodeSnippetModal({ melody, onClose }) {
  const [copied, setCopied] = useState(false);
  if (!melody) return null;
  return (
    <div className="fixed inset-0 flex items-center justify-center z-50" style={{ backgroundColor: "rgba(0,0,0,0.7)" }}
      onClick={(e) => e.target === e.currentTarget && onClose()}>
      <div className="w-full rounded-lg border shadow-xl"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)", maxWidth: "720px", maxHeight: "80vh", display: "flex", flexDirection: "column" }}>
        <div className="flex items-center justify-between px-6 py-4 border-b flex-shrink-0" style={{ borderColor: "var(--border-primary)" }}>
          <div>
            <h2 className="text-base font-semibold" style={{ color: "var(--text-heading)" }}>Firmware Code Snippet</h2>
            <p className="text-xs mt-0.5" style={{ color: "var(--text-muted)" }}>{melody.name} · PID: <span className="font-mono">{melody.pid || "—"}</span></p>
          </div>
          <div className="flex items-center gap-3">
            <button onClick={() => copyText(melody.progmem_code, () => { setCopied(true); setTimeout(() => setCopied(false), 2000); })}
              className="px-3 py-1.5 text-xs rounded transition-colors"
              style={{ backgroundColor: copied ? "var(--success-bg)" : "var(--bg-card-hover)", color: copied ? "var(--success-text)" : "var(--text-secondary)", border: "1px solid var(--border-primary)" }}>
              {copied ? "Copied!" : "Copy"}
            </button>
            <button onClick={onClose} className="text-xl leading-none" style={{ color: "var(--text-muted)" }}>&times;</button>
          </div>
        </div>
        <pre className="flex-1 overflow-auto p-5 text-xs"
          style={{ fontFamily: "monospace", color: "var(--text-primary)", backgroundColor: "var(--bg-primary)", whiteSpace: "pre", margin: 0 }}>
          {melody.progmem_code}
        </pre>
      </div>
    </div>
  );
}

function AssignedMelodiesModal({ archetype, melodyDetails, primaryLang, onClose }) {
  if (!archetype) return null;
  return (
    <div className="fixed inset-0 flex items-center justify-center z-50" style={{ backgroundColor: "rgba(0,0,0,0.6)" }}
      onClick={(e) => e.target === e.currentTarget && onClose()}>
      <div className="w-full max-w-md rounded-lg border shadow-xl"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}>
        <div className="flex items-center justify-between px-6 py-4 border-b" style={{ borderColor: "var(--border-primary)" }}>
          <h2 className="text-base font-semibold" style={{ color: "var(--text-heading)" }}>Melodies using "{archetype.name}"</h2>
          <button onClick={onClose} className="text-xl leading-none" style={{ color: "var(--text-muted)" }}>&times;</button>
        </div>
        <div className="p-4 space-y-2 max-h-72 overflow-y-auto">
          {melodyDetails.length === 0 ? (
            <p className="text-sm text-center py-4" style={{ color: "var(--text-muted)" }}>No melodies assigned.</p>
          ) : (
            melodyDetails.map((m) => (
              <Link key={m.id} to={`/melodies/${m.id}`} target="_blank" rel="noopener noreferrer" onClick={onClose}
                className="flex items-center justify-between px-3 py-2 rounded-lg border transition-colors hover:bg-[var(--bg-card-hover)]"
                style={{ borderColor: "var(--border-primary)", color: "var(--text-heading)", textDecoration: "none" }}>
                <span className="text-sm font-medium">{getLocalizedValue(m.nameRaw, primaryLang, m.id)}</span>
                <span className="text-xs" style={{ color: "var(--accent)" }}>View →</span>
              </Link>
            ))
          )}
        </div>
        <div className="flex justify-end px-6 py-4 border-t" style={{ borderColor: "var(--border-primary)" }}>
          <button onClick={onClose} className="px-4 py-2 text-sm rounded-md transition-colors"
            style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)" }}>Close</button>
        </div>
      </div>
    </div>
  );
}

function GenerateBuiltinModal({ open, onClose }) {
  const [loading, setLoading] = useState(false);
  const [code, setCode] = useState("");
  const [copied, setCopied] = useState(false);
  const [error, setError] = useState("");

  useEffect(() => {
    if (!open) { setCode(""); setCopied(false); setError(""); return; }
    setLoading(true);
    const token = localStorage.getItem("access_token");
    fetch("/api/builder/melodies/generate-builtin-list", {
      headers: token ? { Authorization: `Bearer ${token}` } : {},
    })
      .then((r) => r.ok ? r.text() : r.text().then((t) => Promise.reject(t)))
      .then(setCode)
      .catch((e) => setError(String(e)))
      .finally(() => setLoading(false));
  }, [open]);

  if (!open) return null;

  return (
    <div className="fixed inset-0 flex items-center justify-center z-50" style={{ backgroundColor: "rgba(0,0,0,0.7)" }}
      onClick={(e) => e.target === e.currentTarget && onClose()}>
      <div className="w-full rounded-lg border shadow-xl"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)", maxWidth: "780px", maxHeight: "85vh", display: "flex", flexDirection: "column" }}>
        <div className="flex items-center justify-between px-6 py-4 border-b flex-shrink-0" style={{ borderColor: "var(--border-primary)" }}>
          <div>
            <h2 className="text-base font-semibold" style={{ color: "var(--text-heading)" }}>Generate Built-in List</h2>
            <p className="text-xs mt-0.5" style={{ color: "var(--text-muted)" }}>C++ PROGMEM header for all is_builtin archetypes</p>
          </div>
          <div className="flex items-center gap-3">
            {code && (
              <button onClick={() => copyText(code, () => { setCopied(true); setTimeout(() => setCopied(false), 2000); })}
                className="px-3 py-1.5 text-xs rounded transition-colors"
                style={{ backgroundColor: copied ? "var(--success-bg)" : "var(--bg-card-hover)", color: copied ? "var(--success-text)" : "var(--text-secondary)", border: "1px solid var(--border-primary)" }}>
                {copied ? "Copied!" : "Copy"}
              </button>
            )}
            <button onClick={onClose} className="text-xl leading-none" style={{ color: "var(--text-muted)" }}>&times;</button>
          </div>
        </div>
        <div className="flex-1 overflow-auto">
          {loading ? (
            <div className="p-8 text-center text-sm" style={{ color: "var(--text-muted)" }}>Generating...</div>
          ) : error ? (
            <div className="p-6 text-sm" style={{ color: "var(--danger)" }}>{error}</div>
          ) : (
            <pre className="p-5 text-xs" style={{ fontFamily: "monospace", color: "var(--text-primary)", backgroundColor: "var(--bg-primary)", whiteSpace: "pre", margin: 0 }}>
              {code}
            </pre>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Column Selector ─────────────────────────────────────────────────────────

function ColumnSelector({ visible, orderedIds, onToggle, onReorder, onClose }) {
  const dragIdx = useRef(null);
  const cols = orderedIds.map((k) => ALL_COLUMNS.find((c) => c.key === k)).filter(Boolean);

  return (
    <div className="absolute right-0 top-8 z-30 rounded-lg border shadow-xl p-4 w-52"
      style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}>
      <div className="flex items-center justify-between mb-3">
        <span className="text-xs font-semibold uppercase tracking-wide" style={{ color: "var(--text-muted)" }}>Columns</span>
        <button onClick={onClose} className="text-sm leading-none" style={{ color: "var(--text-muted)" }}>&times;</button>
      </div>
      <div className="space-y-1">
        {cols.map((col, idx) => (
          <div key={col.key}
            draggable={!col.alwaysOn}
            onDragStart={() => { dragIdx.current = idx; }}
            onDragOver={(e) => { e.preventDefault(); }}
            onDrop={() => {
              if (dragIdx.current === null || dragIdx.current === idx) return;
              const next = [...orderedIds];
              const [moved] = next.splice(dragIdx.current, 1);
              next.splice(idx, 0, moved);
              dragIdx.current = null;
              onReorder(next);
            }}
            className="flex items-center gap-2 px-2 py-1.5 rounded cursor-grab select-none hover:bg-[var(--bg-card-hover)]">
            <span className="text-xs" style={{ color: "var(--text-muted)" }}>⠿</span>
            <label className="flex items-center gap-2 flex-1 cursor-pointer text-sm" style={{ color: "var(--text-secondary)" }}>
              <input type="checkbox" checked={col.alwaysOn ? true : (visible[col.key] ?? true)}
                disabled={col.alwaysOn}
                onChange={() => !col.alwaysOn && onToggle(col.key)}
                className="accent-[var(--accent)]" />
              {col.label}
            </label>
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── Actions Dropdown ────────────────────────────────────────────────────────

function ActionsDropdown({ onPlay, onDelete, onLoadToComposer }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  return (
    <div className="relative inline-flex justify-center" ref={ref} onClick={(e) => e.stopPropagation()}>
      <button onClick={() => setOpen((v) => !v)}
        className="px-2 py-1 text-xs rounded transition-colors flex items-center gap-1"
        style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)", border: "1px solid var(--border-primary)" }}>
        Actions <span style={{ fontSize: "10px" }}>▼</span>
      </button>
      {open && (
        <div className="absolute right-0 mt-1 rounded-lg border shadow-xl py-1 w-44"
          style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)", zIndex: 100, top: "100%" }}>
          <button onClick={() => { setOpen(false); onPlay(); }}
            className="w-full text-left px-3 py-2 text-xs transition-colors hover:bg-[var(--bg-card-hover)] flex items-center gap-2.5"
            style={{ color: "#4ade80" }}>
            <MelodiesIcon width={13} height={13} style={{ flexShrink: 0, color: "#4ade80" }} />
            Play
          </button>
          <button onClick={() => { setOpen(false); onLoadToComposer(); }}
            className="w-full text-left px-3 py-2 text-xs transition-colors hover:bg-[var(--bg-card-hover)] flex items-center gap-2.5"
            style={{ color: "#60a5fa" }}>
            <ComposerIcon width={13} height={13} style={{ flexShrink: 0, color: "#60a5fa" }} />
            Load to Composer
          </button>
          <div style={{ height: "1px", backgroundColor: "var(--border-primary)", margin: "2px 0" }} />
          <button onClick={() => { setOpen(false); onDelete(); }}
            className="w-full text-left px-3 py-2 text-xs transition-colors hover:bg-[var(--bg-card-hover)] flex items-center gap-2.5"
            style={{ color: "var(--danger)" }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
              <polyline points="3 6 5 6 21 6" />
              <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
              <path d="M10 11v6M14 11v6" />
              <path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2" />
            </svg>
            Delete
          </button>
        </div>
      )}
    </div>
  );
}

// ─── Main Component ──────────────────────────────────────────────────────────

const sectionStyle = { backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" };

export default function ArchetypeList() {
  const navigate = useNavigate();
  const [archetypes, setArchetypes] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [deleteTarget, setDeleteTarget] = useState(null);
  const [deleteWarningConfirmed, setDeleteWarningConfirmed] = useState(false);
  const [codeSnippetMelody, setCodeSnippetMelody] = useState(null);
  const [assignedModal, setAssignedModal] = useState(null);
  const [loadingAssigned, setLoadingAssigned] = useState(false);
  const [primaryLang, setPrimaryLang] = useState("en");
  const [showUploadModal, setShowUploadModal] = useState(false);
  const [showGenerateModal, setShowGenerateModal] = useState(false);
  const [playbackArchetype, setPlaybackArchetype] = useState(null);
  const [togglingBuiltin, setTogglingBuiltin] = useState(null);

  // Search + sort
  const [search, setSearch] = useState("");
  const [sortKey, setSortKey] = useState("updated");
  const [sortDir, setSortDir] = useState("desc");

  // Columns
  const [{ visible, orderedIds }, setColState] = useState(loadColumnPrefs);
  const [showColSelector, setShowColSelector] = useState(false);
  const colSelectorRef = useRef(null);

  useEffect(() => {
    const handler = (e) => { if (colSelectorRef.current && !colSelectorRef.current.contains(e.target)) setShowColSelector(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  useEffect(() => {
    api.get("/settings/melody").then((ms) => setPrimaryLang(ms.primary_language || "en")).catch(() => {});
    loadArchetypes({ verify: true });
  }, []);

  const loadArchetypes = async ({ verify = false } = {}) => {
    setLoading(true);
    setError("");
    try {
      const data = await api.get("/builder/melodies");
      const list = data.melodies || [];
      setArchetypes(list);

      if (verify) {
        let didPrune = false;
        await Promise.all(
          list.map(async (archetype) => {
            const ids = archetype.assigned_melody_ids || [];
            if (ids.length === 0) return;
            const results = await Promise.allSettled(ids.map((mid) => api.get(`/melodies/${mid}`)));
            const missingIds = results
              .map((r, i) => (!r.value || r.status === "rejected" ? ids[i] : null))
              .filter(Boolean);
            for (const mid of missingIds) {
              try { await api.post(`/builder/melodies/${archetype.id}/unassign?firestore_melody_id=${mid}`); didPrune = true; }
              catch { /* best-effort */ }
            }
          })
        );
        if (didPrune) {
          const refreshed = await api.get("/builder/melodies");
          setArchetypes(refreshed.melodies || []);
        }
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const handleDelete = async () => {
    if (!deleteTarget) return;
    try {
      await api.delete(`/builder/melodies/${deleteTarget.id}`);
      setDeleteTarget(null);
      setDeleteWarningConfirmed(false);
      loadArchetypes();
    } catch (err) {
      setError(err.message);
      setDeleteTarget(null);
      setDeleteWarningConfirmed(false);
    }
  };

  const handleToggleBuiltin = async (e, m) => {
    e.stopPropagation();
    if (togglingBuiltin === m.id) return;
    setTogglingBuiltin(m.id);
    try {
      const updated = await api.post(`/builder/melodies/${m.id}/toggle-builtin`);
      setArchetypes((prev) => prev.map((a) => a.id === m.id ? { ...a, is_builtin: updated.is_builtin } : a));
    } catch (err) {
      setError(err.message);
    } finally {
      setTogglingBuiltin(null);
    }
  };

  const handleShowAssigned = async (e, archetype) => {
    e.stopPropagation();
    const ids = archetype.assigned_melody_ids || [];
    if (ids.length === 0) return;
    setLoadingAssigned(true);
    try {
      const results = await Promise.allSettled(ids.map((mid) => api.get(`/melodies/${mid}`)));
      const melodyDetails = results
        .filter((r) => r.status === "fulfilled" && r.value)
        .map((r) => ({ id: r.value.id, nameRaw: r.value.information?.name }));
      const missingIds = results.map((r, i) => r.status === "rejected" || !r.value ? ids[i] : null).filter(Boolean);
      for (const mid of missingIds) {
        try { await api.post(`/builder/melodies/${archetype.id}/unassign?firestore_melody_id=${mid}`); }
        catch { /* best-effort */ }
      }
      if (missingIds.length > 0) loadArchetypes();
      setAssignedModal({ archetype, melodyDetails });
    } catch (err) {
      setError(err.message);
    } finally {
      setLoadingAssigned(false);
    }
  };

  const handleDownloadBinary = async (e, m) => {
    e.stopPropagation();
    const token = localStorage.getItem("access_token");
    const res = await fetch(`/api/builder/melodies/${m.id}/download`, {
      headers: token ? { Authorization: `Bearer ${token}` } : {},
    });
    if (!res.ok) return;
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `${m.name || m.id}.bsm`;
    a.click();
    URL.revokeObjectURL(url);
  };

  // Sorting
  const handleSort = (key) => {
    if (sortKey === key) {
      setSortDir((d) => d === "asc" ? "desc" : "asc");
    } else {
      setSortKey(key);
      setSortDir("asc");
    }
  };

  const sortIcon = (key) => {
    if (sortKey !== key) return <span style={{ color: "var(--text-muted)", opacity: 0.4, fontSize: "10px" }}>↕</span>;
    return <span style={{ color: "var(--accent)", fontSize: "10px" }}>{sortDir === "asc" ? "↑" : "↓"}</span>;
  };

  const countSteps = (s) => !s ? 0 : s.split(",").length;

  // Filter + sort
  const filtered = archetypes
    .filter((m) => {
      if (!search) return true;
      const q = search.toLowerCase();
      return (m.name || "").toLowerCase().includes(q) || (m.pid || "").toLowerCase().includes(q);
    })
    .sort((a, b) => {
      let av, bv;
      switch (sortKey) {
        case "name":     av = (a.name || "").toLowerCase(); bv = (b.name || "").toLowerCase(); break;
        case "pid":      av = (a.pid || "").toLowerCase();  bv = (b.pid || "").toLowerCase();  break;
        case "steps":    av = countSteps(a.steps); bv = countSteps(b.steps); break;
        case "assigned": av = (a.assigned_melody_ids || []).length; bv = (b.assigned_melody_ids || []).length; break;
        case "updated":  av = a.updated_at; bv = b.updated_at; break;
        default:         av = a.updated_at; bv = b.updated_at;
      }
      if (av < bv) return sortDir === "asc" ? -1 : 1;
      if (av > bv) return sortDir === "asc" ? 1 : -1;
      return 0;
    });

  // Column helpers
  const toggleCol = (key) => {
    const next = { ...visible, [key]: !visible[key] };
    setColState({ visible: next, orderedIds });
    saveColumnPrefs(next, orderedIds);
  };
  const reorderCols = (next) => {
    setColState({ visible, orderedIds: next });
    saveColumnPrefs(visible, next);
  };

  const visibleCols = orderedIds
    .map((k) => ALL_COLUMNS.find((c) => c.key === k))
    .filter((c) => c && (c.alwaysOn || visible[c.key]));

  // Sortable column headers
  const SORTABLE = { name: "name", pid: "pid", steps: "steps", assigned: "assigned", updated: "updated" };

  const isLeftAligned = (key) => key === "name" || key === "pid";

  const renderTh = (col) => {
    const sortable = SORTABLE[col.key];
    const leftAlign = isLeftAligned(col.key);
    return (
      <th key={col.key}
        onClick={sortable ? () => handleSort(col.key) : undefined}
        className={`px-4 py-3 font-medium${sortable ? " cursor-pointer select-none" : ""}`}
        style={{ color: "var(--text-secondary)", userSelect: "none", textAlign: leftAlign ? "left" : "center" }}>
        <span className={`flex items-center gap-1 ${leftAlign ? "justify-start" : "justify-center"}`}>
          {col.label}
          {sortable && sortIcon(col.key)}
        </span>
      </th>
    );
  };

  const renderCell = (col, m, _idx) => {
    switch (col.key) {
      case "name":
        return (
          <td key="name" className="px-4 py-3 font-medium" style={{ color: "var(--text-heading)" }}>{m.name}</td>
        );
      case "pid":
        return (
          <td key="pid" className="px-4 py-3" style={{ color: "var(--text-secondary)" }}>
            <span className="font-mono text-xs px-2 py-0.5 rounded" style={{ backgroundColor: "var(--bg-card-hover)" }}>{m.pid || "—"}</span>
          </td>
        );
      case "steps":
        return (
          <td key="steps" className="px-4 py-3 text-center" style={{ color: "var(--text-secondary)" }}>{countSteps(m.steps)}</td>
        );
      case "builtin":
        return (
          <td key="builtin" className="px-4 py-3 text-center" onClick={(e) => e.stopPropagation()}>
            <button
              onClick={(e) => handleToggleBuiltin(e, m)}
              disabled={togglingBuiltin === m.id}
              title={m.is_builtin ? "Mark as not built-in" : "Mark as built-in"}
              className="px-2 py-0.5 text-xs rounded-full cursor-pointer transition-opacity hover:opacity-70 disabled:opacity-40"
              style={m.is_builtin
                ? { backgroundColor: "rgba(139,92,246,0.15)", color: "#a78bfa", border: "1px solid rgba(139,92,246,0.3)" }
                : { backgroundColor: "var(--bg-card-hover)", color: "var(--text-muted)", border: "1px solid var(--border-primary)" }
              }>
              {m.is_builtin ? "Built-in" : "—"}
            </button>
          </td>
        );
      case "binary":
        return (
          <td key="binary" className="px-4 py-3 text-center">
            {m.binary_path ? (
              <button title="Click to Download" onClick={(e) => handleDownloadBinary(e, m)}
                className="px-2 py-0.5 text-xs rounded-full cursor-pointer transition-opacity hover:opacity-70"
                style={{ backgroundColor: "var(--success-bg)", color: "var(--success-text)", border: "none" }}>
                Download
              </button>
            ) : (
              <span className="px-2 py-0.5 text-xs rounded-full" style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-muted)" }}>—</span>
            )}
          </td>
        );
      case "progmem":
        return (
          <td key="progmem" className="px-4 py-3 text-center">
            {m.progmem_code ? (
              <button title="Click to View Code Snippet" onClick={(e) => { e.stopPropagation(); setCodeSnippetMelody(m); }}
                className="px-2 py-0.5 text-xs rounded-full cursor-pointer transition-opacity hover:opacity-70"
                style={{ backgroundColor: "var(--success-bg)", color: "var(--success-text)", border: "none" }}>
                View
              </button>
            ) : (
              <span className="px-2 py-0.5 text-xs rounded-full" style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-muted)" }}>—</span>
            )}
          </td>
        );
      case "assigned": {
        const count = (m.assigned_melody_ids || []).length;
        return (
          <td key="assigned" className="px-4 py-3 text-center" style={{ color: "var(--text-secondary)" }}>
            {count > 0 ? (
              <button title="Click to see assigned melodies" onClick={(e) => handleShowAssigned(e, m)}
                className="px-2 py-0.5 text-xs rounded-full cursor-pointer transition-opacity hover:opacity-70"
                style={{ backgroundColor: "rgba(59,130,246,0.15)", color: "#60a5fa", border: "none" }}
                disabled={loadingAssigned}>
                {count} {count === 1 ? "melody" : "melodies"}
              </button>
            ) : (
              <span style={{ color: "var(--text-muted)" }}>—</span>
            )}
          </td>
        );
      }
      case "updated":
        return (
          <td key="updated" className="px-4 py-3 text-xs text-center" style={{ color: "var(--text-muted)" }}>
            {new Date(m.updated_at).toLocaleDateString()}
          </td>
        );
      case "actions":
        return (
          <td key="actions" className="px-4 py-3 text-center">
            <ActionsDropdown
              onPlay={() => setPlaybackArchetype(m)}
              onDelete={() => { setDeleteTarget(m); setDeleteWarningConfirmed(false); }}
              onLoadToComposer={() => navigate("/melodies/composer", { state: { archetype: m } })}
            />
          </td>
        );
      default:
        return <td key={col.key} />;
    }
  };

  return (
    <div>
      {/* Header */}
      <div className="flex items-center justify-between mb-4">
        <div>
          <h1 className="text-2xl font-bold" style={{ color: "var(--text-heading)" }}>Archetype Manager</h1>
          <p className="text-sm mt-1" style={{ color: "var(--text-muted)" }}>
            Add Archetypes, and Build binary (.bsm) files and firmware PROGMEM code.
          </p>
        </div>
      </div>

      {/* Toolbar row: search + buttons */}
      <div className="flex items-center gap-2 mb-4 flex-wrap">
        <div className="relative flex-1 min-w-48">
          <span className="absolute left-3 top-1/2 -translate-y-1/2 text-sm" style={{ color: "var(--text-muted)" }}>🔍</span>
          <input
            type="text"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            placeholder="Search archetypes..."
            className="w-full pl-8 pr-3 py-2 rounded-md text-sm border"
            style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)", color: "var(--text-primary)" }}
          />
        </div>
        {/* Column selector */}
        <div className="relative" ref={colSelectorRef}>
          <button onClick={() => setShowColSelector((v) => !v)}
            className="flex items-center gap-1.5 px-3 py-2 text-sm rounded-md font-medium border cursor-pointer hover:opacity-80 transition-opacity"
            style={{ backgroundColor: "var(--bg-card-hover)", borderColor: "var(--border-primary)", color: "var(--text-secondary)" }}
            title="Choose columns">
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <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>
            Columns
          </button>
          {showColSelector && (
            <ColumnSelector
              visible={visible}
              orderedIds={orderedIds}
              onToggle={toggleCol}
              onReorder={reorderCols}
              onClose={() => setShowColSelector(false)}
            />
          )}
        </div>

        <button onClick={() => setShowGenerateModal(true)}
          className="px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1.5"
          style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)", border: "1px solid var(--border-primary)" }}>
          Generate Built-in List
        </button>
        <button onClick={() => setShowUploadModal(true)}
          className="px-3 py-2 text-sm rounded-md transition-colors"
          style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)", border: "1px solid var(--border-primary)" }}>
          Upload Archetype
        </button>
        <button onClick={() => navigate("/melodies/archetypes/new")}
          className="px-3 py-2 text-sm rounded-md transition-colors"
          style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}>
          + Add Archetype
        </button>
      </div>

      {error && (
        <div className="text-sm rounded-md p-3 mb-4 border" style={{ backgroundColor: "var(--danger-bg)", borderColor: "var(--danger)", color: "var(--danger-text)" }}>
          {error}
        </div>
      )}

      {loading ? (
        <div className="text-center py-8" style={{ color: "var(--text-muted)" }}>Loading...</div>
      ) : archetypes.length === 0 ? (
        <div className="rounded-lg border p-12 text-center" style={sectionStyle}>
          <p className="text-sm mb-4" style={{ color: "var(--text-muted)" }}>No archetypes yet.</p>
          <button onClick={() => navigate("/melodies/archetypes/new")}
            className="px-4 py-2 text-sm rounded-md transition-colors"
            style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}>
            Add Your First Archetype
          </button>
        </div>
      ) : (
        <div className="rounded-lg border" style={sectionStyle}>
          <table className="w-full text-sm">
            <thead>
              <tr className="border-b" style={{ borderColor: "var(--border-primary)", backgroundColor: "var(--bg-card-hover)" }}>
                {visibleCols.map(renderTh)}
              </tr>
            </thead>
            <tbody>
              {filtered.length === 0 ? (
                <tr>
                  <td colSpan={visibleCols.length} className="px-4 py-8 text-center text-sm" style={{ color: "var(--text-muted)" }}>
                    No archetypes match your search.
                  </td>
                </tr>
              ) : (
                filtered.map((m, idx) => (
                  <tr key={m.id}
                    onClick={() => navigate(`/melodies/archetypes/${m.id}`)}
                    className="border-b cursor-pointer transition-colors hover:bg-[var(--bg-card-hover)]"
                    style={{ borderColor: "var(--border-primary)", backgroundColor: idx % 2 === 1 ? "var(--bg-row-alt)" : "transparent" }}>
                    {visibleCols.map((col) => renderCell(col, m, idx))}
                  </tr>
                ))
              )}
            </tbody>
          </table>
        </div>
      )}

      {/* Two-stage delete */}
      {deleteTarget && !deleteWarningConfirmed && (deleteTarget.assigned_melody_ids?.length || 0) > 0 && (
        <div className="fixed inset-0 flex items-center justify-center z-50" style={{ backgroundColor: "rgba(0,0,0,0.6)" }}>
          <div className="w-full max-w-md rounded-lg border shadow-xl p-6 space-y-4"
            style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}>
            <h3 className="text-base font-semibold" style={{ color: "var(--text-heading)" }}>Archetype In Use</h3>
            <div className="rounded-md p-3 border text-sm"
              style={{ backgroundColor: "var(--warning-bg, rgba(245,158,11,0.1))", borderColor: "var(--warning, #f59e0b)", color: "var(--warning-text, #f59e0b)" }}>
              <strong>"{deleteTarget.name}"</strong> is currently assigned to{" "}
              <strong>{deleteTarget.assigned_melody_ids.length} {deleteTarget.assigned_melody_ids.length === 1 ? "melody" : "melodies"}</strong>.
              Deleting it will flag those melodies as <strong>outdated</strong>.
            </div>
            <p className="text-sm" style={{ color: "var(--text-muted)" }}>Do you still want to delete this archetype?</p>
            <div className="flex justify-end gap-3">
              <button onClick={() => { setDeleteTarget(null); setDeleteWarningConfirmed(false); }}
                className="px-4 py-2 text-sm rounded-md transition-colors"
                style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)" }}>Cancel</button>
              <button onClick={() => setDeleteWarningConfirmed(true)}
                className="px-4 py-2 text-sm rounded-md transition-colors font-medium"
                style={{ backgroundColor: "var(--danger)", color: "#fff" }}>
                Yes, Delete Anyway
              </button>
            </div>
          </div>
        </div>
      )}

      <ConfirmDialog
        open={Boolean(deleteTarget) && (deleteWarningConfirmed || (deleteTarget?.assigned_melody_ids?.length || 0) === 0)}
        title="Delete Archetype"
        message={`Are you sure you want to permanently delete "${deleteTarget?.name}"? This will also delete the .bsm binary file. This action cannot be undone.`}
        onConfirm={handleDelete}
        onCancel={() => { setDeleteTarget(null); setDeleteWarningConfirmed(false); }}
      />

      <CodeSnippetModal melody={codeSnippetMelody} onClose={() => setCodeSnippetMelody(null)} />

      <AssignedMelodiesModal
        archetype={assignedModal?.archetype}
        melodyDetails={assignedModal?.melodyDetails || []}
        primaryLang={primaryLang}
        onClose={() => setAssignedModal(null)}
      />

      <UploadArchetypeModal
        open={showUploadModal}
        existingArchetypes={archetypes}
        onClose={() => setShowUploadModal(false)}
        onUploaded={() => loadArchetypes()}
      />

      <GenerateBuiltinModal
        open={showGenerateModal}
        onClose={() => setShowGenerateModal(false)}
      />

      {/* Playback modal for archetypes */}
      <PlaybackModal
        open={Boolean(playbackArchetype)}
        melody={null}
        builtMelody={playbackArchetype}
        files={null}
        archetypeCsv={null}
        onClose={() => setPlaybackArchetype(null)}
      />
    </div>
  );
}
