<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Kikusan</title>
    <link rel="icon" type="image/x-icon" href="/static/favicon.ico" />
    <link rel="stylesheet" href="/static/style.css" />
  </head>
  <body>
    <div class="container">
      <header>
        <a
          href="https://github.com/dadav/kikusan"
          target="_blank"
          class="github-star"
          aria-label="Star on GitHub"
        >
          ⭐ GitHub
        </a>
        <div class="header-content">
          <div class="header-title">
            <h1>Kikusan <span class="version">v{{ version }}</span></h1>
            <p>Search and download music from YouTube Music</p>
          </div>
          <div class="header-controls">
            <select
              id="format-selector"
              class="format-selector"
              aria-label="Audio format"
            >
              <option value="opus">Opus</option>
              <option value="mp3">MP3</option>
              <option value="flac">FLAC</option>
            </select>
            <button
              id="theme-toggle"
              class="theme-toggle"
              aria-label="Toggle theme"
            >
              <span class="theme-icon">🌙</span>
            </button>
            <button
              id="settings-btn"
              class="icon-btn"
              aria-label="Settings"
              title="Settings"
            >
              ⚙️
            </button>
          </div>
        </div>
      </header>

      <form id="search-form" class="search-form">
        <div class="search-input-wrapper">
          <input
            type="text"
            id="search-input"
            name="q"
            placeholder="Search for music or paste url..."
            autocomplete="off"
            required
          />
          <button
            type="button"
            class="search-clear-btn"
            id="search-clear-btn"
            aria-label="Clear search"
            title="Clear"
          >
            ✕
          </button>
        </div>
        <button type="submit">Search</button>
      </form>

      <div class="search-tabs">
        <button class="tab-btn active" data-tab="songs">Songs</button>
        <button class="tab-btn" data-tab="albums">Albums</button>
        <button class="tab-btn" data-tab="explore">Explore</button>
        <button
          class="tab-btn"
          data-tab="downloads"
          id="downloads-tab-btn"
          style="display: none"
        >
          Local
        </button>
      </div>

      <div id="status" class="status"></div>

      <!-- Queue Section -->
      <div id="queue-section" class="queue-section">
        <div class="queue-header">
          <h2>Download Queue</h2>
          <button id="clear-completed-btn" class="clear-btn">
            Clear Completed
          </button>
        </div>
        <div id="queue-list" class="queue-list"></div>
      </div>

      <div id="songs-results" class="results active"></div>
      <div id="albums-results" class="albums-grid"></div>
      <div id="explore-results" class="explore-section"></div>
      <div id="downloads-results" class="downloads-section"></div>
    </div>

    <!-- Settings Modal -->
    <div id="settings-modal" class="modal">
      <div class="modal-content">
        <div class="modal-header">
          <h2>Settings</h2>
          <button class="modal-close">&times;</button>
        </div>
        <div class="modal-body">
          <section class="settings-section">
            <h3>Cookie Authentication</h3>
            <p class="settings-description">
              Upload a cookies.txt file to access age-restricted or
              region-locked content.
              <a
                href="https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp"
                target="_blank"
                >Learn more</a
              >
            </p>

            <div id="cookie-status" class="cookie-status">
              <span class="status-indicator"></span>
              <span class="status-text">Checking...</span>
            </div>

            <div class="cookie-actions">
              <input
                type="file"
                id="cookie-file-input"
                accept=".txt"
                style="display: none"
              />
              <button id="upload-cookie-btn" class="btn btn-primary">
                Upload cookies.txt
              </button>
              <button
                id="delete-cookie-btn"
                class="btn btn-danger"
                style="display: none"
              >
                Delete Cookie File
              </button>
            </div>
          </section>
        </div>
      </div>
    </div>

    <script>
      // Theme management
      const themeToggle = document.getElementById("theme-toggle");
      const themeIcon = document.querySelector(".theme-icon");
      const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");

      function initTheme() {
        const savedTheme = localStorage.getItem("theme");
        const theme = savedTheme || (prefersDark.matches ? "dark" : "light");
        setTheme(theme);
      }

      function setTheme(theme) {
        if (theme === "light") {
          document.body.classList.add("light-theme");
          themeIcon.textContent = "☀️";
        } else {
          document.body.classList.remove("light-theme");
          themeIcon.textContent = "🌙";
        }
        localStorage.setItem("theme", theme);
      }

      function toggleTheme() {
        const isLight = document.body.classList.contains("light-theme");
        setTheme(isLight ? "dark" : "light");
      }

      themeToggle.addEventListener("click", toggleTheme);
      initTheme();

      // Listen for system preference changes
      prefersDark.addEventListener("change", (e) => {
        if (!localStorage.getItem("theme")) {
          setTheme(e.matches ? "dark" : "light");
        }
      });

      // Format management
      const formatSelector = document.getElementById("format-selector");

      function initFormat() {
        const savedFormat = localStorage.getItem("audioFormat") || "opus";
        formatSelector.value = savedFormat;
      }

      function saveFormat() {
        localStorage.setItem("audioFormat", formatSelector.value);
      }

      formatSelector.addEventListener("change", saveFormat);
      initFormat();

      // Audio playback management
      let currentAudio = null;
      let currentPlayingButton = null;

      function stopCurrentAudio() {
        if (currentAudio) {
          currentAudio.pause();
          currentAudio = null;
        }
        if (currentPlayingButton) {
          currentPlayingButton.textContent = "▶";
          currentPlayingButton.disabled = false;
          currentPlayingButton = null;
        }
      }

      async function togglePlay(videoId, button) {
        if (currentPlayingButton === button) {
          stopCurrentAudio();
          return;
        }

        stopCurrentAudio();
        button.disabled = true;
        button.textContent = "⏳";

        try {
          const response = await fetch(`/api/stream-url/${videoId}`);
          if (!response.ok) throw new Error("Failed to get stream URL");
          const data = await response.json();

          currentAudio = new Audio();
          currentPlayingButton = button;

          currentAudio.addEventListener("loadedmetadata", () => {
            button.textContent = "⏸";
            button.disabled = false;
          });

          currentAudio.addEventListener("ended", () => {
            stopCurrentAudio();
          });

          currentAudio.addEventListener("error", (e) => {
            console.error("Audio playback error:", e);
            showStatus("Playback failed", true);
            stopCurrentAudio();
          });

          // HLS URLs can't be played directly due to CORS; use server proxy
          if (data.is_hls) {
            currentAudio.src = `/api/preview/${videoId}`;
            await new Promise((resolve, reject) => {
              currentAudio.addEventListener("canplay", () => resolve(), {
                once: true,
              });
              currentAudio.addEventListener(
                "error",
                () => reject(new Error("Playback failed")),
                { once: true },
              );
            });
            await currentAudio.play();
          } else {
            currentAudio.src = data.url;
            await currentAudio.play();
          }
        } catch (error) {
          console.error("Failed to play:", error);
          showStatus("Failed to start playback: " + error.message, true);
          button.textContent = "▶";
          button.disabled = false;
        }
      }

      // Search functionality
      const searchForm = document.getElementById("search-form");
      const searchInput = document.getElementById("search-input");
      const resultsDiv = document.getElementById("songs-results");
      const statusDiv = document.getElementById("status");
      const searchClearBtn = document.getElementById("search-clear-btn");
      let playlistEventSource = null;

      // Toggle clear button visibility based on input content
      function updateClearButtonVisibility() {
        if (searchInput.value.trim()) {
          searchClearBtn.classList.add("visible");
        } else {
          searchClearBtn.classList.remove("visible");
        }
      }

      // Clear search input
      function clearSearch() {
        closePlaylistSearch();
        searchInput.value = "";
        updateClearButtonVisibility();
        searchInput.focus();

        // Clear results and update URL
        songsResults.innerHTML = "";
        albumsResults.innerHTML = "";
        showStatus("");
        updateUrlParams("", currentTab);
      }

      // Event listeners for clear button
      searchInput.addEventListener("input", updateClearButtonVisibility);
      searchClearBtn.addEventListener("click", clearSearch);

      // Initialize clear button visibility
      updateClearButtonVisibility();

      // Tab management
      const tabButtons = document.querySelectorAll(".tab-btn");
      const songsResults = document.getElementById("songs-results");
      const albumsResults = document.getElementById("albums-results");
      let currentTab = "songs";
      let viewingAlbum = null; // Tracks current album view mode { id, title, query }

      tabButtons.forEach((btn) => {
        btn.addEventListener("click", () => {
          const tab = btn.dataset.tab;
          exitAlbumViewMode(); // Clear album view mode when switching tabs

          if (tab === "explore") {
            // Explore tab manages its own URL params via updateExploreUrlParams
            // Reset explore state so it reloads fresh
            exploreLoaded = false;
            exploreBreadcrumb = [];
            setActiveTab(tab);
          } else if (tab === "downloads") {
            setActiveTab(tab);
            updateUrlParams("", tab);
          } else {
            setActiveTab(tab);
            const query = searchInput.value.trim();
            updateUrlParams(query, tab);
            if (query) performSearch(query);
          }
        });
      });

      // Exit album view mode and return to normal search
      function exitAlbumViewMode() {
        if (viewingAlbum) {
          viewingAlbum = null;
          hideBackToAlbumsButton();
        }
      }

      // Show a "Back to Albums" button when viewing album tracks
      function showBackToAlbumsButton(albumTitle) {
        let backBtn = document.getElementById("back-to-albums-btn");
        if (!backBtn) {
          backBtn = document.createElement("button");
          backBtn.id = "back-to-albums-btn";
          backBtn.className = "back-to-albums-btn";
          backBtn.addEventListener("click", handleBackToAlbums);
          // Insert after the search tabs
          const searchTabs = document.querySelector(".search-tabs");
          searchTabs.parentNode.insertBefore(backBtn, searchTabs.nextSibling);
        }
        backBtn.textContent = "Back to Albums";
        backBtn.style.display = "block";
      }

      function hideBackToAlbumsButton() {
        const backBtn = document.getElementById("back-to-albums-btn");
        if (backBtn) {
          backBtn.style.display = "none";
        }
      }

      function handleBackToAlbums() {
        if (viewingAlbum && viewingAlbum.query) {
          const query = viewingAlbum.query;
          exitAlbumViewMode();
          setActiveTab("albums");
          updateUrlParams(query, "albums");
          performSearch(query);
        } else {
          exitAlbumViewMode();
          setActiveTab("albums");
          updateUrlParams(searchInput.value.trim(), "albums");
        }
      }

      function showStatus(message, isError = false) {
        statusDiv.textContent = message;
        statusDiv.className = "status " + (isError ? "error" : "info");
        statusDiv.style.display = message ? "block" : "none";
      }

      function closePlaylistSearch() {
        if (playlistEventSource) {
          playlistEventSource.close();
          playlistEventSource = null;
        }
      }

      function isPlaylistQuery(query) {
        const trimmed = query.trim();
        if (!trimmed) return false;
        if (!/^https?:\/\//i.test(trimmed)) return false;

        try {
          const url = new URL(trimmed);
          const host = url.hostname.replace(/^www\./, "").toLowerCase();

          if (host === "deezer.com") {
            return url.pathname.includes("/playlist/");
          }

          if (
            host === "music.youtube.com" ||
            host === "youtube.com" ||
            host === "youtu.be"
          ) {
            return url.searchParams.has("list");
          }
        } catch (error) {
          return false;
        }

        return false;
      }

      function formatPlaylistProgress(progress) {
        if (progress.message) {
          return progress.message;
        }

        if (progress.stage === "matching" && progress.total) {
          const matched =
            progress.matched !== undefined
              ? ` (${progress.matched} matched)`
              : "";
          return `Matching ${progress.processed || 0}/${progress.total} tracks${matched}...`;
        }

        if (progress.stage === "resolved" && progress.total) {
          return `Found ${progress.total} tracks`;
        }

        return "Fetching playlist tracks...";
      }

      async function performPlaylistSearch(query) {
        closePlaylistSearch();
        songsResults.innerHTML = "";
        showStatus("Fetching playlist tracks...");

        return new Promise((resolve) => {
          const url = `/api/search/playlist/stream?q=${encodeURIComponent(query)}`;
          playlistEventSource = new EventSource(url);

          playlistEventSource.addEventListener("progress", (event) => {
            const data = JSON.parse(event.data);
            showStatus(formatPlaylistProgress(data));
          });

          playlistEventSource.addEventListener("complete", (event) => {
            const data = JSON.parse(event.data);
            closePlaylistSearch();

            if (!data.results || data.results.length === 0) {
              showStatus("No results found.");
              resolve();
              return;
            }

            showStatus(`Found ${data.results.length} tracks.`);
            renderResults(data.results);
            resolve();
          });

          playlistEventSource.addEventListener("failure", (event) => {
            const data = JSON.parse(event.data);
            closePlaylistSearch();
            showStatus(data.message || "Playlist search failed.", true);
            resolve();
          });

          playlistEventSource.onerror = () => {
            if (!playlistEventSource) return;
            closePlaylistSearch();
            showStatus("Playlist search connection failed.", true);
            resolve();
          };
        });
      }

      // URL query parameter handling
      function getQueryParam(name) {
        const urlParams = new URLSearchParams(window.location.search);
        return urlParams.get(name);
      }

      function updateUrlParams(query, tab, albumId = null) {
        const url = new URL(window.location);
        // Clear all explore-specific params when updating search/tab params
        url.searchParams.delete("view");
        url.searchParams.delete("country");
        url.searchParams.delete("params");
        url.searchParams.delete("title");
        url.searchParams.delete("playlistId");
        url.searchParams.delete("playlistTitle");
        url.searchParams.delete("moodParams");
        url.searchParams.delete("moodTitle");

        if (query) {
          url.searchParams.set("q", query);
        } else {
          url.searchParams.delete("q");
        }
        if (tab && tab !== "songs") {
          url.searchParams.set("tab", tab);
        } else {
          url.searchParams.delete("tab");
        }
        if (albumId) {
          url.searchParams.set("album", albumId);
        } else {
          url.searchParams.delete("album");
        }
        window.history.pushState({}, "", url);
      }

      // Update URL params for explore tab navigation states
      // view: 'home' | 'charts' | 'mood' | 'playlist'
      function updateExploreUrlParams(view, options = {}) {
        const url = new URL(window.location);
        // Clear search-specific params
        url.searchParams.delete("q");
        url.searchParams.delete("album");
        // Always set tab=explore
        url.searchParams.set("tab", "explore");
        // Clear all explore-specific params first
        url.searchParams.delete("view");
        url.searchParams.delete("country");
        url.searchParams.delete("params");
        url.searchParams.delete("title");
        url.searchParams.delete("playlistId");
        url.searchParams.delete("playlistTitle");
        url.searchParams.delete("moodParams");
        url.searchParams.delete("moodTitle");

        if (view && view !== "home") {
          url.searchParams.set("view", view);
        }
        if (view === "charts" && options.country && options.country !== "ZZ") {
          url.searchParams.set("country", options.country);
        }
        if (view === "mood") {
          if (options.params) url.searchParams.set("params", options.params);
          if (options.title) url.searchParams.set("title", options.title);
        }
        if (view === "playlist") {
          if (options.playlistId)
            url.searchParams.set("playlistId", options.playlistId);
          if (options.playlistTitle)
            url.searchParams.set("playlistTitle", options.playlistTitle);
          // Preserve parent mood context for breadcrumb navigation
          if (options.moodParams)
            url.searchParams.set("moodParams", options.moodParams);
          if (options.moodTitle)
            url.searchParams.set("moodTitle", options.moodTitle);
        }
        window.history.pushState({}, "", url);
      }

      // Initialize tab from URL
      function initTabFromUrl() {
        const tab = getQueryParam("tab") || "songs";
        if (
          tab === "songs" ||
          tab === "albums" ||
          tab === "explore" ||
          tab === "downloads"
        ) {
          setActiveTab(tab);
        }
      }

      // Set active tab (updates UI and currentTab variable)
      const exploreResults = document.getElementById("explore-results");
      const downloadsResults = document.getElementById("downloads-results");

      function setActiveTab(tab, skipExploreLoad = false) {
        currentTab = tab;
        tabButtons.forEach((b) => {
          if (b.dataset.tab === tab) {
            b.classList.add("active");
          } else {
            b.classList.remove("active");
          }
        });

        songsResults.classList.remove("active");
        albumsResults.classList.remove("active");
        exploreResults.classList.remove("active");
        downloadsResults.classList.remove("active");

        if (tab === "songs") {
          songsResults.classList.add("active");
        } else if (tab === "albums") {
          albumsResults.classList.add("active");
        } else if (tab === "explore") {
          exploreResults.classList.add("active");
          if (!skipExploreLoad) {
            loadExploreContent();
          }
        } else if (tab === "downloads") {
          downloadsResults.classList.add("active");
          loadDownloads();
        }
      }

      // Auto-search on page load
      function initAutoSearch() {
        const query = getQueryParam("q");
        const albumId = getQueryParam("album");
        const tab = getQueryParam("tab") || "songs";

        // If we have an album ID, load album tracks directly
        if (albumId) {
          if (query) {
            searchInput.value = query;
          }
          loadAlbumTracks(albumId, query);
          return;
        }

        // Restore explore tab state from URL params
        if (tab === "explore") {
          const restored = restoreExploreFromUrl();
          if (restored) return;
        }

        // Downloads tab: just activate it, loadDownloads is called by setActiveTab
        if (tab === "downloads") {
          initTabFromUrl();
          return;
        }

        initTabFromUrl();
        if (query) {
          searchInput.value = query;
          performSearch(query);
        }
      }

      // Restore explore navigation state from URL params on page load
      // Returns true if a specific explore view was restored, false for default explore home
      function restoreExploreFromUrl() {
        const view = getQueryParam("view");
        const country = getQueryParam("country");
        const moodParams = getQueryParam("params");
        const moodTitle = getQueryParam("title");
        const playlistId = getQueryParam("playlistId");
        const playlistTitle = getQueryParam("playlistTitle");
        const parentMoodParams = getQueryParam("moodParams");
        const parentMoodTitle = getQueryParam("moodTitle");

        if (view === "charts") {
          // Restore charts view with specific country (skipUrlUpdate since we're restoring from URL)
          setActiveTab("explore", true);
          loadExploreContentWithCountry(country || "ZZ", true);
          return true;
        } else if (view === "mood" && moodParams) {
          // Restore mood playlists view (skipUrlUpdate since we're restoring from URL)
          setActiveTab("explore", true);
          loadMoodPlaylists(moodParams, moodTitle || "Category", true);
          return true;
        } else if (view === "playlist" && playlistId) {
          // Restore playlist tracks view (with parent mood context if available)
          setActiveTab("explore", true);
          if (parentMoodParams) {
            exploreBreadcrumb = [
              {
                type: "category",
                title: parentMoodTitle || "Category",
                params: parentMoodParams,
              },
            ];
          }
          loadPlaylistTracks(playlistId, playlistTitle || "Playlist", true);
          return true;
        }

        // No specific explore view to restore; default explore home will load via setActiveTab
        return false;
      }

      // Load explore home with a specific country for charts (used by URL restore and country selector)
      // skipUrlUpdate: true when restoring from URL to avoid redundant pushState
      async function loadExploreContentWithCountry(
        country,
        skipUrlUpdate = false,
      ) {
        exploreLoaded = true;
        exploreBreadcrumb = [];
        exploreResults.innerHTML =
          '<div class="explore-loading">Loading explore content...</div>';

        const [moodsResult, chartsResult] = await Promise.allSettled([
          fetch("/api/explore/moods").then((res) => {
            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            return res.json();
          }),
          fetch(
            `/api/explore/charts?country=${encodeURIComponent(country)}`,
          ).then((res) => {
            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            return res.json();
          }),
        ]);

        let moods =
          moodsResult.status === "fulfilled" ? moodsResult.value : null;
        let charts =
          chartsResult.status === "fulfilled" ? chartsResult.value : null;

        if (!moods && !charts) {
          exploreResults.innerHTML =
            '<div class="explore-error">Failed to load explore content. Please try again later.</div>';
          exploreLoaded = false;
          return;
        }

        renderExploreHome(moods, charts, skipUrlUpdate);
      }

      // Extract search logic into reusable function
      async function performSearch(query) {
        closePlaylistSearch();
        showStatus("Searching...");
        if (currentTab === "songs") {
          songsResults.innerHTML = "";
          await performSongSearch(query);
        } else if (currentTab === "albums") {
          albumsResults.innerHTML = "";
          await performAlbumSearch(query);
        }
        // Explore tab doesn't use search
      }

      async function performSongSearch(query) {
        if (isPlaylistQuery(query)) {
          await performPlaylistSearch(query);
          return;
        }

        try {
          const response = await fetch(
            `/api/search?q=${encodeURIComponent(query)}`,
          );

          if (!response.ok) {
            const errorData = await response
              .json()
              .catch(() => ({ detail: "Unknown error" }));
            throw new Error(errorData.detail || `HTTP ${response.status}`);
          }

          const data = await response.json();

          if (data.results.length === 0) {
            showStatus("No results found.");
            return;
          }

          showStatus("");
          renderResults(data.results);
        } catch (error) {
          showStatus("Search failed: " + error.message, true);
        }
      }

      async function performAlbumSearch(query) {
        try {
          const response = await fetch(
            `/api/search/albums?q=${encodeURIComponent(query)}`,
          );

          if (!response.ok) {
            const errorData = await response
              .json()
              .catch(() => ({ detail: "Unknown error" }));
            throw new Error(errorData.detail || `HTTP ${response.status}`);
          }

          const data = await response.json();
          if (data.results.length === 0) {
            showStatus("No albums found.");
            return;
          }
          showStatus("");
          renderAlbums(data.results);
        } catch (error) {
          showStatus("Album search failed: " + error.message, true);
        }
      }

      searchForm.addEventListener("submit", async (e) => {
        e.preventDefault();
        const query = searchInput.value.trim();
        if (!query) return;

        updateUrlParams(query, currentTab);
        performSearch(query);
      });

      // Handle browser back/forward
      window.addEventListener("popstate", () => {
        const query = getQueryParam("q");
        const tab = getQueryParam("tab") || "songs";
        const albumId = getQueryParam("album");

        // Handle album view state
        if (albumId) {
          // Restore album view mode
          viewingAlbum = { id: albumId, query: query };
          loadAlbumTracks(albumId, query);
          return;
        } else {
          exitAlbumViewMode();
        }

        // Handle explore tab state restoration from URL params
        if (tab === "explore") {
          const view = getQueryParam("view");
          if (view) {
            // Reset explore state before restoring
            exploreLoaded = false;
            exploreBreadcrumb = [];
            restoreExploreFromUrl();
            return;
          }
          // No view param means explore home - reset and reload
          exploreLoaded = false;
          exploreBreadcrumb = [];
          setActiveTab("explore");
          return;
        }

        // Restore tab state
        if (tab === "songs" || tab === "albums" || tab === "downloads") {
          setActiveTab(tab);
        }

        if (query) {
          searchInput.value = query;
          performSearch(query);
        } else {
          searchInput.value = "";
          songsResults.innerHTML = "";
          albumsResults.innerHTML = "";
          showStatus("");
        }
      });

      // Helper function to load album tracks (used by popstate handler)
      async function loadAlbumTracks(browseId, originalQuery) {
        try {
          showStatus("Loading album tracks...");
          const response = await fetch(`/api/album/${browseId}/tracks`);

          if (!response.ok) {
            const errorData = await response
              .json()
              .catch(() => ({ detail: "Unknown error" }));
            throw new Error(errorData.detail || `HTTP ${response.status}`);
          }

          const data = await response.json();
          showStatus("");
          viewingAlbum = {
            id: browseId,
            title: data.album_title,
            query: originalQuery,
          };

          setActiveTab("songs");
          showBackToAlbumsButton(data.album_title);
          if (originalQuery) {
            searchInput.value = originalQuery;
          }

          renderResults(data.tracks);
          showStatus(
            `Showing ${data.tracks.length} tracks from "${data.album_title}"`,
          );
        } catch (error) {
          showStatus("Failed to load album: " + error.message, true);
          exitAlbumViewMode();
        }
      }

      initAutoSearch();

      function renderAlbums(albums) {
        albumsResults.innerHTML = albums
          .map(
            (album) => `
                <div class="album-card">
                    <img class="album-cover" src="${proxyImageUrl(album.thumbnail_url)}" alt="${escapeHtml(album.title)}" onerror="this.style.display='none'">
                    <div class="album-info">
                        <div class="album-title">${escapeHtml(album.title)}</div>
                        <div class="album-artist">${escapeHtml(album.artist)}</div>
                        <div class="album-meta">
                            ${album.year ? `<span>${album.year}</span>` : ""}
                            ${album.track_count ? `<span>${album.track_count} tracks</span>` : ""}
                        </div>
                    </div>
                    <div class="album-actions">
                        <button class="view-album-btn" data-browse-id="${album.browse_id}">View Tracks</button>
                        <button class="download-album-btn"
                                data-browse-id="${album.browse_id}"
                                data-title="${escapeHtml(album.title)}"
                                data-artist="${escapeHtml(album.artist)}">
                            Download Album
                        </button>
                    </div>
                </div>
            `,
          )
          .join("");

        document.querySelectorAll(".view-album-btn").forEach((btn) => {
          btn.addEventListener("click", handleViewAlbum);
        });
        document.querySelectorAll(".download-album-btn").forEach((btn) => {
          btn.addEventListener("click", handleDownloadAlbum);
        });
      }

      async function handleViewAlbum(e) {
        const browseId = e.target.dataset.browseId;
        try {
          showStatus("Loading album tracks...");
          const response = await fetch(`/api/album/${browseId}/tracks`);

          if (!response.ok) {
            const errorData = await response
              .json()
              .catch(() => ({ detail: "Unknown error" }));
            throw new Error(errorData.detail || `HTTP ${response.status}`);
          }

          const data = await response.json();
          showStatus("");

          // Store album view state
          viewingAlbum = {
            id: browseId,
            title: data.album_title,
            query: searchInput.value.trim(),
          };

          // Update URL to reflect we're viewing an album's tracks
          updateUrlParams(searchInput.value.trim(), "songs", browseId);

          setActiveTab("songs");
          showBackToAlbumsButton(data.album_title);

          renderResults(data.tracks);
          showStatus(
            `Showing ${data.tracks.length} tracks from "${data.album_title}"`,
          );
        } catch (error) {
          showStatus("Failed to load album: " + error.message, true);
        }
      }

      async function handleDownloadAlbum(e) {
        const btn = e.target;
        const browseId = btn.dataset.browseId;
        const title = btn.dataset.title;
        const artist = btn.dataset.artist;
        const format = formatSelector.value;

        btn.disabled = true;
        btn.textContent = "Queuing...";

        try {
          const response = await fetch("/api/queue/add-album", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
              browse_id: browseId,
              album_title: title,
              artist: artist,
              audio_format: format,
            }),
          });
          const data = await response.json();

          if (response.ok) {
            btn.textContent = `Queued (${data.track_count})`;
            showStatus(`Added ${data.track_count} tracks to queue`);
            setTimeout(() => {
              btn.textContent = "Download Album";
              btn.disabled = false;
            }, 3000);
          } else {
            throw new Error(data.detail || "Unknown error");
          }
        } catch (error) {
          btn.textContent = "Failed";
          showStatus("Failed: " + error.message, true);
          setTimeout(() => {
            btn.disabled = false;
            btn.textContent = "Download Album";
          }, 3000);
        }
      }

      function renderResults(tracks) {
        // Stop any playing audio when re-rendering results
        stopCurrentAudio();

        songsResults.innerHTML = tracks
          .map(
            (track) => `
                <div class="track">
                    <div class="track-main">
                        <img
                            class="track-cover"
                            src="${proxyImageUrl(track.thumbnail_url)}"
                            alt=""
                            loading="lazy"
                        >
                        <div class="track-info">
                            <div class="track-title">${escapeHtml(track.title)}${isUgcVideoType(track.video_type) ? '<span class="ugc-badge">UGC</span>' : ""}</div>
                            <div class="track-artist">${escapeHtml(track.artist)}${track.album ? " - " + escapeHtml(track.album) : ""}</div>
                            <div class="track-meta">
                                <span class="track-duration">${track.duration}</span>
                                ${track.view_count ? `<span class="track-views">${track.view_count} views</span>` : ""}
                            </div>
                        </div>
                    </div>
                    <div class="track-actions">
                        <button
                            class="play-btn"
                            data-video-id="${track.video_id}"
                            aria-label="Play preview"
                        >
                            ▶
                        </button>
                        <button class="copy-url-btn" data-video-id="${track.video_id}" aria-label="Copy URL" title="Copy YouTube Music URL">
                            🔗
                        </button>
                        <button
                            class="download-btn"
                            data-video-id="${track.video_id}"
                            data-title="${escapeHtml(track.title)}"
                            data-artist="${escapeHtml(track.artist)}"
                        >
                            Download
                        </button>
                    </div>
                </div>
            `,
          )
          .join("");

        // Add play button handlers
        document.querySelectorAll(".play-btn").forEach((btn) => {
          btn.addEventListener("click", (e) => {
            e.stopPropagation();
            const videoId = e.target.dataset.videoId;
            togglePlay(videoId, e.target);
          });
        });

        // Add copy URL button handlers
        document.querySelectorAll(".copy-url-btn").forEach((btn) => {
          btn.addEventListener("click", handleCopyUrl);
        });

        // Add download button handlers
        document.querySelectorAll(".download-btn").forEach((btn) => {
          btn.addEventListener("click", handleDownload);
        });
      }

      async function handleCopyUrl(e) {
        e.stopPropagation();
        const btn = e.target;
        const videoId = btn.dataset.videoId;
        const url = `https://music.youtube.com/watch?v=${videoId}`;
        const originalText = btn.textContent;

        try {
          if (navigator.clipboard && navigator.clipboard.writeText) {
            await navigator.clipboard.writeText(url);
            btn.textContent = "✓";
            showStatus("URL copied to clipboard");
          } else {
            // Fallback for older browsers
            const textarea = document.createElement("textarea");
            textarea.value = url;
            textarea.style.position = "fixed";
            textarea.style.opacity = "0";
            document.body.appendChild(textarea);
            textarea.select();
            const success = document.execCommand("copy");
            document.body.removeChild(textarea);

            if (success) {
              btn.textContent = "✓";
              showStatus("URL copied to clipboard");
            } else {
              throw new Error("Copy command failed");
            }
          }

          setTimeout(() => {
            btn.textContent = originalText;
          }, 2000);
        } catch (error) {
          console.error("Failed to copy URL:", error);
          btn.textContent = "✗";
          showStatus("Failed to copy URL: " + error.message, true);
          setTimeout(() => {
            btn.textContent = originalText;
          }, 2000);
        }
      }

      async function handleDownload(e) {
        e.stopPropagation();
        const btn = e.target;
        const videoId = btn.dataset.videoId;
        const title = btn.dataset.title;
        const artist = btn.dataset.artist;
        const format = formatSelector.value;

        btn.disabled = true;
        const originalText = btn.textContent;
        btn.textContent = "Adding...";

        try {
          const response = await fetch("/api/queue/add", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
              video_id: videoId,
              title,
              artist,
              audio_format: format,
            }),
          });
          const data = await response.json();

          if (response.ok) {
            btn.textContent = "Queued";
            showStatus(`Added to queue: ${title}`);
            setTimeout(() => {
              btn.textContent = originalText;
              btn.disabled = false;
            }, 2000);
          } else {
            btn.textContent = "Failed";
            showStatus("Failed to add to queue", true);
            setTimeout(() => {
              btn.disabled = false;
              btn.textContent = originalText;
            }, 3000);
          }
        } catch (error) {
          btn.textContent = "Error";
          showStatus("Failed to add to queue: " + error.message, true);
          setTimeout(() => {
            btn.disabled = false;
            btn.textContent = originalText;
          }, 3000);
        }
      }

      // Queue Management
      let eventSource = null;

      function initQueue() {
        // Connect to SSE endpoint for real-time updates
        eventSource = new EventSource("/api/queue/stream");

        eventSource.onmessage = (event) => {
          const jobs = JSON.parse(event.data);
          updateQueueUI(jobs);
        };

        eventSource.onerror = (error) => {
          console.error("SSE error:", error);
          // Reconnect after 5 seconds
          setTimeout(() => {
            if (eventSource) {
              eventSource.close();
            }
            initQueue();
          }, 5000);
        };

        // Load initial queue
        fetchQueue();
      }

      async function fetchQueue() {
        try {
          const response = await fetch("/api/queue/jobs");
          const data = await response.json();
          updateQueueUI(data.jobs);
        } catch (error) {
          console.error("Failed to fetch queue:", error);
        }
      }

      let previousJobStatuses = {};

      function updateQueueUI(jobs) {
        const queueList = document.getElementById("queue-list");
        const queueSection = document.getElementById("queue-section");

        // Detect newly completed jobs and refresh downloads tab if active
        let hasNewlyCompleted = false;
        for (const job of jobs) {
          if (
            job.status === "completed" &&
            previousJobStatuses[job.id] &&
            previousJobStatuses[job.id] !== "completed"
          ) {
            hasNewlyCompleted = true;
          }
          previousJobStatuses[job.id] = job.status;
        }
        if (hasNewlyCompleted && currentTab === "downloads") {
          loadDownloads();
        }

        if (jobs.length === 0) {
          queueSection.style.display = "none";
          return;
        }

        queueSection.style.display = "block";

        queueList.innerHTML = jobs
          .map((job) => {
            const statusClass = job.status.toLowerCase();
            let progressHtml = "";
            let actionsHtml = "";

            if (job.status === "downloading") {
              progressHtml = `
                        <div class="progress-bar">
                            <div class="progress-fill" style="width: ${job.progress}%"></div>
                            <span class="progress-text">${job.progress.toFixed(1)}% - ${job.speed} - ${job.eta}</span>
                        </div>
                    `;
            }

            if (job.status === "completed" && job.file_path) {
              actionsHtml = `
                        <a href="/api/download-file/${encodeURIComponent(job.file_path)}" class="download-file-btn" download>
                            💾 Save to Computer
                        </a>
                    `;
            }

            if (job.status === "completed" || job.status === "failed") {
              actionsHtml += `
                        <button class="clear-job-btn" data-job-id="${job.id}">Clear</button>
                    `;
            }

            const errorHtml = job.error
              ? `<div class="job-error">${escapeHtml(job.error)}</div>`
              : "";

            return `
                    <div class="job-card ${statusClass}">
                        <div class="job-header">
                            <div class="job-info">
                                <span class="job-title">${escapeHtml(job.title)}</span>
                                <span class="job-artist">${escapeHtml(job.artist)}</span>
                            </div>
                            <span class="job-status status-${statusClass}">${job.status.toUpperCase()}</span>
                        </div>
                        ${progressHtml}
                        ${errorHtml}
                        ${actionsHtml}
                    </div>
                `;
          })
          .join("");

        // Add clear button handlers
        document.querySelectorAll(".clear-job-btn").forEach((btn) => {
          btn.addEventListener("click", async (e) => {
            const jobId = e.target.dataset.jobId;
            await removeJob(jobId);
          });
        });
      }

      async function removeJob(jobId) {
        try {
          await fetch(`/api/queue/${jobId}`, { method: "DELETE" });
        } catch (error) {
          console.error("Failed to remove job:", error);
          showStatus("Failed to remove job", true);
        }
      }

      async function clearCompleted() {
        try {
          const response = await fetch("/api/queue/jobs");
          const data = await response.json();
          for (const job of data.jobs) {
            if (job.status === "completed" || job.status === "failed") {
              await removeJob(job.id);
            }
          }
        } catch (error) {
          console.error("Failed to clear completed:", error);
          showStatus("Failed to clear completed jobs", true);
        }
      }

      document
        .getElementById("clear-completed-btn")
        .addEventListener("click", clearCompleted);

      // Initialize queue on page load
      initQueue();

      // Settings modal management
      const settingsBtn = document.getElementById("settings-btn");
      const settingsModal = document.getElementById("settings-modal");
      const modalClose = settingsModal.querySelector(".modal-close");
      const cookieFileInput = document.getElementById("cookie-file-input");
      const uploadCookieBtn = document.getElementById("upload-cookie-btn");
      const deleteCookieBtn = document.getElementById("delete-cookie-btn");
      const cookieStatus = document.getElementById("cookie-status");

      // Open settings
      settingsBtn.addEventListener("click", async () => {
        settingsModal.classList.add("active");
        await refreshCookieStatus();
      });

      // Close settings
      modalClose.addEventListener("click", () => {
        settingsModal.classList.remove("active");
      });
      settingsModal.addEventListener("click", (e) => {
        if (e.target === settingsModal) {
          settingsModal.classList.remove("active");
        }
      });

      // Refresh cookie status
      async function refreshCookieStatus() {
        try {
          const response = await fetch("/api/settings/cookies/status");
          const data = await response.json();

          const indicator = cookieStatus.querySelector(".status-indicator");
          const text = cookieStatus.querySelector(".status-text");

          if (data.configured && data.exists) {
            indicator.className = "status-indicator status-success";
            text.textContent = `Cookie file configured (${data.source})`;
            deleteCookieBtn.style.display =
              data.source === "uploaded" ? "inline-block" : "none";
          } else if (data.configured && !data.exists) {
            indicator.className = "status-indicator status-warning";
            text.textContent = `Cookie file configured but not found`;
            deleteCookieBtn.style.display = "none";
          } else {
            indicator.className = "status-indicator status-none";
            text.textContent = "No cookie file configured";
            deleteCookieBtn.style.display = "none";
          }
        } catch (error) {
          console.error("Failed to get cookie status:", error);
          const indicator = cookieStatus.querySelector(".status-indicator");
          const text = cookieStatus.querySelector(".status-text");
          indicator.className = "status-indicator status-error";
          text.textContent = "Error checking cookie status";
        }
      }

      // Upload cookie file
      uploadCookieBtn.addEventListener("click", () => {
        cookieFileInput.click();
      });

      cookieFileInput.addEventListener("change", async (e) => {
        const file = e.target.files[0];
        if (!file) return;

        const formData = new FormData();
        formData.append("file", file);

        uploadCookieBtn.disabled = true;
        uploadCookieBtn.textContent = "Uploading...";

        try {
          const response = await fetch("/api/settings/cookies/upload", {
            method: "POST",
            body: formData,
          });
          const data = await response.json();

          if (response.ok) {
            showStatus("Cookie file uploaded successfully");
            await refreshCookieStatus();
          } else {
            throw new Error(data.detail || "Upload failed");
          }
        } catch (error) {
          showStatus("Failed to upload cookie file: " + error.message, true);
        } finally {
          uploadCookieBtn.disabled = false;
          uploadCookieBtn.textContent = "Upload cookies.txt";
          cookieFileInput.value = "";
        }
      });

      // Delete cookie file
      deleteCookieBtn.addEventListener("click", async () => {
        if (
          !confirm("Are you sure you want to delete the uploaded cookie file?")
        ) {
          return;
        }

        deleteCookieBtn.disabled = true;
        deleteCookieBtn.textContent = "Deleting...";

        try {
          const response = await fetch("/api/settings/cookies", {
            method: "DELETE",
          });
          const data = await response.json();

          if (response.ok) {
            showStatus("Cookie file deleted successfully");
            await refreshCookieStatus();
          } else {
            throw new Error(data.detail || "Delete failed");
          }
        } catch (error) {
          showStatus("Failed to delete cookie file: " + error.message, true);
        } finally {
          deleteCookieBtn.disabled = false;
          deleteCookieBtn.textContent = "Delete Cookie File";
        }
      });

      // --- Cron config generation ---

      // Country code to human-readable name mapping for cron config key generation
      const COUNTRY_NAMES = {
        ZZ: "global",
        US: "united-states",
        GB: "united-kingdom",
        DE: "germany",
        FR: "france",
        JP: "japan",
        KR: "south-korea",
        BR: "brazil",
        IN: "india",
        AU: "australia",
        CA: "canada",
      };

      function generateCronYaml(type, options) {
        // Generate a YAML snippet for cron.yaml explore section
        // type: "charts" or "mood"
        // options: { country, title, params, playlistId }
        if (type === "charts") {
          const country = options.country || "ZZ";
          const nameBase = COUNTRY_NAMES[country] || country.toLowerCase();
          const key = nameBase + "-charts";
          let yaml = `${key}:\n`;
          yaml += `  type: charts\n`;
          if (country !== "ZZ") {
            yaml += `  country: ${country}\n`;
          }
          yaml += `  sync: true\n`;
          yaml += `  schedule: "0 6 * * *"\n`;
          yaml += `  limit: 10`;
          return yaml;
        } else if (type === "mood") {
          const title = options.title || "mood";
          const params = options.params || "";
          const playlistId = options.playlistId || "";
          // Sanitize title into a valid cron key: lowercase, replace spaces/special chars with dashes
          const key =
            title
              .toLowerCase()
              .replace(/[^a-z0-9]+/g, "-")
              .replace(/^-|-$/g, "") || "mood";
          let yaml = `${key}:\n`;
          yaml += `  type: mood\n`;
          yaml += `  params: "${params}"\n`;
          if (playlistId) {
            yaml += `  playlist_id: "${playlistId}"\n`;
          }
          yaml += `  sync: true\n`;
          yaml += `  schedule: "0 6 * * *"`;
          return yaml;
        }
        return "";
      }

      async function copyCronConfig(yamlSnippet, btn) {
        const originalText = btn.textContent;
        try {
          if (navigator.clipboard && navigator.clipboard.writeText) {
            await navigator.clipboard.writeText(yamlSnippet);
          } else {
            // Fallback for older browsers
            const textarea = document.createElement("textarea");
            textarea.value = yamlSnippet;
            textarea.style.position = "fixed";
            textarea.style.opacity = "0";
            document.body.appendChild(textarea);
            textarea.select();
            const success = document.execCommand("copy");
            document.body.removeChild(textarea);
            if (!success) throw new Error("Copy command failed");
          }

          btn.textContent = "Copied!";
          btn.classList.add("cron-copied");
          showStatus("Cron config copied to clipboard");
          setTimeout(() => {
            btn.textContent = originalText;
            btn.classList.remove("cron-copied");
          }, 2000);
        } catch (error) {
          console.error("Failed to copy cron config:", error);
          btn.textContent = "Failed";
          showStatus("Failed to copy cron config: " + error.message, true);
          setTimeout(() => {
            btn.textContent = originalText;
          }, 2000);
        }
      }

      // --- Explore functionality ---
      let exploreLoaded = false;
      let exploreBreadcrumb = []; // tracks navigation: [{type, title, params?}]

      async function loadExploreContent() {
        if (exploreLoaded && exploreBreadcrumb.length === 0) return;
        if (exploreBreadcrumb.length > 0) return; // Don't reload if navigating

        exploreLoaded = true;
        exploreResults.innerHTML =
          '<div class="explore-loading">Loading explore content...</div>';

        // Fetch moods and charts independently so one failure does not block the other
        let moods = null;
        let charts = null;

        const [moodsResult, chartsResult] = await Promise.allSettled([
          fetch("/api/explore/moods").then((res) => {
            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            return res.json();
          }),
          fetch("/api/explore/charts?country=ZZ").then((res) => {
            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            return res.json();
          }),
        ]);

        if (moodsResult.status === "fulfilled") {
          moods = moodsResult.value;
        } else {
          console.error("Failed to load moods:", moodsResult.reason);
        }

        if (chartsResult.status === "fulfilled") {
          charts = chartsResult.value;
        } else {
          console.error("Failed to load charts:", chartsResult.reason);
        }

        if (!moods && !charts) {
          exploreResults.innerHTML =
            '<div class="explore-error">Failed to load explore content. Please try again later.</div>';
          exploreLoaded = false;
          return;
        }

        renderExploreHome(moods, charts);
      }

      function renderExploreHome(moods, charts, skipUrlUpdate = false) {
        if (!skipUrlUpdate) chartPage = 0;
        exploreBreadcrumb = [];
        // Cache moods so country selector changes skip re-fetching them
        if (moods) {
          cachedMoods = moods;
        }
        // Update URL to reflect charts country if not ZZ
        if (!skipUrlUpdate) {
          const chartsCountryCode =
            charts && charts.country ? charts.country : "ZZ";
          if (chartsCountryCode !== "ZZ") {
            updateExploreUrlParams("charts", { country: chartsCountryCode });
          } else {
            updateExploreUrlParams("home");
          }
        }
        let html = '<div class="explore-home">';

        // Charts section -- always render the header with country selector
        const chartsCountry = charts && charts.country ? charts.country : "ZZ";
        const chartTracks = charts && charts.tracks ? charts.tracks : [];

        html += `
                <div class="explore-charts-section">
                    <div class="explore-section-header">
                        <h2>Top Charts (${escapeHtml(chartsCountry === "ZZ" ? "Global" : chartsCountry)})</h2>
                        <div class="explore-section-actions">
                            <select id="charts-country-selector" class="country-selector">
                                <option value="ZZ" ${chartsCountry === "ZZ" ? "selected" : ""}>Global</option>
                                <option value="US" ${chartsCountry === "US" ? "selected" : ""}>United States</option>
                                <option value="GB" ${chartsCountry === "GB" ? "selected" : ""}>United Kingdom</option>
                                <option value="DE" ${chartsCountry === "DE" ? "selected" : ""}>Germany</option>
                                <option value="FR" ${chartsCountry === "FR" ? "selected" : ""}>France</option>
                                <option value="JP" ${chartsCountry === "JP" ? "selected" : ""}>Japan</option>
                                <option value="KR" ${chartsCountry === "KR" ? "selected" : ""}>South Korea</option>
                                <option value="BR" ${chartsCountry === "BR" ? "selected" : ""}>Brazil</option>
                                <option value="IN" ${chartsCountry === "IN" ? "selected" : ""}>India</option>
                                <option value="AU" ${chartsCountry === "AU" ? "selected" : ""}>Australia</option>
                                <option value="CA" ${chartsCountry === "CA" ? "selected" : ""}>Canada</option>
                            </select>
                            ${chartTracks.length > 0 ? '<button class="download-all-btn" id="download-all-charts">Download All</button>' : ""}
                            <button class="cron-btn" id="cron-charts-btn" data-country="${chartsCountry}" aria-label="Copy cron config" title="Copy cron.yaml config for this chart">+ Cron</button>
                        </div>
                    </div>
            `;

        if (chartTracks.length > 0) {
          const start = chartPage * TRACKS_PER_PAGE;
          const pageTracks = chartTracks.slice(start, start + TRACKS_PER_PAGE);
          html += `
                    <div class="chart-tracks-list">
                        ${pageTracks
                          .map(
                            (track, idx) => `
                            <div class="track chart-track">
                                <span class="chart-rank">${track.rank || start + idx + 1}</span>
                                <div class="track-main">
                                    <img class="track-cover" src="${proxyImageUrl(track.thumbnail_url)}" alt="" loading="lazy" onerror="this.style.display='none'">
                                    <div class="track-info">
                                        <div class="track-title">${escapeHtml(track.title)}${isUgcVideoType(track.video_type) ? '<span class="ugc-badge">UGC</span>' : ""}</div>
                                        <div class="track-artist">${escapeHtml(track.artist)}</div>
                                        <div class="track-meta">
                                            ${track.duration ? `<span class="track-duration">${track.duration}</span>` : ""}
                                            ${track.view_count ? `<span class="track-views">${track.view_count} views</span>` : ""}
                                        </div>
                                    </div>
                                </div>
                                ${track.trend ? `<span class="chart-trend trend-${track.trend}">${track.trend === "up" ? "&#9650;" : track.trend === "down" ? "&#9660;" : "&#8212;"}</span>` : ""}
                                <div class="track-actions">
                                    <button class="play-btn" data-video-id="${track.video_id}" aria-label="Play preview">&#9654;</button>
                                    <button class="copy-url-btn" data-video-id="${track.video_id}" aria-label="Copy URL" title="Copy YouTube Music URL">&#128279;</button>
                                    <button class="download-btn" data-video-id="${track.video_id}" data-title="${escapeHtml(track.title)}" data-artist="${escapeHtml(track.artist)}">Download</button>
                                </div>
                            </div>
                        `,
                          )
                          .join("")}
                    </div>
                    ${renderPaginationControls(chartPage, chartTracks.length, "chart-page")}
                `;
        } else {
          html +=
            '<div class="explore-charts-empty">No chart tracks available. Try selecting a different country.</div>';
        }

        html += "</div>";

        // Moods & Genres section
        if (moods && moods.length > 0) {
          html += '<div class="explore-moods-section">';
          for (const section of moods) {
            html += `
                        <div class="mood-section">
                            <h2>${escapeHtml(section.title)}</h2>
                            <div class="mood-categories-grid">
                                ${section.categories
                                  .map(
                                    (cat) => `
                                    <button class="mood-category-card" data-params="${escapeHtml(cat.params)}" data-title="${escapeHtml(cat.title)}">
                                        ${escapeHtml(cat.title)}
                                    </button>
                                `,
                                  )
                                  .join("")}
                            </div>
                        </div>
                    `;
          }
          html += "</div>";
        }

        html += "</div>";
        exploreResults.innerHTML = html;
        attachExploreHandlers(charts);
      }

      // Cache moods data so country selector changes do not re-fetch moods
      let cachedMoods = null;

      // Pagination state
      const TRACKS_PER_PAGE = 10;
      let chartPage = 0;
      let playlistPage = 0;

      function renderPaginationControls(page, totalTracks, idPrefix) {
        const totalPages = Math.ceil(totalTracks / TRACKS_PER_PAGE);
        if (totalPages <= 1) return "";
        return `
          <div class="pagination-controls">
            <button class="pagination-btn" id="${idPrefix}-prev" ${page === 0 ? "disabled" : ""}>&#8592; Prev</button>
            <span class="pagination-info">${page + 1} / ${totalPages}</span>
            <button class="pagination-btn" id="${idPrefix}-next" ${page >= totalPages - 1 ? "disabled" : ""}>Next &#8594;</button>
          </div>
        `;
      }

      function attachPaginationHandlers(idPrefix, onPageChange) {
        const prevBtn = document.getElementById(`${idPrefix}-prev`);
        const nextBtn = document.getElementById(`${idPrefix}-next`);
        if (prevBtn) prevBtn.addEventListener("click", () => onPageChange(-1));
        if (nextBtn) nextBtn.addEventListener("click", () => onPageChange(1));
      }

      function attachExploreHandlers(charts) {
        // Country selector
        const countrySelector = document.getElementById(
          "charts-country-selector",
        );
        if (countrySelector) {
          countrySelector.addEventListener("change", async (e) => {
            const country = e.target.value;
            chartPage = 0;
            showStatus("Loading charts...");
            try {
              const res = await fetch(
                `/api/explore/charts?country=${encodeURIComponent(country)}`,
              );
              if (!res.ok) throw new Error("Failed to load charts");
              const newCharts = await res.json();
              showStatus("");
              renderExploreHome(cachedMoods, newCharts);
            } catch (error) {
              showStatus("Failed to load charts: " + error.message, true);
            }
          });
        }

        // Chart pagination
        attachPaginationHandlers("chart-page", (delta) => {
          chartPage += delta;
          renderExploreHome(cachedMoods, charts, true);
        });

        // Cron config button for charts
        const cronChartsBtn = document.getElementById("cron-charts-btn");
        if (cronChartsBtn) {
          cronChartsBtn.addEventListener("click", (e) => {
            e.stopPropagation();
            const country = cronChartsBtn.dataset.country;
            const yaml = generateCronYaml("charts", { country });
            copyCronConfig(yaml, cronChartsBtn);
          });
        }

        // Download all charts
        const chartTracks = charts && charts.tracks ? charts.tracks : [];
        const downloadAllChartsBtn = document.getElementById(
          "download-all-charts",
        );
        if (downloadAllChartsBtn && chartTracks.length > 0) {
          downloadAllChartsBtn.addEventListener("click", () => {
            queueMultipleTracks(chartTracks, downloadAllChartsBtn);
          });
        }

        // Mood category cards
        document.querySelectorAll(".mood-category-card").forEach((card) => {
          card.addEventListener("click", () => {
            const params = card.dataset.params;
            const title = card.dataset.title;
            loadMoodPlaylists(params, title);
          });
        });

        // Play buttons in chart tracks
        exploreResults.querySelectorAll(".play-btn").forEach((btn) => {
          btn.addEventListener("click", (e) => {
            e.stopPropagation();
            const videoId = e.target.dataset.videoId;
            togglePlay(videoId, e.target);
          });
        });

        // Copy URL buttons in chart tracks
        exploreResults.querySelectorAll(".copy-url-btn").forEach((btn) => {
          btn.addEventListener("click", handleCopyUrl);
        });

        // Download buttons in chart tracks
        exploreResults.querySelectorAll(".download-btn").forEach((btn) => {
          btn.addEventListener("click", handleDownload);
        });
      }

      async function loadMoodPlaylists(
        params,
        categoryTitle,
        skipUrlUpdate = false,
      ) {
        showStatus("Loading playlists...");
        if (!skipUrlUpdate) {
          updateExploreUrlParams("mood", { params, title: categoryTitle });
        }
        try {
          const res = await fetch(
            `/api/explore/mood-playlists?params=${encodeURIComponent(params)}`,
          );
          if (!res.ok) throw new Error("Failed to load playlists");
          const playlists = await res.json();
          showStatus("");
          renderMoodPlaylists(playlists, categoryTitle, params);
        } catch (error) {
          showStatus("Failed to load playlists: " + error.message, true);
        }
      }

      function renderMoodPlaylists(playlists, categoryTitle, params) {
        exploreBreadcrumb = [
          { type: "category", title: categoryTitle, params },
        ];
        let html = `
                <div class="explore-breadcrumb">
                    <button class="breadcrumb-link" id="breadcrumb-home">Explore</button>
                    <span class="breadcrumb-sep">›</span>
                    <span class="breadcrumb-current">${escapeHtml(categoryTitle)}</span>
                    <button class="cron-btn cron-btn-inline" id="cron-mood-btn" data-params="${escapeHtml(params)}" data-title="${escapeHtml(categoryTitle)}" aria-label="Copy cron config" title="Copy cron.yaml config for this mood/genre">+ Cron</button>
                </div>
                <div class="mood-playlists-grid">
                    ${playlists
                      .map(
                        (pl) => `
                        <div class="mood-playlist-card" data-playlist-id="${escapeHtml(pl.playlist_id)}" data-title="${escapeHtml(pl.title)}">
                            <img class="mood-playlist-thumb" src="${proxyImageUrl(pl.thumbnail_url)}" alt="${escapeHtml(pl.title)}" loading="lazy" onerror="this.style.display='none'">
                            <div class="mood-playlist-info">
                                <div class="mood-playlist-title">${escapeHtml(pl.title)}</div>
                                ${pl.author ? `<div class="mood-playlist-author">${escapeHtml(pl.author)}</div>` : ""}
                            </div>
                            <div class="mood-playlist-actions">
                                <button class="view-playlist-btn" data-playlist-id="${escapeHtml(pl.playlist_id)}" data-title="${escapeHtml(pl.title)}">View Tracks</button>
                                <button class="download-playlist-btn" data-playlist-id="${escapeHtml(pl.playlist_id)}" data-title="${escapeHtml(pl.title)}">Download All</button>
                            </div>
                        </div>
                    `,
                      )
                      .join("")}
                </div>
            `;
        exploreResults.innerHTML = html;

        // Breadcrumb home link
        document
          .getElementById("breadcrumb-home")
          .addEventListener("click", () => {
            exploreBreadcrumb = [];
            exploreLoaded = false;
            updateExploreUrlParams("home");
            loadExploreContent();
          });

        // Cron config button for mood/genre category
        const cronMoodBtn = document.getElementById("cron-mood-btn");
        if (cronMoodBtn) {
          cronMoodBtn.addEventListener("click", (e) => {
            e.stopPropagation();
            const moodParams = cronMoodBtn.dataset.params;
            const moodTitle = cronMoodBtn.dataset.title;
            const yaml = generateCronYaml("mood", {
              params: moodParams,
              title: moodTitle,
            });
            copyCronConfig(yaml, cronMoodBtn);
          });
        }

        // View playlist tracks
        document.querySelectorAll(".view-playlist-btn").forEach((btn) => {
          btn.addEventListener("click", (e) => {
            e.stopPropagation();
            loadPlaylistTracks(btn.dataset.playlistId, btn.dataset.title);
          });
        });

        // Download all tracks from playlist
        document.querySelectorAll(".download-playlist-btn").forEach((btn) => {
          btn.addEventListener("click", async (e) => {
            e.stopPropagation();
            btn.disabled = true;
            btn.textContent = "Loading...";
            try {
              const res = await fetch(
                `/api/explore/playlist/${encodeURIComponent(btn.dataset.playlistId)}/tracks`,
              );
              if (!res.ok) {
                const err = await res.json().catch(() => null);
                throw new Error(err?.detail || "Failed to load tracks");
              }
              const data = await res.json();
              await queueMultipleTracks(data.tracks, btn);
            } catch (error) {
              btn.textContent = "Failed";
              showStatus("Failed: " + error.message, true);
              setTimeout(() => {
                btn.disabled = false;
                btn.textContent = "Download All";
              }, 3000);
            }
          });
        });
      }

      async function loadPlaylistTracks(
        playlistId,
        playlistTitle,
        skipUrlUpdate = false,
      ) {
        showStatus("Loading tracks...");
        if (!skipUrlUpdate) {
          // Build URL params including parent mood context from breadcrumb
          const urlOptions = { playlistId, playlistTitle };
          if (
            exploreBreadcrumb.length > 0 &&
            exploreBreadcrumb[0].type === "category"
          ) {
            urlOptions.moodParams = exploreBreadcrumb[0].params;
            urlOptions.moodTitle = exploreBreadcrumb[0].title;
          }
          updateExploreUrlParams("playlist", urlOptions);
        }
        try {
          const res = await fetch(
            `/api/explore/playlist/${encodeURIComponent(playlistId)}/tracks`,
          );
          if (!res.ok) {
            const err = await res.json().catch(() => null);
            throw new Error(err?.detail || "Failed to load tracks");
          }
          const data = await res.json();
          showStatus("");
          playlistPage = 0;
          renderPlaylistTracks(data.tracks, playlistTitle, playlistId);
        } catch (error) {
          showStatus("Failed to load tracks: " + error.message, true);
        }
      }

      function renderPlaylistTracks(tracks, playlistTitle, playlistId) {
        const prevBreadcrumb = [...exploreBreadcrumb];
        exploreBreadcrumb.push({
          type: "playlist",
          title: playlistTitle,
          playlistId,
        });

        // Extract parent mood params if available for cron config
        const parentMoodParams =
          prevBreadcrumb.length > 0 && prevBreadcrumb[0].params
            ? prevBreadcrumb[0].params
            : "";

        let html = `
                <div class="explore-breadcrumb">
                    <button class="breadcrumb-link" id="breadcrumb-home">Explore</button>
                    <span class="breadcrumb-sep">›</span>
                    ${
                      prevBreadcrumb.length > 0
                        ? `
                        <button class="breadcrumb-link" id="breadcrumb-category">${escapeHtml(prevBreadcrumb[0].title)}</button>
                        <span class="breadcrumb-sep">›</span>
                    `
                        : ""
                    }
                    <span class="breadcrumb-current">${escapeHtml(playlistTitle)}</span>
                </div>
                <div class="explore-playlist-header">
                    <div>
                        <h2>${escapeHtml(playlistTitle)}</h2>
                        <span class="explore-track-count">${tracks.length} tracks</span>
                    </div>
                    <div class="explore-section-actions">
                        <button class="download-all-btn" id="download-all-playlist">Download All</button>
                        ${parentMoodParams ? `<button class="cron-btn" id="cron-playlist-btn" data-params="${escapeHtml(parentMoodParams)}" data-playlist-id="${escapeHtml(playlistId)}" data-title="${escapeHtml(playlistTitle)}" aria-label="Copy cron config" title="Copy cron.yaml config for this playlist">+ Cron</button>` : ""}
                    </div>
                </div>
                <div class="explore-tracks-list">
            `;

        const plStart = playlistPage * TRACKS_PER_PAGE;
        const pageTracks = tracks.slice(plStart, plStart + TRACKS_PER_PAGE);
        html += pageTracks
          .map(
            (track) => `
                <div class="track">
                    <div class="track-main">
                        <img class="track-cover" src="${proxyImageUrl(track.thumbnail_url)}" alt="" loading="lazy">
                        <div class="track-info">
                            <div class="track-title">${escapeHtml(track.title)}${isUgcVideoType(track.video_type) ? '<span class="ugc-badge">UGC</span>' : ""}</div>
                            <div class="track-artist">${escapeHtml(track.artist)}${track.album ? " - " + escapeHtml(track.album) : ""}</div>
                            <div class="track-meta">
                                <span class="track-duration">${track.duration}</span>
                                ${track.view_count ? `<span class="track-views">${track.view_count} views</span>` : ""}
                            </div>
                        </div>
                    </div>
                    <div class="track-actions">
                        <button class="play-btn" data-video-id="${track.video_id}" aria-label="Play preview">&#9654;</button>
                        <button class="copy-url-btn" data-video-id="${track.video_id}" aria-label="Copy URL" title="Copy YouTube Music URL">&#128279;</button>
                        <button class="download-btn" data-video-id="${track.video_id}" data-title="${escapeHtml(track.title)}" data-artist="${escapeHtml(track.artist)}">Download</button>
                    </div>
                </div>
            `,
          )
          .join("");

        html += "</div>";
        html += renderPaginationControls(playlistPage, tracks.length, "playlist-page");
        exploreResults.innerHTML = html;

        // Breadcrumb navigation
        document
          .getElementById("breadcrumb-home")
          .addEventListener("click", () => {
            exploreBreadcrumb = [];
            exploreLoaded = false;
            updateExploreUrlParams("home");
            loadExploreContent();
          });

        const catBtn = document.getElementById("breadcrumb-category");
        if (catBtn && prevBreadcrumb.length > 0) {
          catBtn.addEventListener("click", () => {
            exploreBreadcrumb = [];
            loadMoodPlaylists(
              prevBreadcrumb[0].params,
              prevBreadcrumb[0].title,
            );
          });
        }

        // Cron config button for playlist
        const cronPlaylistBtn = document.getElementById("cron-playlist-btn");
        if (cronPlaylistBtn) {
          cronPlaylistBtn.addEventListener("click", (e) => {
            e.stopPropagation();
            const moodParams = cronPlaylistBtn.dataset.params;
            const playlistIdData = cronPlaylistBtn.dataset.playlistId;
            const playlistTitleData = cronPlaylistBtn.dataset.title;
            const yaml = generateCronYaml("mood", {
              params: moodParams,
              title: playlistTitleData,
              playlistId: playlistIdData,
            });
            copyCronConfig(yaml, cronPlaylistBtn);
          });
        }

        // Download all tracks
        document
          .getElementById("download-all-playlist")
          .addEventListener("click", () => {
            queueMultipleTracks(
              tracks,
              document.getElementById("download-all-playlist"),
            );
          });

        // Play buttons in playlist tracks
        exploreResults.querySelectorAll(".play-btn").forEach((btn) => {
          btn.addEventListener("click", (e) => {
            e.stopPropagation();
            const videoId = e.target.dataset.videoId;
            togglePlay(videoId, e.target);
          });
        });

        // Copy URL buttons in playlist tracks
        exploreResults.querySelectorAll(".copy-url-btn").forEach((btn) => {
          btn.addEventListener("click", handleCopyUrl);
        });

        // Individual download buttons
        exploreResults.querySelectorAll(".download-btn").forEach((btn) => {
          btn.addEventListener("click", handleDownload);
        });

        // Playlist pagination
        attachPaginationHandlers("playlist-page", (delta) => {
          playlistPage += delta;
          renderPlaylistTracks(tracks, playlistTitle, playlistId);
        });
      }

      async function queueMultipleTracks(tracks, btn) {
        if (!tracks || tracks.length === 0) {
          showStatus("No tracks to download");
          return;
        }

        const originalText = btn.textContent;
        btn.disabled = true;
        const format = formatSelector.value;
        let queued = 0;

        for (let i = 0; i < tracks.length; i++) {
          btn.textContent = `Queueing ${i + 1}/${tracks.length}...`;
          const track = tracks[i];
          try {
            const response = await fetch("/api/queue/add", {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({
                video_id: track.video_id,
                title: track.title,
                artist: track.artist,
                artists: track.artists || [],
                audio_format: format,
              }),
            });
            if (response.ok) queued++;
          } catch (error) {
            console.error(`Failed to queue ${track.title}:`, error);
          }
        }

        btn.textContent = `Queued ${queued}/${tracks.length}`;
        showStatus(`Added ${queued} tracks to queue`);
        setTimeout(() => {
          btn.textContent = originalText;
          btn.disabled = false;
        }, 3000);
      }

      // --- Downloads tab ---

      async function checkPlaylistStatus() {
        try {
          const response = await fetch("/api/playlist/status");
          const data = await response.json();
          const btn = document.getElementById("downloads-tab-btn");
          if (data.enabled) {
            btn.style.display = "";
          }
        } catch (error) {
          console.error("Failed to check playlist status:", error);
        }
      }

      // Check on load
      checkPlaylistStatus();

      async function loadDownloads() {
        downloadsResults.innerHTML =
          '<div class="explore-loading">Loading playlist...</div>';
        try {
          const response = await fetch("/api/playlist/tracks");
          if (!response.ok) {
            if (response.status === 404) {
              downloadsResults.innerHTML =
                '<div class="downloads-empty">Playlist not configured.</div>';
              return;
            }
            throw new Error(`HTTP ${response.status}`);
          }
          const data = await response.json();
          renderDownloads(data.tracks, data.total);
        } catch (error) {
          downloadsResults.innerHTML = `<div class="explore-error">Failed to load playlist: ${escapeHtml(error.message)}</div>`;
        }
      }

      function renderDownloads(tracks, total) {
        if (!tracks || tracks.length === 0) {
          downloadsResults.innerHTML =
            '<div class="downloads-empty">Your download playlist is empty. Download some tracks to see them here.</div>';
          return;
        }

        let html = `
          <div class="downloads-header">
            <h2>Downloads</h2>
            <span class="downloads-count">${total} track${total !== 1 ? "s" : ""}</span>
          </div>
          <div class="downloads-list">
        `;

        html += tracks
          .map((track) => {
            const missingClass = track.file_exists ? "" : " track-missing";
            const missingLabel = track.file_exists
              ? ""
              : '<span class="track-missing-label">File missing</span>';
            const saveLink = track.file_exists
              ? `<a href="/api/download-file/${encodeURIComponent(track.entry_path)}" class="download-file-btn" download>Save</a>`
              : "";

            return `
            <div class="track${missingClass}">
              <div class="track-main">
                <div class="track-info">
                  <div class="track-title">${escapeHtml(track.title)}</div>
                  <div class="track-artist">${escapeHtml(track.artist)}${track.album ? " - " + escapeHtml(track.album) : ""}</div>
                  <div class="track-meta">
                    ${track.duration ? `<span class="track-duration">${track.duration}</span>` : ""}
                    ${missingLabel}
                  </div>
                </div>
              </div>
              <div class="track-actions">
                ${saveLink}
                <button class="remove-btn" data-entry-path="${escapeHtml(track.entry_path)}">Remove</button>
              </div>
            </div>
          `;
          })
          .join("");

        html += "</div>";
        downloadsResults.innerHTML = html;

        // Attach remove handlers
        downloadsResults.querySelectorAll(".remove-btn").forEach((btn) => {
          btn.addEventListener("click", handleRemoveTrack);
        });
      }

      async function handleRemoveTrack(e) {
        const btn = e.target;
        const entryPath = btn.dataset.entryPath;
        btn.disabled = true;
        btn.textContent = "Removing...";

        try {
          const response = await fetch(
            `/api/playlist/tracks?entry_path=${encodeURIComponent(entryPath)}`,
            {
              method: "DELETE",
            },
          );

          if (!response.ok) {
            const data = await response
              .json()
              .catch(() => ({ detail: "Unknown error" }));
            throw new Error(data.detail || `HTTP ${response.status}`);
          }

          // Reload downloads list
          await loadDownloads();
        } catch (error) {
          btn.textContent = "Failed";
          showStatus("Failed to remove track: " + error.message, true);
          setTimeout(() => {
            btn.disabled = false;
            btn.textContent = "Remove";
          }, 3000);
        }
      }

      function isUgcVideoType(videoType) {
        return videoType === "MUSIC_VIDEO_TYPE_UGC" || videoType === "MUSIC_VIDEO_TYPE_OFFICIAL_SOURCE_MUSIC";
      }

      function escapeHtml(text) {
        const div = document.createElement("div");
        div.textContent = text;
        return div.innerHTML;
      }

      function proxyImageUrl(url) {
        if (!url) return "";
        return "/api/image-proxy?url=" + encodeURIComponent(url);
      }
    </script>
  </body>
</html>
