// MusicRoom - Controls module
// Play, pause, seek, volume, prev/next track

(function() {
  const M = window.MusicRoom;

  function blockSyncedControlIfNeeded() {
    if (!M.synced || M.canControl()) return false;
    M.flashPermissionDenied();
    M.showToast("Sign in to control playback", "warning", 2500);
    return true;
  }
  
  // Load saved volume
  const savedVolume = localStorage.getItem(M.STORAGE_KEY);
  if (savedVolume !== null) {
    M.audio.volume = parseFloat(savedVolume);
    M.$("#volume").value = savedVolume;
  } else {
    // No saved volume - sync audio to slider's default value
    M.audio.volume = parseFloat(M.$("#volume").value);
  }
  
  // Toggle play/pause
  function togglePlayback() {
    if (!M.currentTrackId) return;
    
    if (M.synced) {
      if (blockSyncedControlIfNeeded()) return;
      if (M.ws && M.ws.readyState === WebSocket.OPEN) {
        M.ws.send(JSON.stringify({ action: M.serverPaused ? "unpause" : "pause" }));
      }
    } else {
      if (M.audio.paused) {
        if (!M.audio.src) {
          M.audio.src = M.getTrackUrl(M.currentTrackId);
          M.audio.currentTime = M.localTimestamp;
        }
        M.audio.play();
      } else {
        M.localTimestamp = M.audio.currentTime;
        M.audio.pause();
      }
      M.updateUI();
    }
  }
  
  // Jump to a specific track index
  async function jumpToTrack(index) {
    if (M.queue.length === 0) return;
    const newIndex = (index + M.queue.length) % M.queue.length;
    
    if (M.synced && M.currentChannelId) {
      if (blockSyncedControlIfNeeded()) return;
      const res = await fetch("/api/channels/" + M.currentChannelId + "/jump", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ index: newIndex })
      });
      if (res.status === 403) M.flashPermissionDenied();
      if (res.status === 400) console.warn("Jump failed: 400 - newIndex:", newIndex, "queue length:", M.queue.length);
    } else {
      const track = M.queue[newIndex];
      const trackId = track.id || track.filename;
      M.currentIndex = newIndex;
      M.currentTrackId = trackId;
      M.serverTrackDuration = track.duration;
      M.setTrackTitle(track.title?.trim() || track.filename?.replace(/\.[^.]+$/, "") || "Unknown");
      M.loadingSegments.clear();
      const cachedUrl = await M.loadTrackBlob(trackId);
      M.audio.src = cachedUrl || M.getTrackUrl(trackId);
      M.audio.currentTime = 0;
      M.localTimestamp = 0;
      M.audio.play();
      M.renderQueue();
      setTimeout(() => M.scrollToCurrentTrack(), 100);
    }
  }
  
  // Sync toggle
  M.$("#btn-sync").onclick = () => {
    M.wantSync = !M.wantSync;
    if (M.wantSync) {
      // User wants to sync - try to connect
      if (M.currentChannelId) {
        M.connectChannel(M.currentChannelId);
      }
    } else {
      // User wants local mode - disconnect
      M.synced = false;
      M.localTimestamp = M.audio.currentTime || M.getServerTime();
      if (M.ws) {
        const oldWs = M.ws;
        M.ws = null;
        oldWs.onclose = null;
        oldWs.close();
      }
    }
    M.updateUI();
  };
  
  // Play/pause button
  M.$("#status-icon").onclick = togglePlayback;
  
  // Expose jumpToTrack for double-click handling
  M.jumpToTrack = jumpToTrack;
  
  // Prev/next buttons
  M.$("#btn-prev").onclick = () => jumpToTrack(M.currentIndex - 1);
  M.$("#btn-next").onclick = () => jumpToTrack(M.currentIndex + 1);
  
  // Stream-only toggle
  const streamBtn = M.$("#btn-stream-only");
  M.updateStreamOnlyButton = function() {
    streamBtn.classList.toggle("active", M.streamOnly);
    streamBtn.title = M.streamOnly ? "Stream-only mode (click to enable caching)" : "Click to enable stream-only mode (no caching)";
  };
  streamBtn.onclick = () => {
    M.setStreamOnly(!M.streamOnly);
    M.updateStreamOnlyButton();
  };
  M.updateStreamOnlyButton();
  
  // Playback mode button
  const modeLabels = {
    "once": "loop(off)",
    "repeat-all": "loop(all)",
    "repeat-one": "loop(one)",
    "shuffle": "shuffle"
  };
  const modeOrder = ["once", "repeat-all", "repeat-one", "shuffle"];
  
  M.updateModeButton = function() {
    const btn = M.$("#btn-mode");
    btn.textContent = modeLabels[M.playbackMode] || "repeat";
    btn.title = `Playback: ${M.playbackMode}`;
    btn.className = "mode-" + M.playbackMode;
  };
  
  M.$("#btn-mode").onclick = async () => {
    if (!M.synced || !M.currentChannelId) {
      // Local mode - just cycle through modes
      const currentIdx = modeOrder.indexOf(M.playbackMode);
      M.playbackMode = modeOrder[(currentIdx + 1) % modeOrder.length];
      M.updateModeButton();
      return;
    }
    if (blockSyncedControlIfNeeded()) return;
    
    // Synced mode - send to server
    const currentIdx = modeOrder.indexOf(M.playbackMode);
    const newMode = modeOrder[(currentIdx + 1) % modeOrder.length];
    const res = await fetch("/api/channels/" + M.currentChannelId + "/mode", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ mode: newMode })
    });
    if (res.status === 403) M.flashPermissionDenied();
  };
  
  M.updateModeButton();
  
  // Progress bar seek tooltip
  M.$("#progress-container").onmousemove = (e) => {
    if (M.serverTrackDuration <= 0) return;
    const rect = M.$("#progress-container").getBoundingClientRect();
    const pct = (e.clientX - rect.left) / rect.width;
    const hoverTime = pct * M.serverTrackDuration;
    const tooltip = M.$("#seek-tooltip");
    tooltip.textContent = M.fmt(hoverTime);
    tooltip.style.left = (pct * 100) + "%";
    tooltip.style.display = "block";
  };
  
  M.$("#progress-container").onmouseleave = () => {
    M.$("#seek-tooltip").style.display = "none";
  };
  
  // Progress bar seek
  M.$("#progress-container").onclick = (e) => {
    const dur = M.synced ? M.serverTrackDuration : (M.audio.duration || M.serverTrackDuration);
    if (!M.currentTrackId || dur <= 0) return;
    const rect = M.$("#progress-container").getBoundingClientRect();
    const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    const seekTime = pct * dur;

    if (M.synced && M.currentChannelId) {
      if (blockSyncedControlIfNeeded()) return;
      fetch("/api/channels/" + M.currentChannelId + "/seek", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ timestamp: seekTime })
      }).then(res => { if (res.status === 403) M.flashPermissionDenied(); });
    } else {
      if (!M.audio.src) {
        M.audio.src = M.getTrackUrl(M.currentTrackId);
      }
      M.audio.currentTime = seekTime;
      M.localTimestamp = seekTime;
    }
  };
  
  // Mute toggle
  M.$("#btn-mute").onclick = () => {
    if (M.audio.volume > 0) {
      M.preMuteVolume = M.audio.volume;
      M.audio.volume = 0;
      M.$("#volume").value = 0;
    } else {
      M.audio.volume = M.preMuteVolume;
      M.$("#volume").value = M.preMuteVolume;
    }
    localStorage.setItem(M.STORAGE_KEY, M.audio.volume);
    M.updateUI();
  };
  
  // Volume slider
  M.$("#volume").oninput = (e) => {
    M.audio.volume = e.target.value;
    localStorage.setItem(M.STORAGE_KEY, e.target.value);
    M.updateUI();
  };
  
  // Audio element events
  M.audio.onplay = () => { M.$("#progress-bar").classList.add("playing"); M.updateUI(); };
  M.audio.onpause = () => { M.$("#progress-bar").classList.remove("playing"); M.updateUI(); };
  
  // Track loading state from audio element's progress
  M.audio.onprogress = () => {
    if (M.serverTrackDuration <= 0) return;
    const segmentDur = M.serverTrackDuration / M.SEGMENTS;
    M.loadingSegments.clear();
    for (let i = 0; i < M.SEGMENTS; i++) {
      const segStart = i * segmentDur;
      const segEnd = (i + 1) * segmentDur;
      let fullyBuffered = false;
      let partiallyBuffered = false;
      for (let j = 0; j < M.audio.buffered.length; j++) {
        const bufStart = M.audio.buffered.start(j);
        const bufEnd = M.audio.buffered.end(j);
        if (bufStart <= segStart && bufEnd >= segEnd) {
          fullyBuffered = true;
          break;
        }
        // Check if buffer is actively loading into this segment
        if (bufEnd > segStart && bufEnd < segEnd && bufStart <= segStart) {
          partiallyBuffered = true;
        }
      }
      if (partiallyBuffered && !fullyBuffered) {
        M.loadingSegments.add(i);
      }
    }
  };
})();
