// -- Get voices ---------------------------------------------------------------
const DEFAULT_VOICE_SOURCE_URLS = [
  'https://aiartes.com/voiceai',
  'https://sample-files.com/downloads/audio/wav/voice-sample.wav',
  'https://freesound.org/people/Scott%20Simpson/',
  'https://lanceblairvo.com/raw-voiceover-samples/',
  'https://github.com/yaph/tts-samples/tree/main/mp3',
  'https://github.com/jim-schwoebel/voice_datasets',
];
const VOICE_SOURCE_STORAGE_KEY = 'ttsvc-getvoices-sources';
let _voiceSourcePayload = null;
let _voiceSourceItems = [];

function sourceTextareaValueFromDefaults() {
  return DEFAULT_VOICE_SOURCE_URLS.join('\n');
}

function initGetVoiceSourcesEditor() {
  const box = $('getvoices-sources');
  if (!box || box.dataset.ready) return;
  box.value = localStorage.getItem(VOICE_SOURCE_STORAGE_KEY) || sourceTextareaValueFromDefaults();
  box.dataset.ready = '1';
  box.addEventListener('input', () => {
    localStorage.setItem(VOICE_SOURCE_STORAGE_KEY, box.value);
    _voiceSourcePayload = null;
    $('getvoices-status').textContent = 'Source list changed.';
  });
}

function getEditableVoiceSourceUrls() {
  initGetVoiceSourcesEditor();
  return ($('getvoices-sources')?.value || '')
    .split(/\r?\n/)
    .map(line => line.trim())
    .filter(line => line && !line.startsWith('#'));
}

function getVoiceSourceItems() {
  const sources = (_voiceSourcePayload && _voiceSourcePayload.sources) || [];
  return sources.flatMap(src => (src.items || []).map(item => ({...item, _sourceName: src.name, _sourceHomepage: src.homepage})));
}

function voiceSourceSearchText(item) {
  return [item.name, item.kind, item.category, item.language, item.gender, item.description, item.source, item._sourceName].join(' ').toLowerCase();
}

function setOptions(selectId, values, allLabel) {
  const sel = $(selectId);
  if (!sel) return;
  const current = sel.value || 'all';
  sel.innerHTML = `<option value="all">${escHtml(allLabel)}</option>` + values.map(value => `<option value="${escHtml(value)}">${escHtml(value)}</option>`).join('');
  sel.value = values.includes(current) ? current : 'all';
}

function renderGetVoices() {
  initGetVoiceSourcesEditor();
  const list = $('getvoices-list');
  const summary = $('getvoices-summary');
  if (!list || !summary) return;
  const payload = _voiceSourcePayload || {sources:[], total:0, direct_audio:0, errors:[]};
  const sources = payload.sources || [];
  const sourceFilter = $('getvoices-source-filter')?.value || 'all';
  const languageFilter = $('getvoices-language-filter')?.value || 'all';
  const genderFilter = $('getvoices-gender-filter')?.value || 'all';
  const filetypeFilter = $('getvoices-filetype-filter')?.value || 'all';
  const q = ($('getvoices-search')?.value || '').trim().toLowerCase();
  const directOnly = !!$('getvoices-direct-only')?.checked;
  _voiceSourceItems = getVoiceSourceItems();

  summary.innerHTML = [
    [`${payload.total || 0}`, 'Items found'],
    [`${payload.direct_audio || 0}`, 'Direct audio'],
    [`${sources.length}`, 'Sources OK'],
    [`${(payload.errors || []).length}`, 'Errors'],
  ].map(([value, label]) => `<div class="insight"><strong>${escHtml(value)}</strong><span>${escHtml(label)}</span></div>`).join('');

  setOptions('getvoices-source-filter', sources.map(src => src.id).filter(Boolean), 'Source: all');
  const sourceSelect = $('getvoices-source-filter');
  if (sourceSelect) {
    [...sourceSelect.options].forEach(option => {
      if (option.value === 'all') return;
      const src = sources.find(s => s.id === option.value);
      if (src) option.textContent = src.name || src.id;
    });
  }
  const languages = [...new Set(_voiceSourceItems.map(item => item.language || 'Unknown'))].sort((a, b) => a.localeCompare(b));
  const genders = [...new Set(_voiceSourceItems.map(item => item.gender || 'Unknown'))].sort((a, b) => a.localeCompare(b));
  const filetypes = [...new Set(_voiceSourceItems.map(item => (item.file_type || (item.direct_audio ? 'audio' : 'page')).toUpperCase()))].sort((a, b) => a.localeCompare(b));
  setOptions('getvoices-language-filter', languages, 'Language: all');
  setOptions('getvoices-gender-filter', genders, 'Sex: all');
  setOptions('getvoices-filetype-filter', filetypes, 'Filetype: all');

  let items = _voiceSourceItems.filter(item => {
    if (sourceFilter !== 'all' && item.source_id !== sourceFilter) return false;
    if (languageFilter !== 'all' && (item.language || 'Unknown') !== languageFilter) return false;
    if (genderFilter !== 'all' && (item.gender || 'Unknown') !== genderFilter) return false;
    const itemFiletype = (item.file_type || (item.direct_audio ? 'audio' : 'page')).toUpperCase();
    if (filetypeFilter !== 'all' && itemFiletype !== filetypeFilter) return false;
    if (directOnly && !item.direct_audio) return false;
    if (q && !voiceSourceSearchText(item).includes(q)) return false;
    return true;
  });
  const shown = items.slice(0, 240);
  const more = items.length - shown.length;
  if (!shown.length) {
    const errorText = (payload.errors || []).map(e => `${e.source}: ${e.detail}`).join(' | ');
    list.innerHTML = `<div class="card"><p class="note">No matching sources found.${errorText ? ' Source errors: ' + escHtml(errorText) : ''}</p></div>`;
    return;
  }
  list.innerHTML = shown.map(item => {
    const thumb = item.image_url ? `<img class="voice-source-thumb" src="${escHtml(item.image_url)}" alt="" loading="lazy" decoding="async">` : `<div class="voice-source-thumb"></div>`;
    const audio = item.audio_url ? `<audio controls preload="none" src="${escHtml(item.audio_url)}"></audio>` : '';
    const audioLink = item.audio_url ? `<a class="btn-secondary" href="${escHtml(item.audio_url)}" target="_blank" rel="noopener">Open audio</a>` : '';
    const canGetVoice = !!(item.import_url || item.audio_url);
    const importUrl = item.import_url || item.audio_url;
    const getVoice = canGetVoice ? `<button class="btn-primary get-source-voice" data-url="${escHtml(importUrl)}" data-name="${escHtml(item.name || '')}" data-image="${escHtml(item.image_url || '')}" data-language="${escHtml(item.language || '')}" data-gender="${escHtml(item.gender || '')}" data-kind="${escHtml(item.kind || '')}" data-description="${escHtml(item.description || '')}" data-page="${escHtml(item.page_url || item._sourceHomepage || '')}">Import this voice</button>` : '';
    const type = item.file_type ? `<span class="voice-source-pill">${escHtml(String(item.file_type).toUpperCase())}</span>` : '';
    const language = item.language ? `<span class="voice-source-pill">${escHtml(item.language)}</span>` : '';
    const gender = item.gender ? `<span class="voice-source-pill">${escHtml(item.gender)}</span>` : '';
    return `<div class="voice-source-card">
      <div class="voice-source-head">
        ${thumb}
        <div class="voice-source-title">
          <strong title="${escHtml(item.name || '')}">${escHtml(item.name || 'Untitled')}</strong>
          <span>${escHtml(item._sourceName || item.source || '')}${item.category ? ' · ' + escHtml(item.category) : ''}</span>
        </div>
      </div>
      <div class="voice-source-desc">${escHtml(item.kind || '')}${item.description ? ' - ' + escHtml(item.description) : ''}</div>
      ${audio}
      <div class="voice-source-actions">
        ${type}${language}${gender}
        ${audioLink}
        <a class="btn-secondary" href="${escHtml(item.page_url || item._sourceHomepage || '#')}" target="_blank" rel="noopener">Source page</a>
        <button class="btn-secondary copy-source-url" data-url="${escHtml(item.audio_url || item.page_url || '')}">Copy URL</button>
      </div>
      ${getVoice ? `<div class="vs-import-footer">${getVoice}</div>` : ''}
    </div>`;
  }).join('') + (more > 0 ? `<div class="card"><p class="note">${more} more matches. Narrow the search or filters to see them.</p></div>` : '');
}

async function loadGetVoices(force = false) {
  initGetVoiceSourcesEditor();
  if (_voiceSourcePayload && !force) { renderGetVoices(); return; }
  const urls = getEditableVoiceSourceUrls();
  const list = $('getvoices-list');
  if (!urls.length) {
    if (list) list.innerHTML = '<div class="card"><p class="note">Add at least one source URL, then scrape again.</p></div>';
    $('getvoices-status').textContent = 'No sources.';
    return;
  }
  if (list) list.innerHTML = loadingMarkup('Scraping voice sources', `Fetching ${urls.length} source${urls.length === 1 ? '' : 's'} from the editable list.`, 6);
  $('getvoices-status').textContent = 'Scraping...';
  $('getvoices-refresh-btn').disabled = true;
  try {
    const r = await fetch('/api/voice-sources', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({urls}),
    });
    if (!r.ok) {
      const text = await r.text().catch(() => '');
      let message = r.statusText || `HTTP ${r.status}`;
      try { message = JSON.parse(text).detail || message; } catch (_) { if (text) message = text.slice(0, 160); }
      throw new Error(message);
    }
    _voiceSourcePayload = await r.json();
    renderGetVoices();
    const errors = (_voiceSourcePayload.errors || []).length;
    $('getvoices-status').textContent = `${_voiceSourcePayload.total || 0} found${errors ? `, ${errors} source errors` : ''}`;
  } catch(e) {
    if (list) list.innerHTML = `<div class="card"><p style="color:var(--red)">Scrape failed: ${escHtml(e.message)}</p><p class="note">Check that the TTS Voice Creator backend is restarted and that at least one source URL is reachable.</p></div>`;
    $('getvoices-status').textContent = 'Scrape failed';
    toast('Voice source scrape failed: ' + e.message, 'error');
  } finally {
    $('getvoices-refresh-btn').disabled = false;
  }
}

['getvoices-search', 'getvoices-source-filter', 'getvoices-language-filter', 'getvoices-gender-filter', 'getvoices-filetype-filter', 'getvoices-direct-only'].forEach(id => {
  const el = $(id);
  if (el) el.addEventListener(id === 'getvoices-search' ? 'input' : 'change', renderGetVoices);
});
$('getvoices-refresh-btn')?.addEventListener('click', () => loadGetVoices(true));
$('getvoices-reset-sources-btn')?.addEventListener('click', () => {
  const box = $('getvoices-sources');
  if (!box) return;
  box.value = sourceTextareaValueFromDefaults();
  localStorage.setItem(VOICE_SOURCE_STORAGE_KEY, box.value);
  _voiceSourcePayload = null;
  renderGetVoices();
  $('getvoices-status').textContent = 'Source list reset.';
});
const SOURCE_LANGUAGE_CODES = {
  english:'EN', german:'DE', deutsch:'DE', french:'FR', spanish:'ES', japanese:'JA', korean:'KO',
  italian:'IT', portuguese:'PT', russian:'RU', arabic:'AR', polish:'PL', dutch:'NL', swedish:'SV',
  turkish:'TR', hindi:'HI', chinese:'ZH'
};
function sourceLanguageCode(language) {
  const raw = String(language || '').trim();
  if (/^[A-Z]{2}$/.test(raw)) return raw;
  return SOURCE_LANGUAGE_CODES[raw.toLowerCase()] || 'EN';
}
function sourceGenderCode(gender, name = '') {
  const text = `${gender || ''} ${name || ''}`.toLowerCase();
  if (/female|woman|girl|\bf\b/.test(text)) return 'F';
  if (/male|man|boy|\bm\b/.test(text)) return 'M';
  return 'N';
}
function suggestedVoiceIdFromSourceName(name, language = 'EN', gender = 'N') {
  const base = String(name || 'SourceVoice')
    .normalize('NFKD')
    .replace(/[\u0300-\u036f]/g, '')
    .replace(/[^A-Za-z0-9]+/g, '_')
    .replace(/^_+|_+$/g, '')
    .slice(0, 48) || 'SourceVoice';
  return `${language || 'EN'}_${gender || 'N'}_${base}`;
}

function setLibAddSourcePreview(meta = {}) {
  const sourceBox = document.querySelector('.lib-add-source-box');
  if (!sourceBox) return;
  let preview = $('lib-add-source-preview');
  if (!preview) {
    preview = document.createElement('div');
    preview.id = 'lib-add-source-preview';
    preview.className = 'lib-add-source-preview';
    sourceBox.appendChild(preview);
  }
  if (!meta.name && !meta.imageUrl) {
    preview.classList.remove('open');
    preview.innerHTML = '';
    return;
  }
  const image = meta.imageUrl ? `<img src="${escHtml(meta.imageUrl)}" alt="">` : '<div class="voice-source-thumb"></div>';
  preview.innerHTML = `${image}<div style="min-width:0"><strong>${escHtml(meta.name || 'Source voice')}</strong><span>${escHtml([meta.language, meta.gender, meta.kind].filter(Boolean).join(' · ') || 'Source metadata will be saved with the voice')}</span></div>`;
  preview.classList.add('open');
}

async function getSourceVoiceInLibrary(meta) {
  if (!meta.url) { toast('This source has no direct audio URL', 'error'); return; }
  const lang = sourceLanguageCode(meta.language);
  const gender = sourceGenderCode(meta.gender, meta.name);
  const voiceId = suggestedVoiceIdFromSourceName(meta.name, lang, gender);
  toast(`Importing ${meta.name || 'voice'}…`, 'info');
  try {
    const r = await fetch('/api/quick-import-voice', {
      method: 'POST', headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({audio_url: meta.url, voice_id: voiceId, transcript: ''})
    });
    if (!r.ok) { const e = await r.json().catch(()=>({})); throw new Error(e.detail || r.statusText); }
    const d = await r.json();
    // Tag the imported voice with its source (host of the source page) + a picture if any
    let srcTag = 'online';
    try { if (meta.pageUrl) srcTag = new URL(meta.pageUrl).hostname.replace(/^www\./, '').split('.')[0] || 'online'; } catch (_) {}
    if (typeof saveMeta === 'function') {
      await saveMeta(d.voice_id, {
        name: meta.name || d.voice_id, tag: srcTag, group: srcTag, origin: 'cloned',
        gender: (gender || '').toUpperCase(), note: (meta.description || '').slice(0, 180),
      }).catch(() => {});
    }
    if (meta.imageUrl) {
      await fetch('/api/voice/picture-url', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ voice_id: d.voice_id, image_url: meta.imageUrl }),
      }).catch(() => {});
    }
    _pendingSelectId = d.voice_id;
    navTo('s-voices');
    _libraryLoadPromise = null;
    await loadVoiceLibrary();
    toast(`Saved as ${d.voice_id}`, 'success');
  } catch(e) {
    toast('Import failed: ' + e.message, 'error');
  }
}

$('getvoices-list')?.addEventListener('click', async e => {
  const getBtn = e.target.closest('.get-source-voice');
  if (getBtn) {
    await getSourceVoiceInLibrary({
      url: getBtn.dataset.url || '', name: getBtn.dataset.name || '', imageUrl: getBtn.dataset.image || '',
      language: getBtn.dataset.language || '', gender: getBtn.dataset.gender || '', kind: getBtn.dataset.kind || '',
      description: getBtn.dataset.description || '', pageUrl: getBtn.dataset.page || ''
    });
    return;
  }
  const btn = e.target.closest('.copy-source-url');
  if (!btn) return;
  await copyText(btn.dataset.url || '');
  toast('Source URL copied', 'success');
});

