// ── Fish.audio public voice library browser ─────────────────────────────────
// Lists voices from /api/fishaudio/voices (proxy to api.fish.audio) and imports a
// clonable sample (MP3 + transcript) into the voice library via /api/quick-import-voice.
(function () {
  const $ = id => document.getElementById(id);
  const grid = $('fa-grid');
  if (!grid) return;

  let _page = 1;
  let _audio = null, _playingCard = null;

  const esc = s => String(s == null ? '' : s)
    .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');

  // 28902 → "28.9K", 698 → "698", 1_240_000 → "1.2M"
  function fmtCount(n) {
    n = +n || 0;
    if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
    if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, '') + 'K';
    return String(n);
  }
  const FLAG = { EN: '🇬🇧', DE: '🇩🇪', FR: '🇫🇷', ES: '🇪🇸', IT: '🇮🇹', PT: '🇵🇹', NL: '🇳🇱', JA: '🇯🇵', ZH: '🇨🇳', KO: '🇰🇷', RU: '🇷🇺', AR: '🇸🇦', PL: '🇵🇱' };

  function status(msg, show = true) {
    const bar = $('fa-status-bar'), txt = $('fa-status-text');
    if (bar) bar.hidden = !show;
    if (txt) txt.textContent = msg || '';
  }

  // Collect active filter values from the popover
  function activeVals(group) {
    return [...document.querySelectorAll(`#fa-filter-pop .fa-pchips[data-group="${group}"] .fa-pchip.active`)]
      .map(c => c.dataset.val).filter(Boolean);
  }
  function filterCount() {
    return activeVals('gender').length + activeVals('age').length + activeVals('tag').length
      + (($('fa-tag')?.value.trim()) ? 1 : 0);
  }
  function updateFilterBadge() {
    const n = filterCount(), b = $('fa-filter-badge'), btn = $('fa-filter-btn');
    if (b) { b.textContent = String(n); b.hidden = n === 0; }
    btn?.classList.toggle('active', n > 0);
  }

  function params() {
    const p = new URLSearchParams({ page: String(_page), page_size: '24' });
    const s = $('fa-search')?.value.trim(); if (s) p.set('search', s);
    const l = $('fa-lang')?.value; if (l) p.set('language', l);
    const sort = $('fa-sort')?.value; if (sort) p.set('sort_by', sort);
    const g = activeVals('gender')[0]; if (g) p.set('gender', g);
    const a = activeVals('age')[0]; if (a) p.set('age', a);
    activeVals('tag').forEach(t => p.append('tag', t));        // use-case + quality (multi)
    const t = $('fa-tag')?.value.trim(); if (t) p.append('tag', t);
    return p;
  }

  function card(v) {
    const can = !!v.sample_audio;
    const meta = [
      v.language ? `<span class="fa-meta-chip">${FLAG[v.language] || '🌐'} ${esc(v.language)}</span>` : '',
      v.gender ? `<span class="fa-meta-chip">${esc(v.gender)}</span>` : '',
      v.age ? `<span class="fa-meta-chip">${esc(v.age)}</span>` : '',
    ].join('');
    const avatar = v.image
      ? `<img class="fa-avatar" src="${esc(v.image)}" alt="" loading="lazy" onerror="this.parentElement.classList.add('fa-avatar-fallback');this.remove();">`
      : '';
    return `<div class="fa-card${can ? '' : ' fa-card-disabled'}" data-id="${esc(v.id)}">
      <div class="fa-card-avatar${v.image ? '' : ' fa-avatar-fallback'}">
        ${avatar}
        ${can ? `<button class="fa-play" title="Preview"><span class="mdi mdi-play"></span></button>` : ''}
      </div>
      <div class="fa-card-body">
        <div class="fa-card-top">
          <span class="fa-card-name" title="${esc(v.title)}">${esc(v.title) || 'Untitled'}</span>
          ${v.author ? `<span class="fa-card-author">· ${esc(v.author)}</span>` : ''}
        </div>
        <div class="fa-card-desc">${esc(v.description || v.sample_text || '')}</div>
        <div class="fa-card-meta">${meta}</div>
        <div class="fa-card-foot">
          <span class="fa-stat" title="Generations"><span class="mdi mdi-poll"></span> ${fmtCount(v.plays)}</span>
          <span class="fa-stat" title="Likes"><span class="mdi mdi-heart-outline"></span> ${fmtCount(v.likes)}</span>
          ${can ? `<button class="btn-primary btn-sm fa-import" title="Import as a clonable voice"><span class="mdi mdi-download"></span> Use</button>` : ''}
        </div>
      </div>
    </div>`;
  }

  function render(data) {
    const items = data.items || [];
    if (!items.length) { grid.innerHTML = '<div class="el-idle">No voices found — try a different search, language or filter.</div>'; }
    else {
      grid.innerHTML = items.map(card).join('');
      grid.querySelectorAll('.fa-card').forEach((el, i) => {
        const v = items[i];
        el.querySelector('.fa-play')?.addEventListener('click', e => { e.stopPropagation(); preview(v, el); });
        el.querySelector('.fa-import')?.addEventListener('click', e => { e.stopPropagation(); importVoice(v, el); });
      });
    }
    const pager = $('fa-pager');
    if (pager) {
      pager.hidden = false;
      $('fa-prev').disabled = _page <= 1;
      const pages = Math.max(1, Math.ceil((data.total || 0) / (data.page_size || 24)));
      $('fa-next').disabled = _page >= pages;
      $('fa-pager-info').textContent = `Page ${_page} · ${(data.total || 0).toLocaleString()} voices`;
    }
  }

  async function browse() {
    status('Loading voices from fish.audio…');
    grid.innerHTML = '<div class="el-idle"><span class="reh-imsdb-spinner"></span> Loading…</div>';
    try {
      const d = await fetch('/api/fishaudio/voices?' + params().toString()).then(r => {
        if (!r.ok) throw new Error('HTTP ' + r.status);
        return r.json();
      });
      render(d);
      status(d.offline
        ? '⚠ fish.audio unreachable — showing cached results'
        : `${(d.total || 0).toLocaleString()} matching voices`, !!d.offline);
    } catch (e) {
      grid.innerHTML = `<div class="el-idle">Failed to load: ${esc(e.message)}</div>`;
      status('');
    }
  }

  function stopPreview() {
    if (_audio) { _audio.pause(); _audio = null; }
    _playingCard?.classList.remove('fa-card-playing');
    _playingCard = null;
  }

  function preview(v, el) {
    if (_playingCard === el) { stopPreview(); return; }
    stopPreview();
    if (!v.sample_audio) return;
    _audio = new Audio(v.sample_audio);
    _playingCard = el; el.classList.add('fa-card-playing');
    _audio.addEventListener('ended', stopPreview);
    _audio.play().catch(() => { stopPreview(); typeof toast === 'function' && toast('Could not play preview', 'error'); });
  }

  async function importVoice(v, el) {
    const btn = el.querySelector('.fa-import');
    const orig = btn.innerHTML;
    btn.disabled = true; btn.innerHTML = '<span class="reh-imsdb-spinner"></span>';
    const lang = (v.language || 'EN').slice(0, 2).toUpperCase();
    const base = (v.title || 'fishaudio').replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '').slice(0, 40) || 'Voice';
    const voiceId = `${lang}_${base}`;
    try {
      const r = await fetch('/api/quick-import-voice', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ voice_id: voiceId, audio_url: v.sample_audio, transcript: v.sample_text || v.default_text || '' }),
      });
      if (!r.ok) { const e = await r.json().catch(() => ({})); throw new Error(e.detail || r.statusText); }
      const d = await r.json();
      if (typeof saveMeta === 'function') {
        await saveMeta(d.voice_id, {
          name: v.title || base, tag: 'fish-audio', group: 'fish-audio', origin: 'cloned',
          gender: (v.gender || '').charAt(0).toUpperCase(), note: (v.description || '').slice(0, 180),
        }).catch(() => {});
      }
      // Import the fish.audio cover image as the voice's profile picture
      if (v.image) {
        await fetch('/api/voice/picture-url', {
          method: 'POST', headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ voice_id: d.voice_id, image_url: v.image }),
        }).catch(() => {});
      }
      btn.innerHTML = '<span class="mdi mdi-check"></span> Imported';
      el.classList.add('fa-card-imported');
      if (typeof toast === 'function') toast(`Imported "${v.title}" as ${d.voice_id}`, 'success');
      if (typeof loadVoiceLibrary === 'function') loadVoiceLibrary().catch(() => {});
    } catch (e) {
      btn.disabled = false; btn.innerHTML = orig;
      if (typeof toast === 'function') toast('Import failed: ' + e.message, 'error');
    }
  }

  // ── Wiring ──────────────────────────────────────────────────────────────
  $('fa-fetch')?.addEventListener('click', () => { _page = 1; browse(); });
  $('fa-search')?.addEventListener('keydown', e => { if (e.key === 'Enter') { _page = 1; browse(); } });
  $('fa-tag')?.addEventListener('keydown', e => { if (e.key === 'Enter') { _page = 1; browse(); applyFilters(); } });
  $('fa-lang')?.addEventListener('change', () => { _page = 1; browse(); });
  $('fa-sort')?.addEventListener('change', () => { _page = 1; browse(); });
  $('fa-prev')?.addEventListener('click', () => { if (_page > 1) { _page--; browse(); } });
  $('fa-next')?.addEventListener('click', () => { _page++; browse(); });

  // ── Filter popover ──────────────────────────────────────────────────────
  const pop = $('fa-filter-pop');
  // The popover lives inside .el-browser (overflow:hidden), so anchor it as a
  // fixed-position layer next to the filter button to avoid being clipped.
  function positionPop() {
    const btn = $('fa-filter-btn');
    if (!pop || !btn || pop.hidden) return;
    const r = btn.getBoundingClientRect();
    const w = pop.offsetWidth || 320;
    const margin = 8;
    let left = Math.min(r.right - w, window.innerWidth - w - margin);
    if (left < margin) left = margin;
    pop.style.position = 'fixed';
    pop.style.right = 'auto';
    pop.style.left = left + 'px';
    pop.style.top = (r.bottom + 8) + 'px';
    pop.style.maxHeight = (window.innerHeight - r.bottom - 16) + 'px';
  }
  function openPop(show) {
    if (!pop) return;
    pop.hidden = (show === undefined) ? !pop.hidden : !show;
    if (!pop.hidden) positionPop();
  }
  $('fa-filter-btn')?.addEventListener('click', e => { e.stopPropagation(); openPop(); });
  $('fa-filter-close')?.addEventListener('click', e => { e.stopPropagation(); openPop(false); });
  pop?.addEventListener('click', e => e.stopPropagation());
  document.addEventListener('click', () => openPop(false));
  document.addEventListener('keydown', e => { if (e.key === 'Escape') openPop(false); });
  window.addEventListener('resize', positionPop);
  window.addEventListener('scroll', positionPop, true);

  // Chip selection: single-select for gender/age, multi-select for tag groups
  document.querySelectorAll('#fa-filter-pop .fa-pchip').forEach(chip => {
    chip.addEventListener('click', () => {
      const wrap = chip.closest('.fa-pchips');
      if (wrap.classList.contains('fa-pchips-multi')) chip.classList.toggle('active');
      else { wrap.querySelectorAll('.fa-pchip').forEach(c => c.classList.remove('active')); chip.classList.add('active'); }
      updateFilterBadge();
    });
  });
  $('fa-tag')?.addEventListener('input', updateFilterBadge);

  function applyFilters() { _page = 1; openPop(false); browse(); }
  $('fa-filter-apply')?.addEventListener('click', applyFilters);
  $('fa-filter-reset')?.addEventListener('click', () => {
    document.querySelectorAll('#fa-filter-pop .fa-pchips[data-group="tag"] .fa-pchip').forEach(c => c.classList.remove('active'));
    ['gender', 'age'].forEach(g => {
      const wrap = document.querySelector(`#fa-filter-pop .fa-pchips[data-group="${g}"]`);
      wrap?.querySelectorAll('.fa-pchip').forEach((c, i) => c.classList.toggle('active', i === 0));
    });
    if ($('fa-tag')) $('fa-tag').value = '';
    updateFilterBadge(); applyFilters();
  });
})();

// ── Get Voices Online — source tabs (show one library at a time) ────────────
(function () {
  const tabs = document.getElementById('gvo-tabs');
  if (!tabs) return;
  const map = { direct: 'tab-getvoices', fish: 'fa-browser-card', eleven: 'el-browser-card' };
  function show(src) {
    if (!map[src]) src = 'direct';
    Object.entries(map).forEach(([k, id]) => {
      const el = document.getElementById(id);
      if (!el) return;
      // tab-getvoices is a .tab-content with `display:flex !important`, so a plain
      // inline display:none won't hide it — set/clear the property with priority.
      if (k === src) el.style.removeProperty('display');
      else el.style.setProperty('display', 'none', 'important');
    });
    tabs.querySelectorAll('.gvo-tab').forEach(t => t.classList.toggle('active', t.dataset.src === src));
    try { localStorage.setItem('gvo-src', src); } catch (_) {}
  }
  tabs.querySelectorAll('.gvo-tab').forEach(t => t.addEventListener('click', () => show(t.dataset.src)));
  let saved = 'direct';
  try { saved = localStorage.getItem('gvo-src') || 'direct'; } catch (_) {}
  show(saved);
})();
