(() => {
  let pollTimer = null;
  let lastBeatSeq = 0;

  function el(id) {
    return document.getElementById(id);
  }

  function updateBpmDisplay(bpm) {
    const node = el("audio-bpm-value");
    if (!node) return;
    node.textContent = Number.isFinite(bpm) ? bpm.toFixed(1) : "--";
    const topNode = el("audio-top-bpm-value");
    if (topNode) {
      topNode.textContent = Number.isFinite(bpm) ? bpm.toFixed(1) : "--";
    }
  }

  function updateBeatCounter(seq) {
    const topNode = el("audio-top-beat-count");
    if (!topNode) return;
    const n = Number(seq);
    topNode.textContent = Number.isFinite(n) && n >= 0 ? `#${Math.floor(n)}` : "#0";
  }

  function updateHitTypeDisplay(hitType, confidence) {
    const node = el("audio-hit-type-value");
    if (!node) return;
    const label = String(hitType || "unknown").toLowerCase();
    const conf = Number.isFinite(confidence) ? ` (${confidence.toFixed(2)})` : "";
    node.textContent = `${label}${conf}`;
  }

  function flashBeat() {
    const node = el("audio-beat-flash");
    if (!node) return;
    node.classList.add("active");
    setTimeout(() => node.classList.remove("active"), 80);
    const top = el("audio-top-indicator");
    if (top) {
      top.classList.add("flash");
      setTimeout(() => top.classList.remove("flash"), 90);
    }
  }

  async function stopAudio() {
    if (pollTimer) {
      clearInterval(pollTimer);
      pollTimer = null;
    }
    lastBeatSeq = 0;
    updateBeatCounter(0);
    try {
      await fetch("/api/audio/stop", { method: "POST" });
    } catch (e) {
      console.warn("audio stop failed", e);
    }
  }

  async function pollStatus() {
    try {
      const res = await fetch("/api/audio/status");
      const data = await res.json();
      const status = data?.status || {};
      if (status.error && String(status.error).trim()) {
        const node = el("audio-hit-type-value");
        if (node) {
          node.textContent = String(status.error).trim().slice(0, 120);
        }
        updateBpmDisplay(null);
        if (!status.running && pollTimer) {
          clearInterval(pollTimer);
          pollTimer = null;
        }
        return;
      }
      updateBpmDisplay(status.bpm);
      updateHitTypeDisplay(status.beat_type, Number(status.beat_type_confidence));
      const seq = Number(status.beat_seq || 0);
      updateBeatCounter(seq);
      if (seq > lastBeatSeq) {
        lastBeatSeq = seq;
        flashBeat();
      }
    } catch (e) {
      console.warn("audio status poll failed", e);
    }
  }

  async function startAudio() {
    await stopAudio();
    const override = (el("audio-device-override")?.value || "").trim();
    const selected = el("audio-device-select")?.value || "";
    const rawDevice = override !== "" ? override : selected;
    const numeric = rawDevice !== "" && /^-?\d+$/.test(rawDevice) ? Number(rawDevice) : rawDevice;
    const body = { device: rawDevice === "" ? null : numeric };
    const res = await fetch("/api/audio/start", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body),
    });
    if (!res.ok) {
      const data = await res.json().catch(() => ({}));
      throw new Error(data.error || "Failed to start audio detector");
    }
    updateBpmDisplay(null);
    updateHitTypeDisplay("unknown", NaN);
    updateBeatCounter(0);
    pollTimer = setInterval(pollStatus, 250);
    await pollStatus();
  }

  async function refreshDevices() {
    const select = el("audio-device-select");
    const debug = el("audio-devices-debug");
    if (!select) return;
    const current = select.value;
    const res = await fetch("/api/audio/devices");
    const data = await res.json();
    const inputs = Array.isArray(data?.devices) ? data.devices.slice() : [];
    if (debug) {
      debug.value = JSON.stringify(data?.diagnostics || data, null, 2);
    }
    inputs.sort((a, b) => {
      const am = String(a?.name || "").toLowerCase().includes("monitor");
      const bm = String(b?.name || "").toLowerCase().includes("monitor");
      if (am !== bm) return am ? -1 : 1;
      return Number(a?.id || 0) - Number(b?.id || 0);
    });
    select.innerHTML = '<option value="">System default input</option>';
    let defaultId = "";
    inputs.forEach((d, idx) => {
      const option = document.createElement("option");
      option.value = String(d.id);
      option.textContent = d.label || d.name || `Input ${idx + 1}`;
      if (d.is_default) {
        defaultId = String(d.id);
      }
      select.appendChild(option);
    });
    if (current) {
      select.value = current;
    } else if (defaultId) {
      select.value = defaultId;
    }
  }

  function bind() {
    const modal = el("audio-modal");
    const openBtn = el("audio-btn");
    const closeBtn = el("audio-close-btn");
    const startBtn = el("audio-start-btn");
    const stopBtn = el("audio-stop-btn");
    const refreshBtn = el("audio-refresh-btn");
    if (!modal || !openBtn) return;

    openBtn.addEventListener("click", async () => {
      modal.classList.add("active");
      try {
        await refreshDevices();
      } catch (e) {
        console.warn("audio device refresh failed", e);
      }
    });
    if (closeBtn) {
      closeBtn.addEventListener("click", () => {
        modal.classList.remove("active");
      });
    }
    if (startBtn) {
      startBtn.addEventListener("click", async () => {
        try {
          await startAudio();
          await refreshDevices();
        } catch (e) {
          console.error("audio start failed", e);
          alert("Failed to start audio input. Check mic permissions.");
        }
      });
    }
    if (stopBtn) {
      stopBtn.addEventListener("click", async () => {
        await stopAudio();
      });
    }
    if (refreshBtn) {
      refreshBtn.addEventListener("click", async () => {
        try {
          await refreshDevices();
        } catch (e) {
          console.error("refresh devices failed", e);
        }
      });
    }

  }

  async function resumePollingIfDetectorRunning() {
    try {
      const res = await fetch("/api/audio/status");
      const data = await res.json();
      const status = data?.status || {};
      if (status.running && !pollTimer) {
        pollTimer = setInterval(pollStatus, 250);
        lastBeatSeq = Number(status.beat_seq || 0);
        updateBeatCounter(lastBeatSeq);
        await pollStatus();
      }
    } catch (e) {
      console.warn("audio resume poll check failed", e);
    }
  }

  document.addEventListener("DOMContentLoaded", () => {
    bind();
    resumePollingIfDetectorRunning();
  });
})();
