(() => {
  const el = (id) => document.getElementById(id);
  const t = (key, vars) => window.I18n?.t(key, vars) ?? key;

  const listEl = el("soundList");
  const emptyEl = el("soundListEmpty");
  const createBtnEl = el("soundCreateBtn");
  const dialogEl = el("soundEditDialog");
  const formEl = el("soundEditForm");
  const titleEl = el("soundEditTitle");
  const nameEl = el("soundEditName");
  const descEl = el("soundEditDescription");
  const enabledEl = el("soundEditEnabled");
  const fileMetaEl = el("soundEditFileMeta");
  const uploadInputEl = el("soundEditUploadInput");
  const uploadBtnEl = el("soundEditUploadBtn");
  const playBtnEl = el("soundEditPlayBtn");
  const deleteBtnEl = el("soundEditDeleteBtn");

  const store = {
    sounds: [],
    editingId: null,
    previewAudio: null,
  };

  function canWrite() {
    if (!window.AuthApp?.canWrite) return true;
    return window.AuthApp.canWrite("integrations");
  }

  function escapeHtml(str) {
    return String(str)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
  }

  async function apiJson(url, opts = {}) {
    if (window.AuthApp && !window.AuthApp.isReady()) {
      throw new Error("not authenticated");
    }
    const res = await fetch(url, { credentials: "include", ...opts });
    const text = await res.text();
    let data = null;
    try {
      data = text ? JSON.parse(text) : null;
    } catch {
      data = null;
    }
    if (!res.ok) {
      const msg = (data && data.error) || text || res.statusText;
      throw new Error(msg);
    }
    return data;
  }

  async function refreshSounds() {
    const data = await apiJson("/api/sounds");
    store.sounds = Array.isArray(data.sounds) ? data.sounds : [];
  }

  function formatDuration(ms) {
    if (!Number.isFinite(Number(ms))) return "—";
    const sec = Math.round(Number(ms) / 100) / 10;
    return `${sec}s`;
  }

  function renderList() {
    if (!listEl) return;
    listEl.innerHTML = "";
    if (emptyEl) emptyEl.hidden = store.sounds.length > 0;

    store.sounds.forEach((sound) => {
      const row = document.createElement("div");
      row.className = "missionListItem soundListItem";
      const hasFile = !!sound.file_name;
      row.innerHTML = `
        <div>
          <div class="missionListItemTitle">${escapeHtml(sound.name || sound.id)}</div>
          <div class="missionListItemMeta">
            ${sound.enabled === false ? t("common.disabled") : t("common.enabled")}
            · ${hasFile ? escapeHtml(sound.file_name) : t("sounds.noFile")}
            ${sound.duration_ms != null ? ` · ${formatDuration(sound.duration_ms)}` : ""}
          </div>
        </div>
        <div class="missionListItemActions">
          <button type="button" class="btn subtle soundPlayBtn" data-id="${escapeHtml(sound.id)}" ${hasFile ? "" : "disabled"}>${t("sounds.play")}</button>
          <button type="button" class="btn subtle soundEditBtn" data-id="${escapeHtml(sound.id)}">${t("common.edit")}</button>
        </div>
      `;
      listEl.appendChild(row);
    });

    listEl.querySelectorAll(".soundEditBtn").forEach((btn) => {
      btn.addEventListener("click", () => openDialog(btn.dataset.id));
    });
    listEl.querySelectorAll(".soundPlayBtn").forEach((btn) => {
      btn.addEventListener("click", () => playSound(btn.dataset.id));
    });
  }

  function stopPreview() {
    if (store.previewAudio) {
      store.previewAudio.pause();
      store.previewAudio = null;
    }
  }

  function playSound(id) {
    stopPreview();
    const audio = new Audio(`/api/sounds/${encodeURIComponent(id)}/file`);
    store.previewAudio = audio;
    audio.play().catch(() => alert(t("sounds.playFailed")));
  }

  function updateFileMeta(sound) {
    if (!fileMetaEl) return;
    if (sound?.file_name) {
      fileMetaEl.textContent = t("sounds.fileMeta", {
        name: sound.file_name,
        duration: formatDuration(sound.duration_ms),
      });
    } else {
      fileMetaEl.textContent = t("sounds.noFile");
    }
    if (playBtnEl) playBtnEl.disabled = !sound?.file_name;
  }

  function openDialog(id = null) {
    store.editingId = id;
    const existing = id ? store.sounds.find((s) => s.id === id) : null;
    if (titleEl) {
      titleEl.textContent = existing ? t("sounds.editTitle") : t("sounds.createTitle");
    }
    if (nameEl) nameEl.value = existing?.name || "";
    if (descEl) descEl.value = existing?.description || "";
    if (enabledEl) enabledEl.checked = existing?.enabled !== false;
    updateFileMeta(existing);
    if (deleteBtnEl) deleteBtnEl.hidden = !existing || !canWrite();
    if (uploadBtnEl) uploadBtnEl.disabled = !canWrite();
    if (nameEl) nameEl.readOnly = !canWrite();
    if (descEl) descEl.readOnly = !canWrite();
    if (enabledEl) enabledEl.disabled = !canWrite();
    dialogEl?.showModal();
  }

  async function saveDialog() {
    if (!canWrite()) return;
    const name = nameEl?.value.trim() || "";
    if (!name) {
      alert(t("sounds.nameRequired"));
      return;
    }
    const payload = {
      name,
      description: descEl?.value.trim() || "",
      enabled: enabledEl?.checked !== false,
    };
    try {
      if (store.editingId) {
        await apiJson(`/api/sounds/${encodeURIComponent(store.editingId)}`, {
          method: "PUT",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
        });
      } else {
        const created = await apiJson("/api/sounds", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
        });
        store.editingId = created.id;
      }
      await refreshSounds();
      renderList();
      const updated = store.sounds.find((s) => s.id === store.editingId);
      updateFileMeta(updated);
      if (!uploadInputEl?.files?.length) {
        dialogEl?.close();
      }
    } catch (e) {
      alert(e.message);
    }
  }

  async function uploadFile() {
    if (!canWrite() || !store.editingId) return;
    const file = uploadInputEl?.files?.[0];
    if (!file) return;
    const form = new FormData();
    form.append("file", file);
    try {
      await apiJson(`/api/sounds/${encodeURIComponent(store.editingId)}/file`, {
        method: "POST",
        body: form,
      });
      uploadInputEl.value = "";
      await refreshSounds();
      renderList();
      updateFileMeta(store.sounds.find((s) => s.id === store.editingId));
    } catch (e) {
      alert(e.message);
    }
  }

  async function deleteSound() {
    if (!canWrite() || !store.editingId) return;
    if (!confirm(t("sounds.deleteConfirm"))) return;
    try {
      await apiJson(`/api/sounds/${encodeURIComponent(store.editingId)}`, { method: "DELETE" });
      dialogEl?.close();
      store.editingId = null;
      await refreshSounds();
      renderList();
    } catch (e) {
      alert(e.message);
    }
  }

  function bindEvents() {
    createBtnEl?.addEventListener("click", () => {
      if (!canWrite()) return;
      openDialog(null);
    });
    formEl?.addEventListener("submit", (evt) => {
      evt.preventDefault();
      saveDialog();
    });
    el("soundEditCancelBtn")?.addEventListener("click", () => {
      stopPreview();
      dialogEl?.close();
    });
    dialogEl?.addEventListener("cancel", (evt) => {
      evt.preventDefault();
      stopPreview();
      dialogEl?.close();
    });
    uploadBtnEl?.addEventListener("click", () => uploadInputEl?.click());
    uploadInputEl?.addEventListener("change", () => {
      saveDialog().then(() => uploadFile());
    });
    playBtnEl?.addEventListener("click", () => {
      if (store.editingId) playSound(store.editingId);
    });
    deleteBtnEl?.addEventListener("click", () => deleteSound());
  }

  async function onPageShow() {
    stopPreview();
    if (createBtnEl) createBtnEl.hidden = !canWrite();
    try {
      await refreshSounds();
      renderList();
    } catch (e) {
      if (emptyEl) {
        emptyEl.hidden = false;
        emptyEl.textContent = e.message;
      }
    }
  }

  function onPageHide() {
    stopPreview();
    dialogEl?.close();
  }

  function getSounds() {
    return JSON.parse(JSON.stringify(store.sounds));
  }

  bindEvents();

  window.SoundsApp = {
    onPageShow,
    onPageHide,
    getSounds,
    refreshSounds,
  };
})();
