import { useState, useEffect, useRef } from "react";
import { useNavigate, useParams } from "react-router-dom";
import api from "../api/client";
import { useAuth } from "../auth/AuthContext";
import TranslationModal from "./TranslationModal";
import SelectArchetypeModal from "./archetypes/SelectArchetypeModal";
import BuildOnTheFlyModal from "./archetypes/BuildOnTheFlyModal";
import SpeedCalculatorModal from "./SpeedCalculatorModal";
import PlaybackModal from "./PlaybackModal";
import BinaryTableModal from "./BinaryTableModal";
import {
  getLocalizedValue,
  getLanguageName,
  normalizeColor,
  formatDuration,
  parseLocalizedString,
  serializeLocalizedString,
} from "./melodyUtils";

const MELODY_TYPES = ["orthodox", "catholic", "all"];
const MELODY_TONES = ["normal", "festive", "cheerful", "lamentation"];

const defaultInfo = {
  name: "",
  description: "",
  melodyTone: "normal",
  customTags: [],
  minSpeed: 0,
  maxSpeed: 0,
  totalNotes: 1,
  totalActiveBells: 0,
  steps: 0,
  color: "",
  isTrueRing: false,
  previewURL: "",
};

const defaultSettings = {
  speed: 50,
  duration: 0,
  totalRunDuration: 0,
  pauseDuration: 0,
  infiniteLoop: false,
  echoRing: [],
  noteAssignments: [],
};

const labelStyle = { color: "var(--text-secondary)" };
const mutedStyle = { color: "var(--text-muted)" };

function formatBpm(ms) {
  const value = Number(ms);
  if (!value || value <= 0) return null;
  return Math.round(60000 / value);
}

function mapPercentageToStepDelay(percent, minSpeed, maxSpeed) {
  if (minSpeed == null || maxSpeed == null) return null;
  const p = Math.max(0, Math.min(100, Number(percent || 0)));
  const t = p / 100;
  const a = Number(minSpeed);
  const b = Number(maxSpeed);
  if (a <= 0 || b <= 0) return Math.round(a + (b - a) * t);
  return Math.round(a * Math.pow(b / a, t));
}

function normalizeFileUrl(url) {
  if (!url || typeof url !== "string") return null;
  if (url.startsWith("http") || url.startsWith("/api")) return url;
  if (url.startsWith("/")) return `/api${url}`;
  return `/api/${url}`;
}

export default function MelodyForm() {
  const { id } = useParams();
  const isEdit = Boolean(id);
  const navigate = useNavigate();
  const { user } = useAuth();

  const [information, setInformation] = useState({ ...defaultInfo });
  const [settings, setSettings] = useState({ ...defaultSettings });
  const [type, setType] = useState("all");
  const [url, setUrl] = useState("");
  const [uid, setUid] = useState("");
  const [pid, setPid] = useState("");
  const [melodyStatus, setMelodyStatus] = useState("draft");

  const [previewFile, setPreviewFile] = useState(null);
  const [existingFiles, setExistingFiles] = useState({});
  const [uploading, setUploading] = useState(false);

  const [loading, setLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState("");

  const [tagInput, setTagInput] = useState("");
  const [melodySettings, setMelodySettings] = useState(null);
  const [editLang, setEditLang] = useState("en");

  const [translationModal, setTranslationModal] = useState({
    open: false,
    field: "",
    fieldKey: "",
    multiline: false,
  });

  const [showSelectBuilt, setShowSelectBuilt] = useState(false);
  const [showBuildOnTheFly, setShowBuildOnTheFly] = useState(false);
  const [showSpeedCalc, setShowSpeedCalc] = useState(false);
  const [showPlayback, setShowPlayback] = useState(false);
  const [showBinaryView, setShowBinaryView] = useState(false);
  const [builtMelody, setBuiltMelody] = useState(null);
  const [assignedBinaryName, setAssignedBinaryName] = useState(null);
  const [assignedBinaryPid, setAssignedBinaryPid] = useState(null);
  const previewInputRef = useRef(null);

  // Metadata / Admin Notes
  const [adminNotes, setAdminNotes] = useState([]);
  const [newNote, setNewNote] = useState("");
  const [savedMelodyId, setSavedMelodyId] = useState(null);

  useEffect(() => {
    api.get("/settings/melody").then((ms) => {
      setMelodySettings(ms);
      setEditLang(ms.primary_language || "en");
    });
  }, []);

  useEffect(() => {
    if (isEdit) loadMelody();
  }, [id]);

  useEffect(() => {
    const count = information.totalNotes || 1;
    setSettings((prev) => {
      const current = [...(prev.noteAssignments || [])];
      if (current.length < count) {
        while (current.length < count) current.push(0);
      } else if (current.length > count) {
        current.length = count;
      }
      return { ...prev, noteAssignments: current };
    });
  }, [information.totalNotes]);

  const loadMelody = async () => {
    setLoading(true);
    try {
      const [melody, files] = await Promise.all([
        api.get(`/melodies/${id}`),
        api.get(`/melodies/${id}/files`),
      ]);
      setInformation({ ...defaultInfo, ...melody.information });
      setSettings({ ...defaultSettings, ...melody.default_settings });
      setType(melody.type || "all");
      setUrl(melody.url || "");
      setUid(melody.uid || "");
      setPid(melody.pid || "");
      setMelodyStatus(melody.status || "published");
      setExistingFiles(files);
      // Load admin notes from metadata
      setAdminNotes(melody.metadata?.adminNotes || []);
      // Load built melody assignment (non-fatal)
      try {
        const bm = await api.get(`/builder/melodies/for-melody/${id}`);
        setBuiltMelody(bm || null);
        setAssignedBinaryName(bm?.name || null);
        setAssignedBinaryPid(bm?.pid || null);
      } catch {
        setBuiltMelody(null);
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const updateInfo = (field, value) =>
    setInformation((prev) => ({ ...prev, [field]: value }));
  const updateSettings = (field, value) =>
    setSettings((prev) => ({ ...prev, [field]: value }));

  const addTag = () => {
    const tag = tagInput.trim();
    if (tag && !information.customTags.includes(tag))
      updateInfo("customTags", [...information.customTags, tag]);
    setTagInput("");
  };

  const removeTag = (tag) =>
    updateInfo("customTags", information.customTags.filter((t) => t !== tag));

  const parseIntList = (str) => {
    if (!str.trim()) return [];
    return str.split(",").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n));
  };
  const resolveFilename = (fileUrl, fallbackName) => {
    if (!fileUrl) return fallbackName;
    try {
      const path = decodeURIComponent(new URL(fileUrl, window.location.origin).pathname);
      const parts = path.split("/");
      return parts[parts.length - 1] || fallbackName;
    } catch {
      return fallbackName;
    }
  };

  const getEffectiveBinary = () => {
    const effectiveUrl = normalizeFileUrl(
      (builtMelody?.binary_url ? `/api${builtMelody.binary_url}` : null) ||
      url ||
      existingFiles.binary_url ||
      null
    );
    const fixedNameFromPid = builtMelody?.pid || assignedBinaryPid || pid || null;
    const effectiveName = fixedNameFromPid
      ? `${fixedNameFromPid}.bsm`
      : resolveFilename(effectiveUrl, "binary.bsm");
    return { effectiveUrl, effectiveName };
  };

  const downloadExistingFile = async (fileUrl, fallbackName, e, downloadEndpoint = null) => {
    e?.preventDefault?.();
    if (!fileUrl) return;
    try {
      const token = localStorage.getItem("access_token");
      const sourceUrl = downloadEndpoint || fileUrl;
      let res = null;
      try {
        res = await fetch(sourceUrl, {
          headers: token ? { Authorization: `Bearer ${token}` } : {},
        });
      } catch {
        if (fileUrl.startsWith("http")) {
          res = await fetch(fileUrl);
        } else {
          throw new Error("Download failed: network error");
        }
      }
      if ((!res || !res.ok) && fileUrl.startsWith("http")) {
        window.open(fileUrl, "_blank", "noopener,noreferrer");
        return;
      }
      if (!res.ok) throw new Error(`Download failed: ${res.statusText}`);
      const blob = await res.blob();
      const objectUrl = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = objectUrl;
      a.download = fallbackName || "download.file";
      a.click();
      URL.revokeObjectURL(objectUrl);
    } catch (err) {
      const a = document.createElement("a");
      a.href = fileUrl;
      a.download = fallbackName || "download.file";
      a.rel = "noopener noreferrer";
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    }
  };

  const updateLocalizedField = (fieldKey, text) => {
    const dict = parseLocalizedString(information[fieldKey]);
    dict[editLang] = text;
    updateInfo(fieldKey, serializeLocalizedString(dict));
  };

  // Compute totalActiveBells from the current noteAssignments (unique non-zero values)
  const computeTotalActiveBells = (assignments) => {
    const unique = new Set((assignments || []).filter((v) => v > 0));
    return unique.size;
  };

  const buildBody = (overrideMetadata) => {
    const { notes, ...infoWithoutNotes } = information;
    const totalActiveBells = computeTotalActiveBells(settings.noteAssignments);
    const now = new Date().toISOString();
    const userName = user?.name || "Unknown";
    const metadata = overrideMetadata || {
      dateEdited: now,
      lastEditedBy: userName,
      adminNotes,
    };
    const body = {
      information: { ...infoWithoutNotes, totalActiveBells },
      default_settings: settings,
      type, uid, pid,
      metadata,
    };
    // Only send url if non-empty — avoids overwriting a backend-set URL with an empty string
    if (url) body.url = url;
    return body;
  };

  const uploadFiles = async (melodyId) => {
    if (previewFile) {
      setUploading(true);
      if (previewFile) await api.upload(`/melodies/${melodyId}/upload/preview`, previewFile);
      setUploading(false);
    }
  };

  const handleSave = async (publish) => {
    setSaving(true);
    setError("");
    try {
      const now = new Date().toISOString();
      const userName = user?.name || "Unknown";
      let melodyId = id;

      if (isEdit) {
        const body = buildBody({ dateEdited: now, lastEditedBy: userName, adminNotes });
        await api.put(`/melodies/${id}`, body);
      } else if (savedMelodyId) {
        // Melody was already created (e.g. after using Select Archetype / Build on the Fly)
        // Update it instead of creating a duplicate
        melodyId = savedMelodyId;
        const body = buildBody({ dateEdited: now, lastEditedBy: userName, adminNotes });
        await api.put(`/melodies/${savedMelodyId}`, body);
        if (publish) {
          await api.post(`/melodies/${savedMelodyId}/publish`);
        }
      } else {
        const body = buildBody({ dateCreated: now, dateEdited: now, createdBy: userName, lastEditedBy: userName, adminNotes });
        const created = await api.post(`/melodies?publish=${publish}`, body);
        melodyId = created.id;
        setSavedMelodyId(melodyId);
      }

      await uploadFiles(melodyId);
      navigate(`/melodies/${melodyId}`);
    } catch (err) {
      setError(err.message);
      setUploading(false);
    } finally {
      setSaving(false);
    }
  };

  const handlePublishAction = async () => {
    setSaving(true);
    setError("");
    try {
      const now = new Date().toISOString();
      const userName = user?.name || "Unknown";
      const body = buildBody({ dateEdited: now, lastEditedBy: userName, adminNotes });
      await api.put(`/melodies/${id}`, body);
      await uploadFiles(id);
      await api.post(`/melodies/${id}/publish`);
      navigate(`/melodies/${id}`);
    } catch (err) {
      setError(err.message);
      setUploading(false);
    } finally {
      setSaving(false);
    }
  };

  const handleUnpublishAction = async () => {
    setSaving(true);
    setError("");
    try {
      await api.post(`/melodies/${id}/unpublish`);
      navigate(`/melodies/${id}`);
    } catch (err) {
      setError(err.message);
    } finally {
      setSaving(false);
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    // Default form submit: save as draft for new, update for existing
    if (isEdit) {
      await handleSave(false);
    } else {
      await handleSave(false);
    }
  };

  if (loading) {
    return <div className="text-center py-8" style={mutedStyle}>Loading...</div>;
  }

  const languages = melodySettings?.available_languages || ["en"];
  const durationValues = melodySettings?.duration_values || [0];
  const quickColors = melodySettings?.quick_colors || [];

  const durationIndex = durationValues.indexOf(settings.duration);
  const currentDurationIdx = durationIndex >= 0 ? durationIndex : 0;
  const speedMs = mapPercentageToStepDelay(settings.speed, information.minSpeed, information.maxSpeed);
  const speedBpm = formatBpm(speedMs);
  const minBpm = formatBpm(information.minSpeed);
  const maxBpm = formatBpm(information.maxSpeed);

  const inputClass = "w-full px-3 py-2 rounded-md text-sm border";

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <h1 className="text-2xl font-bold" style={{ color: "var(--text-heading)" }}>
          {isEdit ? "Edit Melody" : "Add Melody"}
        </h1>
        <div className="flex items-center gap-2">
          {isEdit ? (
            <>
              <button
                type="button"
                onClick={() => setShowPlayback(true)}
                className="px-4 py-2 text-sm rounded-md transition-colors"
                style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
              >
                Playback
              </button>
              <button
                type="button"
                onClick={() => setShowSpeedCalc(true)}
                className="px-4 py-2 text-sm rounded-md transition-colors"
                style={{ backgroundColor: "var(--btn-neutral)", color: "var(--text-white)" }}
              >
                Speed Calculator
              </button>
              <div className="w-px h-6 mx-1" style={{ backgroundColor: "var(--border-primary)" }} />
              {melodyStatus === "draft" ? (
                <>
                  <button
                    type="submit"
                    form="melody-form"
                    disabled={saving || uploading}
                    className="px-4 py-2 text-sm rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                    style={{ backgroundColor: "var(--btn-neutral)", color: "var(--text-white)" }}
                  >
                    {saving ? "Saving..." : "Update Melody"}
                  </button>
                  <button
                    type="button"
                    disabled={saving || uploading}
                    onClick={handlePublishAction}
                    className="px-4 py-2 text-sm rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                    style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
                  >
                    {uploading ? "Uploading..." : "Publish"}
                  </button>
                </>
              ) : (
                <>
                  <button
                    type="submit"
                    form="melody-form"
                    disabled={saving || uploading}
                    className="px-4 py-2 text-sm rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                    style={{ backgroundColor: "var(--btn-neutral)", color: "var(--text-white)" }}
                  >
                    {uploading ? "Uploading..." : saving ? "Saving..." : "Update Melody"}
                  </button>
                  <button
                    type="button"
                    disabled={saving || uploading}
                    onClick={handleUnpublishAction}
                    className="px-4 py-2 text-sm rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                    style={{ backgroundColor: "var(--danger-btn)", color: "var(--text-white)" }}
                  >
                    Unpublish
                  </button>
                </>
              )}
              <button
                type="button"
                onClick={() => navigate(`/melodies/${id}`)}
                className="px-4 py-2 text-sm rounded-md transition-colors"
                style={{ backgroundColor: "var(--btn-neutral)", color: "var(--text-white)" }}
              >
                Cancel
              </button>
            </>
          ) : (
            <>
              <button
                type="button"
                onClick={async () => {
                  if (savedMelodyId) {
                    try { await api.delete(`/melodies/${savedMelodyId}`); } catch { /* best-effort */ }
                  }
                  navigate("/melodies");
                }}
                className="px-4 py-2 text-sm rounded-md transition-colors"
                style={{ backgroundColor: "var(--btn-neutral)", color: "var(--text-white)" }}
              >
                Cancel
              </button>
              <div className="w-px h-6 mx-1" style={{ backgroundColor: "var(--border-primary)" }} />
              <button
                type="submit"
                form="melody-form"
                disabled={saving || uploading}
                className="px-4 py-2 text-sm rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                style={{ backgroundColor: "var(--btn-neutral)", color: "var(--text-white)" }}
              >
                {saving ? "Saving..." : "Save Draft"}
              </button>
              <button
                type="button"
                disabled={saving || uploading}
                onClick={() => handleSave(true)}
                className="px-4 py-2 text-sm rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
              >
                {uploading ? "Uploading..." : "Publish Melody"}
              </button>
            </>
          )}
        </div>
      </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>
      )}

      <form id="melody-form" onSubmit={handleSubmit}>
        <div className="grid grid-cols-1 xl:grid-cols-2 gap-6">
          {/* ===== Left Column ===== */}
          <div className="space-y-6">
            {/* --- Melody Info Section --- */}
            <section className="ui-section-card">
              <div className="ui-section-card__title-row">
                <h2 className="ui-section-card__title">Melody Information</h2>
              </div>
              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                {/* Name (localized) */}
                <div className="md:col-span-2">
                  <div className="flex items-center gap-2 mb-1">
                    <label className="ui-form-label">Name *</label>
                    <button
                      type="button"
                      onClick={() => setTranslationModal({ open: true, field: "Name", fieldKey: "name", multiline: false })}
                      className="transition-colors" style={mutedStyle} title="Edit translations"
                    >
                      <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
                      </svg>
                    </button>
                    {languages.length > 1 && (
                      <select value={editLang} onChange={(e) => setEditLang(e.target.value)} className="ml-auto text-xs px-2 py-1 rounded border">
                        {languages.map((l) => (<option key={l} value={l}>{getLanguageName(l)}</option>))}
                      </select>
                    )}
                  </div>
                  <input type="text" required value={getLocalizedValue(information.name, editLang, "")} onChange={(e) => updateLocalizedField("name", e.target.value)} className={inputClass} />
                </div>

                {/* Description (localized) */}
                <div className="md:col-span-2">
                  <div className="flex items-center gap-2 mb-1">
                    <label className="ui-form-label">Description</label>
                    <button
                      type="button"
                      onClick={() => setTranslationModal({ open: true, field: "Description", fieldKey: "description", multiline: true })}
                      className="transition-colors" style={mutedStyle} title="Edit translations"
                    >
                      <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
                      </svg>
                    </button>
                  </div>
                  <textarea value={getLocalizedValue(information.description, editLang, "")} onChange={(e) => updateLocalizedField("description", e.target.value)} rows={3} className={inputClass} />
                </div>

                <div>
                  <label className="ui-form-label">Melody Tone</label>
                  <select value={information.melodyTone} onChange={(e) => updateInfo("melodyTone", e.target.value)} className={inputClass}>
                    {MELODY_TONES.map((t) => (<option key={t} value={t}>{t.charAt(0).toUpperCase() + t.slice(1)}</option>))}
                  </select>
                </div>

                <div>
                  <label className="ui-form-label">Type</label>
                  <select value={type} onChange={(e) => setType(e.target.value)} className={inputClass}>
                    {MELODY_TYPES.map((t) => (<option key={t} value={t}>{t.charAt(0).toUpperCase() + t.slice(1)}</option>))}
                  </select>
                </div>

                <div>
                  <label className="ui-form-label">Total Archetype Notes</label>
                  <input type="number" min="1" max="16" value={information.totalNotes} onChange={(e) => { const val = parseInt(e.target.value, 10); updateInfo("totalNotes", Math.max(1, Math.min(16, val || 1))); }} className={inputClass} />
                </div>

                <div>
                  <label className="ui-form-label">Steps</label>
                  <input type="number" min="0" value={information.steps} onChange={(e) => updateInfo("steps", parseInt(e.target.value, 10) || 0)} className={inputClass} />
                </div>

                <div>
                  <label className="ui-form-label">Min Speed</label>
                  <input type="number" min="0" value={information.minSpeed} onChange={(e) => updateInfo("minSpeed", parseInt(e.target.value, 10) || 0)} className={inputClass} />
                  {information.minSpeed > 0 && (
                    <p className="text-xs mt-1" style={mutedStyle}>{minBpm} bpm · {information.minSpeed} ms</p>
                  )}
                </div>

                <div>
                  <label className="ui-form-label">Max Speed</label>
                  <input type="number" min="0" value={information.maxSpeed} onChange={(e) => updateInfo("maxSpeed", parseInt(e.target.value, 10) || 0)} className={inputClass} />
                  {information.maxSpeed > 0 && (
                    <p className="text-xs mt-1" style={mutedStyle}>{maxBpm} bpm · {information.maxSpeed} ms</p>
                  )}
                </div>

                {/* Color */}
                <div className="md:col-span-2">
                  <label className="ui-form-label">Color</label>
                  <div className="flex items-center gap-2 mb-2">
                    <span className="w-8 h-8 rounded flex-shrink-0 border" style={{ backgroundColor: information.color ? normalizeColor(information.color) : "transparent", borderColor: "var(--border-primary)" }} />
                    <input type="text" value={information.color} onChange={(e) => updateInfo("color", e.target.value)} placeholder="e.g. #FF5733 or 0xFF5733" className="flex-1 px-3 py-2 rounded-md text-sm border" />
                  </div>
                  <div className="flex flex-wrap gap-2 items-center">
                    {quickColors.map((color) => (
                      <button key={color} type="button" onClick={() => updateInfo("color", color)}
                        className="w-7 h-7 rounded-md border-2 transition-all cursor-pointer"
                        style={{ backgroundColor: normalizeColor(color), borderColor: information.color === color ? "var(--accent)" : "var(--border-secondary)" }}
                        title={color}
                      />
                    ))}
                    <label className="relative inline-flex items-center gap-1.5 px-3 py-1.5 border-2 border-dashed rounded-md text-xs transition-colors cursor-pointer" style={{ borderColor: "var(--border-primary)", color: "var(--text-muted)" }} title="Pick a custom color">
                      <svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
                      </svg>
                      Custom
                      <input type="color" value={normalizeColor(information.color)} onChange={(e) => updateInfo("color", e.target.value)} className="absolute inset-0 opacity-0 w-full h-full cursor-pointer" />
                    </label>
                  </div>
                </div>
                <div className="flex items-center gap-2 pt-2">
                  <input type="checkbox" id="isTrueRing" checked={information.isTrueRing} onChange={(e) => updateInfo("isTrueRing", e.target.checked)} className="h-4 w-4 rounded" />
                  <label htmlFor="isTrueRing" className="text-sm font-medium" style={labelStyle}>True Ring</label>
                </div>

                <div className="md:col-span-2">
                  <label className="ui-form-label">Custom Tags</label>
                  <div className="flex gap-2 mb-2">
                    <input type="text" value={tagInput} onChange={(e) => setTagInput(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); addTag(); } }} placeholder="Add a tag and press Enter" className="flex-1 px-3 py-2 rounded-md text-sm border" />
                    <button type="button" onClick={addTag} className="px-3 py-2 text-sm rounded-md transition-colors" style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)" }}>Add</button>
                  </div>
                  {information.customTags.length > 0 && (
                    <div className="flex flex-wrap gap-2">
                      {information.customTags.map((tag) => (
                        <span key={tag} className="inline-flex items-center gap-1 px-2 py-1 text-xs rounded-full" style={{ backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" }}>
                          {tag}
                          <button type="button" onClick={() => removeTag(tag)} style={{ color: "var(--badge-blue-text)" }}>&times;</button>
                        </span>
                      ))}
                    </div>
                  )}
                </div>
              </div>
            </section>

            {/* --- Identifiers Section --- */}
            <section className="ui-section-card">
              <div className="ui-section-card__title-row">
                <h2 className="ui-section-card__title">Identifiers</h2>
              </div>
              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div>
                  <label className="ui-form-label">PID (Playback ID)</label>
                  <input type="text" value={pid} onChange={(e) => setPid(e.target.value)} placeholder="eg. builtin_festive_vesper" className={inputClass} />
                </div>
                {url && (
                  <div className="md:col-span-2">
                    <label className="ui-form-label">URL (auto-set from binary upload)</label>
                    <input type="text" value={url} readOnly className={inputClass} style={{ opacity: 0.7 }} />
                  </div>
                )}
              </div>
            </section>
          </div>

          {/* ===== Right Column ===== */}
          <div className="space-y-6">
            {/* --- Default Settings Section --- */}
            <section className="ui-section-card">
              <div className="ui-section-card__title-row">
                <h2 className="ui-section-card__title">Default Settings</h2>
              </div>
              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div className="md:col-span-2">
                  <label className="ui-form-label">Speed</label>
                  <div className="flex items-center gap-3">
                    <input type="range" min="1" max="100" value={settings.speed} onChange={(e) => updateSettings("speed", parseInt(e.target.value, 10))} className="flex-1 h-2 rounded-lg appearance-none cursor-pointer" />
                    <span className="text-sm font-medium w-12 text-right" style={labelStyle}>{settings.speed}%</span>
                  </div>
                  <div className="text-xs mt-1" style={mutedStyle}>
                    {speedBpm && speedMs ? `${speedBpm} bpm · ${speedMs} ms` : "Set MIN/MAX speed to compute bpm"}
                  </div>
                </div>

                <div className="md:col-span-2">
                  <label className="ui-form-label">Duration</label>
                  <div className="flex items-center gap-3">
                    <input type="range" min="0" max={Math.max(0, durationValues.length - 1)} value={currentDurationIdx} onChange={(e) => updateSettings("duration", durationValues[parseInt(e.target.value, 10)] ?? 0)} className="flex-1 h-2 rounded-lg appearance-none cursor-pointer" />
                    <span className="text-sm font-medium w-24 text-right" style={labelStyle}>{formatDuration(settings.duration)}</span>
                  </div>
                  <div className="text-xs mt-1" style={mutedStyle}>{settings.duration}s</div>
                </div>

                <div>
                  <label className="ui-form-label">Total Run Duration</label>
                  <input type="number" min="0" value={settings.totalRunDuration} onChange={(e) => updateSettings("totalRunDuration", parseInt(e.target.value, 10) || 0)} className={inputClass} />
                </div>

                <div>
                  <label className="ui-form-label">Pause Duration</label>
                  <input type="number" min="0" value={settings.pauseDuration} onChange={(e) => updateSettings("pauseDuration", parseInt(e.target.value, 10) || 0)} className={inputClass} />
                </div>

                <div className="flex items-center gap-2 pt-6">
                  <input type="checkbox" id="infiniteLoop" checked={settings.infiniteLoop} onChange={(e) => updateSettings("infiniteLoop", e.target.checked)} className="h-4 w-4 rounded" />
                  <label htmlFor="infiniteLoop" className="text-sm font-medium" style={labelStyle}>Infinite Loop</label>
                </div>

                <div className="md:col-span-2">
                  <label className="ui-form-label">Echo Ring (comma-separated integers)</label>
                  <input type="text" value={settings.echoRing.join(", ")} onChange={(e) => updateSettings("echoRing", parseIntList(e.target.value))} placeholder="e.g. 0, 1, 0, 1" className={inputClass} />
                </div>

                <div className="md:col-span-2">
                  <div className="flex items-center justify-between mb-2">
                    <label className="ui-form-label">Note Assignments</label>
                    <span className="text-xs px-2 py-0.5 rounded-full" style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-muted)" }}>
                      {computeTotalActiveBells(settings.noteAssignments)} active bell{computeTotalActiveBells(settings.noteAssignments) !== 1 ? "s" : ""}
                    </span>
                  </div>
                  <div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
                    {Array.from({ length: information.totalNotes }, (_, i) => (
                      <div key={i}>
                        <label className="block text-xs mb-0.5 text-left" style={mutedStyle}>Note #{String.fromCharCode(65 + i)}</label>
                        <input type="number" min="0" required value={settings.noteAssignments[i] ?? 0}
                          onChange={(e) => { const na = [...settings.noteAssignments]; while (na.length <= i) na.push(0); na[i] = parseInt(e.target.value, 10) || 0; updateSettings("noteAssignments", na); }}
                          className="w-full px-2 py-1.5 rounded-md text-sm text-center border"
                        />
                      </div>
                    ))}
                  </div>
                  <p className="text-xs mt-1" style={mutedStyle}>Assign which bell rings for each note (0 = none). Total Active Bells = unique assigned values.</p>
                </div>
              </div>
            </section>

            {/* --- File Upload Section --- */}
            <section className="ui-section-card">
              <div className="ui-section-card__title-row">
                <h2 className="ui-section-card__title">Files</h2>
              </div>
              <div className="space-y-4">
                <div>
                  <label className="ui-form-label">Binary File (.bsm)</label>
                  {(() => {
                    const { effectiveUrl: binaryUrl, effectiveName: binaryName } = getEffectiveBinary();
                    const missingArchetype = Boolean(pid) && !builtMelody?.id;
                    return (
                      <div className="text-xs mb-2" style={{ color: "var(--text-muted)" }}>
                        {binaryUrl ? (
                          <span className="inline-flex items-center gap-2 text-sm mr-4" style={{ color: "var(--text-secondary)" }}>
                            <span>{binaryName}</span>
                            {missingArchetype && (
                              <span
                                className="text-xs cursor-help"
                                style={{ color: "#f59e0b" }}
                                title="This binary does not exist in the Archetypes"
                              >
                                ⚠
                              </span>
                            )}
                          </span>
                        ) : (
                          <span>No binary uploaded</span>
                        )}
                        {binaryUrl && (
                          <div className="inline-flex items-center gap-2.5">
                            <button
                              type="button"
                              onClick={(e) =>
                                downloadExistingFile(
                                  binaryUrl,
                                  binaryName,
                                  e,
                                  (id || savedMelodyId) ? `/api/melodies/${id || savedMelodyId}/download/binary` : null
                                )
                              }
                              className="px-2 py-0.5 text-xs rounded-full"
                              style={{ color: "var(--text-link)", backgroundColor: "rgba(88,156,250,0.14)" }}
                            >
                              Download
                            </button>
                            <button
                              type="button"
                              onClick={() => setShowBinaryView(true)}
                              className="px-2 py-0.5 text-xs rounded-full"
                              style={{ color: "var(--text-secondary)", backgroundColor: "var(--bg-card-hover)" }}
                            >
                              View
                            </button>
                            <span style={{ color: "var(--text-muted)" }}>{information.totalNotes ?? 0} active notes</span>
                          </div>
                        )}
                      </div>
                    );
                  })()}
                  <div className="flex flex-wrap gap-2 items-center">
                    <button
                      type="button"
                      onClick={async () => {
                        if (!isEdit && !savedMelodyId) {
                          setSaving(true); setError("");
                          try {
                            const now = new Date().toISOString();
                            const userName = user?.name || "Unknown";
                            const body = buildBody({ dateCreated: now, dateEdited: now, createdBy: userName, lastEditedBy: userName, adminNotes });
                            const created = await api.post(`/melodies?publish=false`, body);
                            setSavedMelodyId(created.id);
                          } catch (err) { setError(err.message); setSaving(false); return; }
                          setSaving(false);
                        }
                        setShowSelectBuilt(true);
                      }}
                      className="px-3 py-1.5 text-xs rounded-md transition-colors"
                      style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)", border: "1px solid var(--border-primary)" }}
                    >
                      Select Archetype
                    </button>
                    <button
                      type="button"
                      onClick={async () => {
                        if (!isEdit && !savedMelodyId) {
                          setSaving(true); setError("");
                          try {
                            const now = new Date().toISOString();
                            const userName = user?.name || "Unknown";
                            const body = buildBody({ dateCreated: now, dateEdited: now, createdBy: userName, lastEditedBy: userName, adminNotes });
                            const created = await api.post(`/melodies?publish=false`, body);
                            setSavedMelodyId(created.id);
                          } catch (err) { setError(err.message); setSaving(false); return; }
                          setSaving(false);
                        }
                        setShowBuildOnTheFly(true);
                      }}
                      className="px-3 py-1.5 text-xs rounded-md transition-colors"
                      style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)", border: "1px solid var(--border-primary)" }}
                    >
                      Build on the Fly
                    </button>
                  </div>
                </div>
                <div>
                  <label className="ui-form-label">Audio Preview (.mp3)</label>
                  {normalizeFileUrl(existingFiles.preview_url) ? (
                    <div className="mb-2 space-y-1">
                      {(() => {
                        const previewUrl = normalizeFileUrl(existingFiles.preview_url);
                        const previewName = resolveFilename(previewUrl, "preview.mp3");
                        return (
                      <a
                        href={previewUrl}
                        onClick={(e) => downloadExistingFile(previewUrl, previewName, e)}
                        className="underline text-xs"
                        style={{ color: "var(--accent)" }}
                      >
                        {resolveFilename(previewUrl, "Click to Download")}
                      </a>
                        );
                      })()}
                      <audio controls src={normalizeFileUrl(existingFiles.preview_url)} className="h-8" />
                    </div>
                  ) : (
                    <p className="text-xs mb-2" style={mutedStyle}>No preview uploaded</p>
                  )}
                  <input
                    ref={previewInputRef}
                    type="file"
                    accept=".mp3,.wav,.ogg"
                    onChange={(e) => setPreviewFile(e.target.files[0] || null)}
                    className="hidden"
                  />
                  <button
                    type="button"
                    onClick={() => previewInputRef.current?.click()}
                    className="px-3 py-1.5 text-xs rounded-md transition-colors"
                    style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)", border: "1px solid var(--border-primary)" }}
                  >
                    Choose Preview File
                  </button>
                  {previewFile && (
                    <p className="text-xs mt-1" style={mutedStyle}>
                      selected: {previewFile.name}
                    </p>
                  )}
                </div>
              </div>
            </section>
          </div>
        </div>

        {/* --- Admin Notes Section --- */}
        <section className="ui-section-card mt-6">
          <div className="ui-section-card__title-row">
            <h2 className="ui-section-card__title">Admin Notes</h2>
          </div>
          <div className="space-y-3">
            {adminNotes.map((note, i) => (
              <div key={i} className="flex items-start gap-3 rounded-lg p-3 border" style={{ borderColor: "var(--border-primary)", backgroundColor: "var(--bg-primary)" }}>
                <p className="flex-1 text-sm" style={{ color: "var(--text-primary)" }}>{note}</p>
                <button
                  type="button"
                  onClick={() => setAdminNotes((prev) => prev.filter((_, j) => j !== i))}
                  className="text-xs px-2 py-0.5 rounded transition-colors flex-shrink-0"
                  style={{ color: "var(--danger)", backgroundColor: "var(--danger-bg)" }}
                >
                  Remove
                </button>
              </div>
            ))}
            <div className="flex gap-2">
              <input
                type="text"
                value={newNote}
                onChange={(e) => setNewNote(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === "Enter") {
                    e.preventDefault();
                    const trimmed = newNote.trim();
                    if (trimmed) { setAdminNotes((prev) => [...prev, trimmed]); setNewNote(""); }
                  }
                }}
                placeholder="Add a note and press Enter or click Add"
                className="flex-1 px-3 py-2 rounded-md text-sm border"
              />
              <button
                type="button"
                onClick={() => {
                  const trimmed = newNote.trim();
                  if (trimmed) { setAdminNotes((prev) => [...prev, trimmed]); setNewNote(""); }
                }}
                className="px-3 py-2 text-sm rounded-md transition-colors"
                style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)" }}
              >
                Add
              </button>
            </div>
          </div>
        </section>
      </form>

      <TranslationModal
        open={translationModal.open}
        onClose={() => setTranslationModal((prev) => ({ ...prev, open: false }))}
        field={translationModal.field}
        value={parseLocalizedString(information[translationModal.fieldKey])}
        onChange={(updated) => updateInfo(translationModal.fieldKey, serializeLocalizedString(updated))}
        languages={languages}
        multiline={translationModal.multiline}
      />

      <PlaybackModal
        open={showPlayback}
        melody={{ id: id || savedMelodyId || "preview", information, default_settings: settings, type, url, uid, pid }}
        builtMelody={builtMelody}
        files={{ binary_url: getEffectiveBinary().effectiveUrl }}
        archetypeCsv={information.archetype_csv || null}
        onClose={() => setShowPlayback(false)}
      />
      <BinaryTableModal
        open={showBinaryView}
        melody={{ id: id || savedMelodyId || "preview", information, default_settings: settings, type, url, uid, pid }}
        builtMelody={builtMelody}
        files={{ binary_url: getEffectiveBinary().effectiveUrl }}
        archetypeCsv={information.archetype_csv || null}
        onClose={() => setShowBinaryView(false)}
      />

      {isEdit && (
        <>
          <SpeedCalculatorModal
            open={showSpeedCalc}
            melody={{ id, information, default_settings: settings, type, url, uid, pid }}
            builtMelody={builtMelody}
            archetypeCsv={information.archetype_csv || null}
            onClose={() => setShowSpeedCalc(false)}
            onSaved={() => { setShowSpeedCalc(false); loadMelody(); }}
          />
        </>
      )}

      {/* Archetype modals — available for both new and edit (need a melodyId) */}
      {(isEdit || savedMelodyId) && (
        <>
          <SelectArchetypeModal
            open={showSelectBuilt}
            melodyId={id || savedMelodyId}
            currentMelody={{ information, default_settings: settings, type, url, uid, pid }}
            currentBuiltMelody={builtMelody}
            onClose={() => setShowSelectBuilt(false)}
            onSuccess={(archetype) => {
              setShowSelectBuilt(false);
              setAssignedBinaryName(archetype.name);
              setAssignedBinaryPid(archetype.pid || null);
              if (!pid.trim() && archetype.pid) setPid(archetype.pid);
              if (archetype.steps != null) updateInfo("steps", archetype.steps);
              if (archetype.totalNotes != null) updateInfo("totalNotes", archetype.totalNotes);
              if (archetype.archetype_csv != null) updateInfo("archetype_csv", archetype.archetype_csv);
              updateInfo("outdated_archetype", false);
              if (isEdit) {
                loadMelody();
              } else {
                // Refresh files so binary_url and url are shown
                const mid = savedMelodyId;
                if (mid) {
                  Promise.all([
                    api.get(`/melodies/${mid}/files`),
                    api.get(`/melodies/${mid}`),
                    api.get(`/builder/melodies/for-melody/${mid}`),
                  ]).then(([files, m, bm]) => {
                    setExistingFiles(files);
                    if (m.url) setUrl(m.url);
                    setBuiltMelody(bm || null);
                  }).catch(() => {});
                }
              }
            }}
          />
          <BuildOnTheFlyModal
            open={showBuildOnTheFly}
            melodyId={id || savedMelodyId}
            currentMelody={{ information, default_settings: settings, type, url, uid, pid }}
            currentBuiltMelody={builtMelody}
            defaultName={getLocalizedValue(information.name, "en", "") || getLocalizedValue(information.name, editLang, "")}
            defaultPid={pid}
            onClose={() => setShowBuildOnTheFly(false)}
            onSuccess={(archetype) => {
              setShowBuildOnTheFly(false);
              setAssignedBinaryName(archetype.name);
              setAssignedBinaryPid(archetype.pid || null);
              if (!pid.trim() && archetype.pid) setPid(archetype.pid);
              if (archetype.steps != null) updateInfo("steps", archetype.steps);
              if (archetype.totalNotes != null) updateInfo("totalNotes", archetype.totalNotes);
              if (archetype.archetype_csv != null) updateInfo("archetype_csv", archetype.archetype_csv);
              updateInfo("outdated_archetype", false);
              if (isEdit) {
                loadMelody();
              } else {
                const mid = savedMelodyId;
                if (mid) {
                  Promise.all([
                    api.get(`/melodies/${mid}/files`),
                    api.get(`/melodies/${mid}`),
                    api.get(`/builder/melodies/for-melody/${mid}`),
                  ]).then(([files, m, bm]) => {
                    setExistingFiles(files);
                    if (m.url) setUrl(m.url);
                    setBuiltMelody(bm || null);
                  }).catch(() => {});
                }
              }
            }}
          />
        </>
      )}
    </div>
  );
}





