// ── Voice Picker ──────────────────────────────────────────────────────────
// Replaces <select> elements with a searchable dropdown that shows voice
// avatars. Usage:
//   VoicePicker.upgrade('tts-voice-select');   // upgrades by select id
//   VoicePicker.populate('tts-voice-select', voices);  // fill options
//   VoicePicker.getValue('tts-voice-select');   // get current value
//   VoicePicker.setValue('tts-voice-select', id); // set value

(function () {
  'use strict';

  const _pickers = {};  // selectId → { root, input, list, select }

  function _voiceData(id) {
    return (window._voices || []).find(v => v.id === id) || null;
  }

  // Shared gender/type avatar icons (male/female/neutral/robot/animal). Returns null
  // when the voice has no avatar key, so callers fall back to photo/flag/letter.
  const VOICE_AVATAR_ICONS = {
    male: 'mdi-face-man', female: 'mdi-face-woman', neutral: 'mdi-account',
    robot: 'mdi-robot-outline', animal: 'mdi-paw',
  };
  const VOICE_AVATAR_COLORS = {
    male: '#3b82f6', female: '#ec4899', neutral: '#6b7280', robot: '#0ea5e9', animal: '#f59e0b',
  };
  window.VOICE_AVATAR_ICONS = VOICE_AVATAR_ICONS;
  window.voiceAvatarIcon = function (avatarKey, size) {
    const icon = VOICE_AVATAR_ICONS[avatarKey];
    if (!icon) return null;
    const s = size + 'px', r = Math.round(size / 2) + 'px';
    const bg = VOICE_AVATAR_COLORS[avatarKey] || '#6b7280';
    return `<span class="vp-avatar" style="width:${s};height:${s};border-radius:${r};background:${bg};color:#fff;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><span class="mdi ${icon}" style="font-size:${Math.round(size * 0.58)}px"></span></span>`;
  };

  function _avatarHtml(id, size) {
    const v = _voiceData(id);
    const s = size + 'px';
    const r = Math.round(size / 2) + 'px';
    if (v?.has_picture) {
      return `<img src="/api/voice/picture/${encodeURIComponent(id)}" class="vp-avatar" style="width:${s};height:${s};border-radius:${r};object-fit:cover;flex-shrink:0" alt="">`;
    }
    const icon = window.voiceAvatarIcon ? window.voiceAvatarIcon(v?.avatar, size) : null;
    if (icon) return icon;
    const lang = v?.lang || '';
    const color = _langColor(lang, id);
    const init  = (id || '?')[0].toUpperCase();
    return `<span class="vp-avatar" style="width:${s};height:${s};border-radius:${r};background:${color};color:#fff;font-weight:700;font-size:${Math.round(size * 0.44)}px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0">${init}</span>`;
  }

  function _langColor(lang, id) {
    const str = (lang || id || '').toLowerCase();
    if (str.startsWith('de')) return '#3b82f6';
    if (str.startsWith('en')) return '#10b981';
    if (str.startsWith('fr')) return '#8b5cf6';
    if (str.startsWith('es')) return '#f59e0b';
    if (str.startsWith('it')) return '#ef4444';
    if (str.startsWith('zh')) return '#ec4899';
    if (str.startsWith('ja')) return '#f97316';
    // deterministic fallback from string hash
    const palette = ['#3b82f6','#10b981','#8b5cf6','#f59e0b','#ef4444','#ec4899','#06b6d4','#84cc16'];
    let h = 0; for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) >>> 0;
    return palette[h % palette.length];
  }

  function _flagSpan(v) {
    if (!v) return '';
    if (v.flag) return `<span class="vp-flag">${v.flag}</span>`;
    return '';
  }

  function _buildItem(id, label) {
    const v = _voiceData(id);
    return `<div class="vp-item" data-value="${_esc(id)}" tabindex="-1">
      ${_avatarHtml(id, 26)}
      <span class="vp-item-name">${_esc(label || id)}</span>
      ${_flagSpan(v)}
    </div>`;
  }

  function _esc(s) {
    return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
  }

  function upgrade(selectId) {
    const sel = document.getElementById(selectId);
    if (!sel || sel.dataset.vpUpgraded) return;
    sel.dataset.vpUpgraded = '1';
    sel.style.display = 'none';

    // Build wrapper
    const root = document.createElement('div');
    root.className = 'vp-root';
    root.dataset.vpFor = selectId;
    sel.parentNode.insertBefore(root, sel);

    // Trigger button
    const trigger = document.createElement('div');
    trigger.className = 'vp-trigger';
    trigger.tabIndex = 0;
    trigger.innerHTML = `<span class="vp-trigger-content"><span class="vp-trigger-placeholder">— select after fetch —</span></span><span class="mdi mdi-chevron-down vp-chevron"></span>`;
    root.appendChild(trigger);

    // Dropdown
    const drop = document.createElement('div');
    drop.className = 'vp-drop';
    drop.hidden = true;
    drop.innerHTML = `<div class="vp-search-wrap"><span class="mdi mdi-magnify vp-search-icon"></span><input class="vp-search" placeholder="Search voices…" autocomplete="off" spellcheck="false"></div><div class="vp-list"></div>`;
    root.appendChild(drop);

    const searchInp = drop.querySelector('.vp-search');
    const list      = drop.querySelector('.vp-list');

    function _renderList(filter) {
      const opts = Array.from(sel.options).filter(o => o.value);
      const q = (filter || '').toLowerCase();
      const filtered = q ? opts.filter(o => o.value.toLowerCase().includes(q) || o.textContent.toLowerCase().includes(q)) : opts;
      if (!filtered.length) { list.innerHTML = '<div class="vp-empty">No voices found</div>'; return; }
      list.innerHTML = filtered.map(o => _buildItem(o.value, o.textContent)).join('');
      // highlight current
      list.querySelectorAll('.vp-item').forEach(el => {
        el.classList.toggle('selected', el.dataset.value === sel.value);
      });
    }

    function _open() {
      drop.hidden = false;
      drop.style.display = '';
      trigger.classList.add('open');
      searchInp.value = '';
      _renderList('');
      searchInp.focus();
      // scroll selected into view
      requestAnimationFrame(() => {
        const sel2 = list.querySelector('.vp-item.selected');
        if (sel2) sel2.scrollIntoView({ block: 'nearest' });
      });
    }

    function _close() {
      drop.hidden = true;
      drop.style.display = 'none';
      trigger.classList.remove('open');
      searchInp.blur();
    }

    function _select(value) {
      sel.value = value;
      sel.dispatchEvent(new Event('change', { bubbles: true }));
      _syncTrigger();
      _close();
      trigger.focus();
    }

    function _syncTrigger() {
      const v = sel.value;
      const content = trigger.querySelector('.vp-trigger-content');
      if (!v) {
        content.innerHTML = '<span class="vp-trigger-placeholder">— select after fetch —</span>';
      } else {
        const label = Array.from(sel.options).find(o => o.value === v)?.textContent || v;
        content.innerHTML = `${_avatarHtml(v, 24)}<span class="vp-trigger-name">${_esc(label)}</span>`;
      }
    }

    trigger.addEventListener('click', () => drop.hidden ? _open() : _close());
    trigger.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); _open(); } });

    searchInp.addEventListener('input', () => _renderList(searchInp.value));
    searchInp.addEventListener('keydown', e => {
      if (e.key === 'Escape') { e.stopPropagation(); _close(); trigger.focus(); }
      if (e.key === 'ArrowDown') { e.preventDefault(); list.querySelector('.vp-item')?.focus(); }
    });

    list.addEventListener('pointerdown', e => {
      const item = e.target.closest('.vp-item');
      if (!item) return;
      e.preventDefault();
      _select(item.dataset.value);
    });
    list.addEventListener('click', e => {
      const item = e.target.closest('.vp-item');
      if (item) _select(item.dataset.value);
    });
    list.addEventListener('keydown', e => {
      const item = e.target.closest('.vp-item');
      if (!item) return;
      if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); _select(item.dataset.value); }
      if (e.key === 'ArrowDown') { e.preventDefault(); (item.nextElementSibling || item).focus(); }
      if (e.key === 'ArrowUp')   { e.preventDefault(); (item.previousElementSibling || item)?.focus() || searchInp.focus(); }
      if (e.key === 'Escape')    { _close(); trigger.focus(); }
    });

    // Close on outside click
    document.addEventListener('click', e => {
      if (!root.contains(e.target)) _close();
    }, true);

    // Any real select change means the user picked something; close the popup.
    sel.addEventListener('change', () => {
      _syncTrigger();
      _close();
    });

    // Watch for programmatic changes to the underlying select
    const mo = new MutationObserver(() => { _syncTrigger(); });
    mo.observe(sel, { childList: true, attributes: true, subtree: true });

    _pickers[selectId] = { root, trigger, drop, searchInp, list, sel, renderList: _renderList, syncTrigger: _syncTrigger };
  }

  function populate(selectId, voices) {
    const p = _pickers[selectId];
    if (!p) return;
    const { sel, renderList, syncTrigger } = p;
    const prev = sel.value;
    sel.innerHTML = '<option value="">— select after fetch —</option>';
    voices.forEach(id => {
      const opt = document.createElement('option');
      opt.value = opt.textContent = id;
      sel.appendChild(opt);
    });
    if (prev && voices.includes(prev)) sel.value = prev;
    renderList('');
    syncTrigger();
  }

  function getValue(selectId) {
    return document.getElementById(selectId)?.value || '';
  }

  function setValue(selectId, value) {
    const sel = document.getElementById(selectId);
    if (!sel) return;
    sel.value = value;
    _pickers[selectId]?.syncTrigger();
  }

  window.VoicePicker = { upgrade, populate, getValue, setValue };
})();
