(() => {
  "use strict";

  const body = document.body;
  const events = document.getElementById("sr-events");
  const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || "";
  const catalogElement = document.getElementById("audio-catalog");
  let catalog = { music: [], effects: [] };
  try { catalog = JSON.parse(catalogElement?.textContent || "{}"); } catch (_error) { /* Audio opcional. */ }
  const musicTracks = new Map((catalog.music || []).map((track) => [track.key, track]));
  const effectTracks = new Map((catalog.effects || []).map((effect) => [effect.key, effect]));
  const trackKeys = [...musicTracks.keys()];
  const preferences = {
    musicEnabled: body.dataset.musicEnabled === "1",
    effectsEnabled: body.dataset.effectsEnabled === "1",
    musicVolume: Math.min(100, Math.max(0, Number(body.dataset.musicVolume || 25))),
    effectsVolume: Math.min(100, Math.max(0, Number(body.dataset.effectsVolume || 65))),
    interfaceVolume: Math.min(100, Math.max(0, Number(body.dataset.interfaceVolume || 65))),
    notificationVolume: Math.min(100, Math.max(0, Number(body.dataset.notificationVolume || 75))),
    combatVolume: Math.min(100, Math.max(0, Number(body.dataset.combatVolume || 80))),
    ambientVolume: Math.min(100, Math.max(0, Number(body.dataset.ambientVolume || 25))),
    monoAudio: body.dataset.monoAudio === "1",
    screenReaderDucking: body.dataset.screenReaderDucking === "1",
    musicTrack: musicTracks.has(body.dataset.musicTrack) ? body.dataset.musicTrack : trackKeys[0],
    musicRandom: body.dataset.musicRandom === "1",
  };
  const music = trackKeys.length ? new Audio() : null;
  let unlocked = false;
  let pendingEffect = null;
  let persistTimer = null;
  let dialogOpener = null;
  let duckedUntil = 0;
  const monoNodes = new WeakMap();
  let musicMonoNode = null;
  const AudioContextClass = window.AudioContext || window.webkitAudioContext;
  const audioContext = AudioContextClass ? new AudioContextClass() : null;

  function connectMono(element) {
    if (!preferences.monoAudio || !audioContext || monoNodes.has(element)) return;
    try {
      const source = audioContext.createMediaElementSource(element);
      const mono = audioContext.createGain();
      mono.channelCount = 1;
      mono.channelCountMode = "explicit";
      mono.channelInterpretation = "speakers";
      source.connect(mono).connect(audioContext.destination);
      monoNodes.set(element, mono);
      if (element === music) musicMonoNode = mono;
    } catch (_error) {
      // La reproducción normal continúa si el navegador no permite reconfigurar canales.
    }
  }

  function updateMonoNodes() {
    if (!musicMonoNode) return;
    musicMonoNode.channelCount = preferences.monoAudio ? 1 : 2;
    musicMonoNode.channelCountMode = "explicit";
  }

  function musicGain() {
    return preferences.musicVolume / 100 * preferences.ambientVolume / 100;
  }

  function duckFactor() {
    return Date.now() < duckedUntil ? 0.12 : 1;
  }

  function requestDucking(milliseconds = 4000) {
    duckedUntil = Math.max(duckedUntil, Date.now() + milliseconds);
    if (music) music.volume = musicGain() * duckFactor();
    window.setTimeout(() => {
      if (music && Date.now() >= duckedUntil) music.volume = musicGain();
    }, milliseconds + 50);
  }

  function announce(message) {
    if (events) events.textContent = message;
  }

  function trackName(key) {
    return musicTracks.get(key)?.name || key || "sin pista";
  }

  function syncEffectDetails() {
    document.querySelectorAll("[data-effect-preview]").forEach((control) => {
      const effect = effectTracks.get(control.value);
      const details = control.closest("[data-audio-surface]")?.querySelector("[data-effect-details]");
      if (details && effect) details.textContent = `Se oye en: ${effect.actions} Cómo reconocerlo: ${effect.character}`;
    });
  }

  function syncControls() {
    document.querySelectorAll('[data-action="music-toggle"]').forEach((button) => {
      button.setAttribute("aria-pressed", String(preferences.musicEnabled));
      button.textContent = preferences.musicEnabled ? "Pausar música" : "Activar música";
    });
    document.querySelectorAll('[data-audio-enabled="music"]').forEach((control) => { control.checked = preferences.musicEnabled; });
    document.querySelectorAll('[data-audio-enabled="effects"]').forEach((control) => { control.checked = preferences.effectsEnabled; });
    document.querySelectorAll("[data-audio-track]").forEach((control) => { control.value = preferences.musicTrack; });
    document.querySelectorAll("[data-audio-random]").forEach((control) => { control.checked = preferences.musicRandom; });
    document.querySelectorAll('[data-audio-volume="music"]').forEach((control) => {
      control.value = String(preferences.musicVolume);
      const output = document.getElementById(control.dataset.volumeOutput);
      if (output) output.textContent = String(preferences.musicVolume);
    });
    document.querySelectorAll('[data-audio-volume="effects"]').forEach((control) => {
      control.value = String(preferences.effectsVolume);
      const output = document.getElementById(control.dataset.volumeOutput);
      if (output) output.textContent = String(preferences.effectsVolume);
    });
    ["interface", "notification", "combat", "ambient"].forEach((category) => {
      document.querySelectorAll(`[data-audio-volume="${category}"]`).forEach((control) => {
        const property = `${category}Volume`;
        control.value = String(preferences[property]);
        const output = document.getElementById(control.dataset.volumeOutput);
        if (output) output.textContent = String(preferences[property]);
      });
    });
    document.querySelectorAll('[data-audio-option="mono"]').forEach((control) => { control.checked = preferences.monoAudio; });
    document.querySelectorAll('[data-audio-option="ducking"]').forEach((control) => { control.checked = preferences.screenReaderDucking; });
    document.querySelectorAll("[data-current-track]").forEach((element) => { element.textContent = trackName(preferences.musicTrack); });
    syncEffectDetails();
  }

  function setMusicSource(key, restart = false) {
    const selected = musicTracks.get(key);
    if (!music || !selected) return false;
    preferences.musicTrack = key;
    if (restart || music.src !== new URL(selected.url, window.location.href).href) {
      music.src = selected.url;
      music.load();
    }
    music.volume = musicGain() * duckFactor();
    connectMono(music);
    syncControls();
    return true;
  }

  function startMusic() {
    if (!music || !preferences.musicEnabled) return;
    setMusicSource(preferences.musicTrack);
    music.play().catch(() => announce("El navegador bloqueó el audio. Pulsa Activar música para iniciarlo."));
  }

  function nextTrack(manual = false) {
    if (!trackKeys.length) return;
    let index = trackKeys.indexOf(preferences.musicTrack);
    if (preferences.musicRandom && trackKeys.length > 1) {
      let nextIndex = index;
      while (nextIndex === index) nextIndex = Math.floor(Math.random() * trackKeys.length);
      index = nextIndex;
    } else {
      index = (index + (manual ? 1 : 0) + trackKeys.length) % trackKeys.length;
    }
    setMusicSource(trackKeys[index], true);
    if (preferences.musicEnabled) startMusic();
    schedulePersist();
  }

  function playEffect(name, force = false) {
    const definition = effectTracks.get(name);
    const source = definition?.url;
    if ((!preferences.effectsEnabled && !force) || !source) return;
    if (!unlocked) {
      pendingEffect = name;
      return;
    }
    const effect = new Audio(source);
    const volume = ["combat", "missile"].includes(name) ? preferences.combatVolume
      : ["notification", "warning", "message", "achievement", "error", "reminder", "season", "recovery"].includes(name) ? preferences.notificationVolume
      : definition?.category === "Interfaz" ? preferences.interfaceVolume : preferences.effectsVolume;
    effect.volume = volume / 100 * duckFactor();
    connectMono(effect);
    effect.play().catch(() => {});
  }

  function unlock() {
    unlocked = true;
    startMusic();
    if (pendingEffect) {
      const name = pendingEffect;
      pendingEffect = null;
      playEffect(name);
    }
  }

  function schedulePersist(immediate = false) {
    window.clearTimeout(persistTimer);
    const persist = () => fetch("/api/audio/configuracion", {
      method: "POST",
      headers: { "Content-Type": "application/json", "X-CSRF-Token": csrfToken },
      body: JSON.stringify({
        music_enabled: preferences.musicEnabled,
        effects_enabled: preferences.effectsEnabled,
        music_volume: preferences.musicVolume,
        effects_volume: preferences.effectsVolume,
        music_track: preferences.musicTrack,
        music_random: preferences.musicRandom,
        interface_volume: preferences.interfaceVolume,
        notification_volume: preferences.notificationVolume,
        combat_volume: preferences.combatVolume,
        ambient_volume: preferences.ambientVolume,
        mono_audio: preferences.monoAudio,
        screen_reader_ducking: preferences.screenReaderDucking,
      }),
    }).then((response) => {
      if (!response.ok) throw new Error("No se pudo guardar");
    }).catch(() => announce("No se pudo guardar la configuración de audio."));
    if (immediate) persist(); else persistTimer = window.setTimeout(persist, 350);
  }

  if (music) {
    music.preload = "metadata";
    music.addEventListener("ended", () => nextTrack(false));
    setMusicSource(preferences.musicTrack);
  }
  document.addEventListener("pointerdown", unlock, { once: true, capture: true });
  document.addEventListener("keydown", unlock, { once: true, capture: true });

  document.addEventListener("click", (event) => {
    const action = event.target.closest("[data-action]")?.dataset.action;
    if (action === "music-toggle") {
      preferences.musicEnabled = !preferences.musicEnabled;
      unlocked = true;
      if (preferences.musicEnabled) startMusic(); else music?.pause();
      syncControls();
      schedulePersist(true);
      return;
    }
    if (action === "audio-settings") {
      const dialog = document.getElementById("audio-dialog");
      if (dialog && !dialog.open) {
        dialogOpener = event.target.closest("button");
        dialog.showModal();
        window.EclipseFocusDialogStart?.(dialog);
      }
      return;
    }
    if (action === "audio-next") {
      preferences.musicEnabled = true;
      unlocked = true;
      nextTrack(true);
      syncControls();
      return;
    }
    if (action === "audio-play-selected") {
      const source = event.target.closest("[data-audio-surface]")?.querySelector("[data-audio-track]");
      if (source) setMusicSource(source.value, true);
      preferences.musicEnabled = true;
      unlocked = true;
      startMusic();
      syncControls();
      schedulePersist(true);
      return;
    }
    if (action === "audio-preview-effect") {
      const button = event.target.closest('[data-action="audio-preview-effect"]');
      const source = button?.closest("[data-audio-surface]")?.querySelector("[data-effect-preview]");
      const effectKey = button?.dataset.effectKey || source?.value || "notification";
      const effect = effectTracks.get(effectKey);
      unlocked = true;
      playEffect(effectKey, true);
      const status = document.getElementById("effect-preview-status");
      if (status && effect) status.textContent = `Prueba: ${effect.name}. ${effect.actions} ${effect.character}`;
      return;
    }
    if (action === "speech-duck") {
      requestDucking(20000);
      announce("Audio reducido durante veinte segundos.");
      return;
    }
    const control = event.target.closest("button, a.button, .sidebar nav a");
    if (control && !["music-toggle", "audio-preview-effect"].includes(action)) playEffect("click");
  });

  document.addEventListener("change", (event) => {
    const control = event.target;
    if (control.matches("[data-audio-track]")) {
      setMusicSource(control.value, true);
      if (preferences.musicEnabled) startMusic();
      schedulePersist();
    } else if (control.matches("[data-audio-random]")) {
      preferences.musicRandom = control.checked;
      syncControls();
      schedulePersist();
    } else if (control.matches('[data-audio-enabled="music"]')) {
      preferences.musicEnabled = control.checked;
      if (control.checked) { unlocked = true; startMusic(); } else music?.pause();
      syncControls();
      schedulePersist();
    } else if (control.matches('[data-audio-enabled="effects"]')) {
      preferences.effectsEnabled = control.checked;
      syncControls();
      if (control.checked) { unlocked = true; playEffect("notification"); }
      schedulePersist();
    } else if (control.matches("[data-audio-volume]")) {
      schedulePersist();
      if (control.dataset.audioVolume === "effects") playEffect("notification");
    } else if (control.matches("[data-effect-preview]")) {
      syncEffectDetails();
    } else if (control.matches('[data-audio-option="mono"]')) {
      preferences.monoAudio = control.checked;
      if (preferences.monoAudio && music) connectMono(music);
      updateMonoNodes();
      schedulePersist(true);
    } else if (control.matches('[data-audio-option="ducking"]')) {
      preferences.screenReaderDucking = control.checked;
      schedulePersist(true);
    }
  });

  document.addEventListener("input", (event) => {
    const control = event.target;
    if (!control.matches("[data-audio-volume]")) return;
    const value = Math.min(100, Math.max(0, Number(control.value)));
    if (control.dataset.audioVolume === "music") {
      preferences.musicVolume = value;
    } else if (control.dataset.audioVolume === "effects") {
      preferences.effectsVolume = value;
    } else {
      const property = `${control.dataset.audioVolume}Volume`;
      if (property in preferences) preferences[property] = value;
    }
    if (music) music.volume = musicGain() * duckFactor();
    syncControls();
  });

  document.getElementById("audio-dialog")?.addEventListener("close", () => {
    dialogOpener?.focus();
    dialogOpener = null;
  });

  syncControls();
  if (events) {
    new MutationObserver(() => {
      if (preferences.screenReaderDucking && events.textContent.trim()) requestDucking(4500);
    }).observe(events, { childList: true, characterData: true, subtree: true });
  }
  const initialCue = body.dataset.soundCue || (document.querySelector(".notice-error") ? "error" : "");
  if (initialCue) playEffect(initialCue);
  window.EclipseAudio = { playEffect, startMusic, nextTrack, preferences };
})();
