// Define global variables to track music files and the current index.
let currentMusicFiles = []; // Array of objects with at least { path, index }
let currentMusicIndex = -1; // Index of the current music file
let currentTrackPath  = "";
let activeTranscriptAudio = ""; // Audio file that matches the currently open transcript.

// Check thumb availability in parallel; sequentially generate missing ones to avoid concurrent creation.
function ensureFirstFivePreviews() {
  const images = Array.from(document.querySelectorAll('.images-grid img')).slice(0, 5);
  if (!images.length) return;

  const checks = images.map(img => {
    const thumbUrl = img.dataset.thumbUrl || img.src;
    const fullUrl = img.dataset.fullUrl;
    return fetch(thumbUrl, { method: 'GET', cache: 'no-store' })
      .then(res => ({
        img,
        thumbUrl,
        fullUrl,
        hasThumb: res.ok && res.status !== 204
      }))
      .catch(() => ({ img, thumbUrl, fullUrl, hasThumb: false }));
  });

  Promise.all(checks).then(results => {
    const missing = results.filter(r => !r.hasThumb && r.fullUrl);
    if (!missing.length) return;
    generateThumbsSequentially(missing);
  });
}

async function generateThumbsSequentially(entries) {
  for (const { img, thumbUrl, fullUrl } of entries) {
    try {
      await fetch(fullUrl, { method: 'GET', cache: 'no-store' });
      const cacheBust = `_=${Date.now()}`;
      const separator = thumbUrl.includes('?') ? '&' : '?';
      img.src = `${thumbUrl}${separator}${cacheBust}`;
    } catch (e) {
      // ignore; best effort
    }
  }
}

function getMainContainer() {
  return document.querySelector('main');
}

function getSearchContainer() {
  return document.querySelector('search');
}

function viewSearch() {
  // Hide the main container and show the search container
  const mainContainer = getMainContainer();
  const searchContainer = getSearchContainer();
  if (!mainContainer || !searchContainer) return;
  mainContainer.style.display = 'none';
  searchContainer.style.display = 'block';
}

function viewMain() {
  // Hide the search container and show the main container
  const mainContainer = getMainContainer();
  const searchContainer = getSearchContainer();
  if (!mainContainer || !searchContainer) return;
  searchContainer.style.display = 'none';
  mainContainer.style.display = 'block';
}


// Helper function: decode each segment then re-encode to avoid double encoding.
function encodeSubpath(subpath) {
  if (!subpath) return '';
  return subpath
    .split('/')
    .map(segment => encodeURIComponent(decodeURIComponent(segment)))
    .join('/');
}

// Convert a timecode string like "00:17" or "01:02:03" into total seconds.
function parseTimecode(text) {
  const parts = text.trim().split(':').map(part => parseInt(part, 10));
  if (parts.some(Number.isNaN) || parts.length < 2 || parts.length > 3) return null;

  const hasHours = parts.length === 3;
  const [hours, minutes, seconds] = hasHours ? parts : [0, parts[0], parts[1]];

  if (seconds < 0 || seconds > 59) return null;
  if (minutes < 0 || (hasHours && minutes > 59)) return null;
  if (hours < 0) return null;

  return hours * 3600 + minutes * 60 + seconds;
}

// Add click handlers to transcript timecodes so they seek the matching audio.
function attachTranscriptTimecodes(audioPath) {
  const container = document.getElementById('transcriptContent');
  const targetAudio = audioPath || activeTranscriptAudio;
  if (!container || !targetAudio) return;

  container.querySelectorAll('code').forEach(codeEl => {
    if (codeEl.closest('pre')) return;
    const label = codeEl.textContent.trim();
    const seconds = parseTimecode(label);
    if (seconds === null) return;

    codeEl.classList.add('timecode-link');
    codeEl.title = `Zu ${label} springen`;
    codeEl.addEventListener('click', () => jumpToTimecode(seconds, targetAudio));
  });
}

// Ensure we load the right track (if needed) and jump to the requested time.
function jumpToTimecode(seconds, relUrl) {
  if (!relUrl || seconds === null) return;

  const updateTrackState = () => {
    currentTrackPath = relUrl;
    const matchIdx = currentMusicFiles.findIndex(file => file.path === relUrl);
    currentMusicIndex = matchIdx !== -1 ? matchIdx : -1;
  };

  const seekWhenReady = () => {
    player.audio.currentTime = Math.min(
      seconds,
      Number.isFinite(player.audio.duration) ? player.audio.duration : seconds
    );
    player.audio.play();
    player.updateTimeline();
  };

  updateTrackState();

  if (player.currentRelUrl === relUrl) {
    if (player.audio.readyState >= 1) {
      seekWhenReady();
    } else {
      player.audio.addEventListener('loadedmetadata', seekWhenReady, { once: true });
    }
    return;
  }

  player.audio.addEventListener('loadedmetadata', seekWhenReady, { once: true });

  const trackLink = document.querySelector(`.play-file[data-url="${relUrl}"]`);
  if (trackLink) {
    trackLink.click();
  } else {
    player.loadTrack(relUrl);
  }
}

// Global variable for gallery images (updated from current folder)
let currentGalleryImages = [];

// Fallback thumbnail (camera icon) when thumbnail generation is unavailable
const thumbnailFallbackData = 'data:image/svg+xml;utf8,' + encodeURIComponent(
  `<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200">
    <rect width="100%" height="100%" fill="#e5e5e5"/>
    <g fill="#333" stroke="#333" stroke-width="4">
      <rect x="100" y="60" width="120" height="80" rx="10" ry="10" fill="none"/>
      <rect x="125" y="40" width="30" height="20" rx="4" ry="4" fill="#333" stroke="none"/>
      <circle cx="160" cy="100" r="24" fill="none"/>
      <circle cx="160" cy="100" r="10" fill="#333" stroke="none"/>
      <circle cx="205" cy="75" r="5" fill="#333" stroke="none"/>
    </g>
  </svg>`
);

function handleThumbnailError(imgEl) {
  imgEl.onerror = null; // Prevent infinite loop
  imgEl.src = thumbnailFallbackData;
  imgEl.classList.add('thumbnail-fallback');
  const item = imgEl.closest('.image-item');
  if (item) {
    item.classList.add('loaded');
  }
}

function handleThumbnailLoad(imgEl) {
  // Some servers return 204 for missing thumbs, which triggers load with zero dimensions
  if (!imgEl.naturalWidth || !imgEl.naturalHeight) {
    handleThumbnailError(imgEl);
    return;
  }
  const item = imgEl.closest('.image-item');
  if (item) {
    item.classList.add('loaded');
  }
}

function paintFile() {
  // Highlight the currently playing file
  if (currentTrackPath) {
    const currentMusicFile = currentMusicFiles.find(file => file.path === currentTrackPath);
    if (currentMusicFile) {
      const currentMusicFileElement = document.querySelector(`.play-file[data-url="${currentMusicFile.path}"]`);
      if (currentMusicFileElement) {
        const fileItem = currentMusicFileElement.closest('.file-item');
        fileItem.classList.add('currently-playing');
      }
    }
  }
}

function renderContent(data) {
  // Render breadcrumbs
  let breadcrumbHTML = '';
  data.breadcrumbs.forEach((crumb, index) => {
    breadcrumbHTML += `<a href="#" class="breadcrumb-link" data-path="${crumb.path}">${crumb.name}</a>`;
    if (index < data.breadcrumbs.length - 1) {
      breadcrumbHTML += `<span class="separator">&#8203;&gt;</span>`;
    }
  });
  document.getElementById('breadcrumbs').innerHTML = breadcrumbHTML;

  const imageFiles = data.files.filter(file => file.file_type === 'image');
  const nonImageFiles = data.files.filter(file => file.file_type !== 'image');

  // Check for image-only directory (no subdirectories, at least one file, all images)
  const isImageOnly = data.directories.length === 0
    && imageFiles.length > 0
    && nonImageFiles.length === 0;

  let contentHTML = '';

  if (isImageOnly) {
    // Display thumbnails grid
    contentHTML += '<div class="images-grid">';
    imageFiles.forEach(file => {
      const thumbUrl = `${file.path}?thumbnail=true`;
      contentHTML += `
        <div class="image-item">
          
          <a href="#" class="play-file image-link"
             data-url="${file.path}"
             data-file-type="${file.file_type}">
            <img
              src="/media/${thumbUrl}"
              data-thumb-url="/media/${thumbUrl}"
              data-full-url="/media/${file.path}"
              class="thumbnail"
              onload="handleThumbnailLoad(this)"
              onerror="handleThumbnailError(this)"
            />
          </a>
          <span class="file-name">${file.name}</span>
        </div>
      `;
    });
    contentHTML += '</div>';
  } else {
    share_link = '';
    // Render directories normally
    if (data.directories.length > 0) {
      const areAllShort = data.directories.every(dir => dir.name.length <= 10) && data.files.length === 0;
      if (areAllShort && data.breadcrumbs.length !== 1) {
        contentHTML += '<div class="directories-grid">';
        data.directories.forEach(dir => {
          if (admin_enabled && data.breadcrumbs.length != 1 && dir.share) {
            share_link = `<a href="#" class="create-share" data-url="${dir.path}"><i class="bi bi-gear"></i></a>`;
          }
          contentHTML += `<div class="directory-item"><a href="#" class="directory-link" data-path="${dir.path}"><i class="bi bi-folder"></i> ${dir.name}</a>
          ${share_link}
          </div>`;
        });
        contentHTML += '</div>';
      } else {
        contentHTML += '<ul>';
        if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_today) && data.folder_today.length > 0) {
          contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="heute"><i class="bi bi-calendar2-day"></i> Heute</a></li>`;
        } else if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_yesterday) && data.folder_yesterday.length > 0) {
          contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="gestern"><i class="bi bi-calendar2-day"></i> Gestern</a></li>`;
        }
        if (data.breadcrumbs.length === 1 && data.toplist_enabled) {
          contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="toplist"><i class="bi bi-graph-up"></i> häufig angehört</a></li>`;
        }
        
        data.directories.forEach(dir => {
          if (admin_enabled && data.breadcrumbs.length != 1 && dir.share) {
            share_link = `<a href="#" class="create-share" data-url="${dir.path}"><i class="bi bi-gear"></i></a>`;
          }
          if (dir.path.includes('toplist')) {
            link_symbol = '<i class="bi bi-star"></i>';
          } else {
            link_symbol = '<i class="bi bi-folder"></i>';
          }
          contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="${dir.path}">${link_symbol} ${dir.name}</a>${share_link}</li>`;
        });
        contentHTML += '</ul>';
      }
    }
    // Add search link at top level above directories/files
    if (data.breadcrumbs.length === 1) {
      contentHTML = `<ul><li class="link-item" onclick="viewSearch()"><a onclick="viewSearch()" class="link-link"><i class="bi bi-search"></i> Suche</a></li></ul>` + contentHTML;
    }

    // Render files (including music and non-image files)
    currentMusicFiles = [];
    if (nonImageFiles.length > 0) {
      contentHTML += '<ul>';
      nonImageFiles.forEach((file, idx) => {
        let symbol = '<i class="bi bi-file-earmark"></i>';
        if (file.file_type === 'music') {
          symbol = '<i class="bi bi-volume-up"></i>';
          currentMusicFiles.push({ path: file.path, index: idx, title: file.name.replace('.mp3', '') });
        } else if (file.file_type === 'image') {
          symbol = '<i class="bi bi-image"></i>';
        } else if ((file.name || '').toLowerCase().endsWith('.sng')) {
          symbol = '<i class="bi bi-music-note-list"></i>';
        }
        const indexAttr = file.file_type === 'music' ? ` data-index="${currentMusicFiles.length - 1}"` : '';
        contentHTML += `<li class="file-item">
                  <a href="#" class="play-file"${indexAttr} data-url="${file.path}" data-file-type="${file.file_type}">${symbol} ${file.name.replace('.mp3', '')}</a>`;
        if (file.has_transcript) {
          contentHTML += `<a href="#" class="show-transcript" data-url="${file.transcript_url}" data-audio-url="${file.path}" title="Show Transcript"><i class="bi bi-journal-text"></i></a>`;
        }
        contentHTML += `</li>`;
      });
      contentHTML += '</ul>';
    }

    // Show images in preview grid after the file list
    if (imageFiles.length > 0) {
      contentHTML += '<div class="images-grid">';
      imageFiles.forEach(file => {
        const thumbUrl = `${file.path}?thumbnail=true`;
        contentHTML += `
          <div class="image-item">
            
            <a href="#" class="play-file image-link"
               data-url="${file.path}"
               data-file-type="${file.file_type}">
              <img
                src="/media/${thumbUrl}"
                data-thumb-url="/media/${thumbUrl}"
                data-full-url="/media/${file.path}"
                class="thumbnail"
                onload="handleThumbnailLoad(this)"
                onerror="handleThumbnailError(this)"
              />
            </a>
            <span class="file-name">${file.name}</span>
          </div>
        `;
      });
      contentHTML += '</div>';
    }
  }

  // Insert generated content
  document.getElementById('content').innerHTML = contentHTML;

  // Attach event listeners for directories
  document.querySelectorAll('.directory-item').forEach(item => {
    item.addEventListener('click', function(e) {
      if (!e.target.closest('a')) {
        const anchor = item.querySelector('a.directory-link');
        if (anchor) anchor.click();
      }
    });
  });

  // Attach event listeners for file items (including images)
  if (isImageOnly || imageFiles.length > 0) {
    document.querySelectorAll('.image-item').forEach(item => {
      item.addEventListener('click', function(e) {
        if (!e.target.closest('a')) {
          const anchor = item.querySelector('a.image-link');
          if (anchor) anchor.click();
        }
      });
    });
  } else {
    document.querySelectorAll('li.file-item').forEach(item => {
      item.addEventListener('click', function(e) {
        if (!e.target.closest('a')) {
          const anchor = item.querySelector('a.play-file');
          if (anchor) anchor.click();
        }
      });
    });
  }

  // Update gallery images for lightbox or similar
  currentGalleryImages = imageFiles.map(f => f.path);
  ensureFirstFivePreviews();

  attachEventListeners();
}

// Helper function to show the spinner
function showSpinner() {
  const overlay = document.getElementById('fullscreen-loader');
  if (overlay) overlay.style.display = 'flex';
}

function hideSpinner() {
  const overlay = document.getElementById('fullscreen-loader');
  if (overlay) overlay.style.display = 'none';
}

// Fetch directory data from the API.
function loadDirectory(subpath) {
  const spinnerTimer = setTimeout(showSpinner, 500);
  const encodedPath = encodeSubpath(subpath);
  const apiUrl = '/api/path/' + encodedPath;
  return fetch(apiUrl)
    .then(response => response.json())
    .then(data => {
      clearTimeout(spinnerTimer);
      hideSpinner();
      if (data.breadcrumbs) {
        renderContent(data);
      } else if (data.error) {
        document.getElementById('content').innerHTML = `<div class="alert alert-warning">${data.error}</div>`;
        return;
      }
      if (data.playfile) {
        const playFileLink = document.querySelector(`.play-file[data-url="${data.playfile}"]`);
        if (playFileLink) {
          playFileLink.click();
        }
      }
      paintFile();
      return data; // return data for further chaining
    })
    .catch(error => {
      clearTimeout(spinnerTimer);
      hideSpinner();
      console.error('Error loading directory:', error);
      document.getElementById('content').innerHTML = `<p>Error loading directory!</p><p>${error}</p>`;
      throw error; // Propagate the error
    });
}

// preload the next audio file
function preload_audio() {
  // Prefetch the next file by triggering the backend diskcache.
  if (currentMusicIndex >= 0 && currentMusicIndex < currentMusicFiles.length - 1) {
    const nextFile = currentMusicFiles[currentMusicIndex + 1];
    const nextMediaUrl = '/media/' + nextFile.path;
    // Use a HEAD request so that the backend reads and caches the file without returning the full content.
    fetch(nextMediaUrl, {
      method: 'HEAD',
      headers: {
        'X-Cache-Request': 'true'
      }
    });
  }
}

const TRACK_CLICK_DEBOUNCE_MS = 3000;
let lastTrackClick = { url: null, ts: 0 };

// Attach event listeners for directory, breadcrumb, file, and transcript links.
function attachEventListeners() {
  // Directory link clicks.
  document.querySelectorAll('.directory-link').forEach(link => {
    link.addEventListener('click', function (event) {
      event.preventDefault();
      const newPath = this.getAttribute('data-path');
      loadDirectory(newPath);
      history.pushState({ subpath: newPath }, '', newPath ? '/path/' + newPath : '/');
      // Scroll to the top of the page after click:
      window.scrollTo({ top: 0, behavior: 'smooth' });
    });
  });

  // Breadcrumb link clicks.
  document.querySelectorAll('.breadcrumb-link').forEach(link => {
    link.addEventListener('click', function (event) {
      event.preventDefault();
      const newPath = this.getAttribute('data-path');
      loadDirectory(newPath);
      history.pushState({ subpath: newPath }, '', newPath ? '/path/' + newPath : '/');
    });
  });




document.querySelectorAll('.play-file').forEach(link => {
  link.addEventListener('click', async function (event) {
    event.preventDefault();

    const { fileType, url: relUrl, index } = this.dataset;
    const now = Date.now();
    const lowerUrl = (relUrl || '').toLowerCase();

    if (lowerUrl.endsWith('.sng')) {
      openSngModal(relUrl);
      return;
    }

    if (fileType === 'music') {
      // If this is the same track already loaded, ignore to avoid extra GETs and unselects.
      if (player.currentRelUrl === relUrl) {
        return;
      }

      // Remove the class from all file items.
      document.querySelectorAll('.file-item').forEach(item => {
        item.classList.remove('currently-playing');
      });

      // Debounce repeated clicks on the same track to avoid extra GETs.
      if (lastTrackClick.url === relUrl && now - lastTrackClick.ts < TRACK_CLICK_DEBOUNCE_MS) {
        return;
      }
      lastTrackClick = { url: relUrl, ts: now };

      // Update the current music index.
      currentMusicIndex = index !== undefined ? parseInt(index) : -1;
      currentTrackPath = relUrl;
      
      // Mark the clicked item as currently playing.
      this.closest('.file-item').classList.add('currently-playing');

      const reqId = crypto.randomUUID ? crypto.randomUUID() : (Date.now().toString(36) + Math.random().toString(36).slice(2));
      player.loadTrack(relUrl, reqId);

      // Delay preloading to avoid blocking playback.
      setTimeout(preload_audio, 1000);
        
    } else if (fileType === 'image') {
      // Open the gallery modal for image files.
      openGalleryModal(relUrl);
    } else {
      // serve like a download
      const reqId = crypto.randomUUID ? crypto.randomUUID() : (Date.now().toString(36) + Math.random().toString(36).slice(2));
      const urlWithReq = `/media/${relUrl}${relUrl.includes('?') ? '&' : '?'}req=${encodeURIComponent(reqId)}`;
      window.location.href = urlWithReq;
    }
  });
});


  // Transcript icon clicks.
  document.querySelectorAll('.show-transcript').forEach(link => {
    link.addEventListener('click', function (event) {
      event.preventDefault();
      const url = this.getAttribute('data-url');
      const audioUrl = this.getAttribute('data-audio-url') || '';
      const highlight = this.getAttribute('highlight');
      activeTranscriptAudio = audioUrl;
      fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error('Network response was not ok');
          }
          return response.text();
        })
        .then(data => {
          // Highlight words in the transcript (substring match, case-insensitive)
          if (highlight) {
            // Escape special regex chars
            const escapeRegExp = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

            // Split on whitespace, escape each word, and join with |
            const words = highlight
              .trim()
              .split(/\s+/)
              .map(escapeRegExp)
              .filter(Boolean);

            if (words.length) {
              const regex = new RegExp(`(${words.join('|')})`, 'gi');
              data = data.replace(regex, '<span class="highlight">$1</span>');
            }
          }
          document.getElementById('transcriptContent').innerHTML = marked.parse(data);
          attachTranscriptTimecodes(audioUrl);
          document.getElementById('transcriptModal').style.display = 'block';
        })
        .catch(error => {
          activeTranscriptAudio = '';
          document.getElementById('transcriptContent').innerHTML = '<p>Error loading transcription.</p>';
          document.getElementById('transcriptModal').style.display = 'block';
        });
    });
  });

  // create token icon clicks.
  document.querySelectorAll('.create-share').forEach(link => {
    link.addEventListener('click', function (event) {
      event.preventDefault();
      const url = '/create_token/' + this.getAttribute('data-url');
      fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error('Network response was not ok');
          }
          return response.text();
        })
        .then(data => {
          document.getElementById('transcriptContent').innerHTML = data;
          document.getElementById('transcriptModal').style.display = 'block';
        })
        .catch(error => {
          console.error('Error creating share:', error);
          document.getElementById('transcriptContent').innerHTML = "<p>You can't share this.</p>";
          document.getElementById('transcriptModal').style.display = 'block';
        });
    });
  });

  // Handle back/forward navigation (bind once).
  if (!window.__appPopstateBound) {
    window.addEventListener('popstate', function (event) {
      const subpath = event.state ? event.state.subpath : '';
      loadDirectory(subpath);
    });
    window.__appPopstateBound = true;
  }

} // End of attachEventListeners function

function escapeHtml(text) {
  return (text || '').replace(/[&<>"']/g, (char) => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
  }[char]));
}

function formatSngLine(line) {
  let safe = escapeHtml(line);
  safe = safe.replace(/&lt;dfn&gt;/gi, '<span class="song-translation">');
  safe = safe.replace(/&lt;\/dfn&gt;/gi, '</span>');
  safe = safe.replace(/&lt;small&gt;/gi, '<span class="song-small">');
  safe = safe.replace(/&lt;\/small&gt;/gi, '</span>');
  return safe;
}

function parseSngText(rawText) {
  const text = (rawText || '').replace(/\r/g, '');
  const lines = text.split('\n');
  const meta = {};
  const sections = [];
  let current = null;
  let inBody = false;

  const pushCurrent = () => {
    if (!current) return;
    while (current.lines.length && current.lines[current.lines.length - 1].trim() === '') {
      current.lines.pop();
    }
    if (current.title || current.lines.length) {
      sections.push(current);
    }
    current = null;
  };

  for (const rawLine of lines) {
    const line = rawLine.replace(/\uFEFF/g, '');
    const trimmed = line.trim();

    if (!inBody && trimmed.startsWith('#')) {
      const idx = trimmed.indexOf('=');
      if (idx > 1) {
        const key = trimmed.slice(1, idx).trim();
        const value = trimmed.slice(idx + 1).trim();
        if (key) meta[key] = value;
      }
      continue;
    }

    if (trimmed === '---' || trimmed === '-----' || trimmed === '----') {
      inBody = true;
      pushCurrent();
      continue;
    }

    if (!inBody && trimmed === '') {
      continue;
    }

    inBody = true;
    if (!current) {
      if (!trimmed) continue;
      current = { title: trimmed, lines: [] };
      continue;
    }
    current.lines.push(line);
  }
  pushCurrent();

  if (!sections.length) {
    const bodyLines = lines
      .filter(l => l && !l.trim().startsWith('#') && !['---', '-----', '----'].includes(l.trim()));
    if (bodyLines.length) {
      sections.push({ title: '', lines: bodyLines });
    }
  }

  return { meta, sections };
}

function buildSngHtml(parsed, fallbackTitle) {
  const meta = parsed.meta || {};
  const sections = parsed.sections || [];
  const title = meta.Title || fallbackTitle || 'Liedtext';

  const metaRows = [];
  const pushMeta = (label, value, asChips = false) => {
    if (!value) return;
    if (asChips) {
      const chips = value
        .split(',')
        .map(v => v.trim())
        .filter(Boolean)
        .map(v => `<span class="song-chip">${escapeHtml(v)}</span>`)
        .join('');
      if (chips) {
        metaRows.push(`<div><div class="song-meta-label">${escapeHtml(label)}</div><div class="song-chip-row">${chips}</div></div>`);
      }
      return;
    }
    metaRows.push(`<div><div class="song-meta-label">${escapeHtml(label)}</div><div class="song-meta-value">${escapeHtml(value)}</div></div>`);
  };

  pushMeta('Liederbuch', meta.Songbook);
  pushMeta('ID', meta.ChurchSongID);
  pushMeta('Stichworte', meta.Keywords, true);
  pushMeta('Reihenfolge', meta.VerseOrder, true);
  pushMeta('Sprache', meta.LangCount);

  let html = '<div class="song-sheet">';
  html += `<div class="song-header"><h2>${escapeHtml(title)}</h2></div>`;
  if (metaRows.length) {
    html += `<div class="song-meta">${metaRows.join('')}</div>`;
  }

  sections.forEach(section => {
    const heading = section.title ? `<h4>${escapeHtml(section.title)}</h4>` : '';
    const body = section.lines.map(l => formatSngLine(l)).join('<br>');
    html += `<section class="song-section">${heading}<p>${body}</p></section>`;
  });

  html += '</div>';
  return html;
}

function openSngModal(relUrl) {
  if (!relUrl) return;
  const modal = document.getElementById('transcriptModal');
  const content = document.getElementById('transcriptContent');
  if (!modal || !content) return;

  const encodedPath = encodeSubpath(relUrl);
  const filename = decodeURIComponent(relUrl.split('/').pop() || '').replace(/\.sng$/i, '');
  content.innerHTML = '<p>Lade Liedtext…</p>';
  modal.style.display = 'block';

  fetch(`/media/${encodedPath}`)
    .then(response => {
      if (!response.ok) throw new Error('Laden fehlgeschlagen');
      return response.arrayBuffer();
    })
    .then(buffer => {
      let text = '';
      try {
        text = new TextDecoder('utf-8', { fatal: true }).decode(buffer);
      } catch (err) {
        text = new TextDecoder('windows-1252').decode(buffer);
      }
      const parsed = parseSngText(text);
      content.innerHTML = buildSngHtml(parsed, filename);
    })
    .catch(() => {
      content.innerHTML = '<p>Der Liedtext konnte nicht geladen werden.</p>';
    });
}

window.openSngModal = openSngModal;

function initTranscriptModal() {
  const modal = document.getElementById('transcriptModal');
  if (!modal || modal.dataset.bound) return;

  const closeBtn = modal.querySelector('.close');
  if (closeBtn) {
    closeBtn.addEventListener('click', function () {
      modal.style.display = 'none';
    });
  }
  window.addEventListener('click', function (event) {
    if (event.target === modal) {
      modal.style.display = 'none';
    }
  });
  modal.dataset.bound = '1';
}

const LIVESTREAM_STATUS_URL = '/api/livestream/status';
const LIVESTREAM_POLL_INTERVAL_MS = 60000;
let livestreamStatusIntervalId = null;
let livestreamStatusErrorLogged = false;

function setLivestreamAlertVisible(isVisible) {
  const alertEl = document.getElementById('livestream-alert');
  if (!alertEl) return;
  alertEl.classList.toggle('d-none', !isVisible);
  alertEl.setAttribute('aria-hidden', String(!isVisible));
}

async function checkLivestreamStatus() {
  const alertEl = document.getElementById('livestream-alert');
  if (!alertEl) return;

  try {
    const response = await fetch(LIVESTREAM_STATUS_URL, {
      cache: 'no-store',
      credentials: 'same-origin',
      headers: { 'Accept': 'application/json' }
    });
    if (!response.ok) throw new Error(`Livestream status request failed (${response.status})`);
    const contentType = response.headers.get('content-type') || '';
    if (!contentType.includes('application/json')) {
      const text = await response.text();
      throw new Error(`Livestream status returned non-JSON: ${text.slice(0, 120)}`);
    }
    const data = await response.json();
    const isLive = Boolean(data && (data.live || data.audio_live));
    setLivestreamAlertVisible(isLive);
  } catch (err) {
    if (!livestreamStatusErrorLogged) {
      console.warn('Livestream status check failed', err);
      livestreamStatusErrorLogged = true;
    }
    setLivestreamAlertVisible(false);
  }
}

function initLivestreamStatus() {
  const alertEl = document.getElementById('livestream-alert');
  if (!alertEl) return;

  checkLivestreamStatus();
  if (!livestreamStatusIntervalId) {
    livestreamStatusIntervalId = setInterval(checkLivestreamStatus, LIVESTREAM_POLL_INTERVAL_MS);
  }
}

function initAppPage() {
  attachEventListeners();
  initTranscriptModal();
  initLivestreamStatus();
  if (typeof window.initSearch === 'function') window.initSearch();
  if (typeof window.initMessages === 'function') window.initMessages();
  if (typeof window.initGallery === 'function') window.initGallery();
  if (typeof window.initCalendar === 'function') window.initCalendar();

  syncThemeColor();

  // Load initial directory based on URL.
  let initialSubpath = '';
  const pendingFolderOpen = getPendingFolderOpen();
  if (window.location.pathname.indexOf('/path/') === 0) {
    initialSubpath = window.location.pathname.substring(6); // remove "/path/"
  }
  if (pendingFolderOpen?.folder !== undefined) {
    initialSubpath = pendingFolderOpen.folder || '';
  }
  loadDirectory(initialSubpath).then(() => {
    if (pendingFolderOpen?.file) {
      highlightPendingFile(pendingFolderOpen.file);
    }
    if (pendingFolderOpen) {
      clearPendingFolderOpen();
    }
  });
}



let isReloadButtonVisible = true; // Boolean to track the visibility of the reload button.

function reloadDirectory() {
  if (!isReloadButtonVisible) return Promise.resolve();

  // Extract the current path from the URL.
  let currentSubpath = '';
  if (window.location.pathname.indexOf('/path/') === 0) {
    currentSubpath = window.location.pathname.substring(6);
  }
  
  // Return the promise so that we can chain actions after the directory has reloaded.
  return loadDirectory(currentSubpath)
    .then(() => {
      // Animate the reload button as before.
      const reloadBtn = document.querySelector('#reload-button');
      const reloadBtnSVG = document.querySelector('#reload-button svg');

      void reloadBtnSVG.offsetWidth; // Force reflow to reset the animation
      reloadBtnSVG.classList.add("rotate");
      isReloadButtonVisible = false;

      setTimeout(() => {
        reloadBtnSVG.classList.remove("rotate");
        reloadBtn.style.transition = 'opacity 0.5s ease';
        reloadBtn.style.opacity = '1';
        reloadBtn.style.pointer = 'cursor';
        isReloadButtonVisible = true;
      }, 10000);
    });
}


if (!window.__appAudioEndedBound) {
  const globalAudio = document.getElementById('globalAudio');
  if (globalAudio) {
    globalAudio.addEventListener('ended', () => {
      reloadDirectory().then(() => {
        // If we had a track playing, try to find it in the updated list.
        if (currentTrackPath) {
          const newIndex = currentMusicFiles.findIndex(file => file.path === currentTrackPath);
          currentMusicIndex = newIndex;
        }

        // Now, if there's a next track, auto-play it.
        if (currentMusicIndex >= 0 && currentMusicIndex < currentMusicFiles.length - 1) {
          const nextFile = currentMusicFiles[currentMusicIndex + 1];
          const nextLink = document.querySelector(`.play-file[data-url=\"${nextFile.path}\"]`);
          if (nextLink) {
            nextLink.click();
          }
        }
      }).catch(error => {
        console.error('Error during reload:', error);
      });
    });
    window.__appAudioEndedBound = true;
  }
}

// document.addEventListener("DOMContentLoaded", function() {
//   // Automatically reload every 5 minutes (300,000 milliseconds)
//   setInterval(reloadDirectory, 300000);
// });


function syncThemeColor() {
  const cssVar = getComputedStyle(document.documentElement)
                       .getPropertyValue('--dark-background')
                       .trim();
  if (!cssVar) return;

  // sync the theme‑color meta tag
  document.querySelector('meta[name="theme-color"]')
          .setAttribute('content', cssVar);

  // apply fill to every <svg> inside elements with .icon-color
  document
    .querySelectorAll('.icon-color svg')
    .forEach(svg => svg.setAttribute('fill', cssVar));
}

// syncThemeColor is called during app init.

function getPendingFolderOpen() {
  try {
    const raw = sessionStorage.getItem('pendingFolderOpen');
    return raw ? JSON.parse(raw) : null;
  } catch {
    return null;
  }
}

function clearPendingFolderOpen() {
  try {
    sessionStorage.removeItem('pendingFolderOpen');
  } catch {
    // ignore
  }
}

function highlightPendingFile(filePath) {
  if (!filePath) return;
  const target = document.querySelector(`.play-file[data-url=\"${filePath}\"]`);
  if (target) {
    target.classList.add('search-highlight');
    target.scrollIntoView({ behavior: 'smooth', block: 'center' });
  }
}

if (window.PageRegistry && typeof window.PageRegistry.register === 'function') {
  window.PageRegistry.register('app', { init: initAppPage });
} else {
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initAppPage);
  } else {
    initAppPage();
  }
}
