/* 美术辅助：DiceBear 头像 + OpenMoji 表情。开源、可自托管。
   头像按 名字/uid 确定性生成（同一人到处头像一致）。 */
(function (global) {
  'use strict';

  // 头像风格池（用户在头像选择器里可换；默认 fun-emoji——按用户偏好全端统一用这套彩色卡通）
  const STYLES = ['fun-emoji', 'notionists', 'lorelei', 'micah', 'bottts', 'adventurer', 'open-peeps', 'thumbs', 'personas'];
  const BG = ['16140f', '2f7d57', 'b05c8e', '3b5fa2', '1f8a4c', '8a5a1f', '5a1f8a', '1f6f8a'];

  function hash(s) {
    let h = 0; s = String(s || '');
    for (let i = 0; i < s.length; i++) { h = (h * 31 + s.charCodeAt(i)) | 0; }
    return Math.abs(h);
  }

  // 返回 DiceBear SVG URL。opts.style 覆盖默认；opts.bg 覆盖背景。
  function avatarUrl(seed, opts) {
    opts = opts || {};
    seed = seed || 'mchat';
    const style = opts.style || 'fun-emoji';
    const bg = opts.bg || BG[hash(seed) % BG.length];
    return 'https://api.dicebear.com/9.x/' + style + '/svg?seed=' + encodeURIComponent(seed) +
      '&backgroundColor=' + bg + (opts.extra ? '&' + opts.extra : '');
  }

  // URL 白名单消毒：只放行 http(s)/data:image，并去掉能逃逸 CSS url('') 或注入脚本的字符
  function safeUrl(u) {
    if (!u) return '';
    u = String(u);
    if (!/^(https?:\/\/|data:image\/)/i.test(u)) return '';
    return u.replace(/['"\\<>\n\r()]/g, '');
  }

  // 网络头像懒加载：进入可视区(±300px)才真正拉图,减轻服务器/DiceBear 压力;
  // 加载失败回退确定性卡通,不再留空白(修"动态头像时显时不显")。data: 内联头像零网络成本,直接铺。
  const _loadedAv = new Set();          // 本页已加载成功的 URL,再次出现直接铺(不重复走 observer)
  let _avObserver;
  function _avObs() {
    if (_avObserver !== undefined) return _avObserver;
    _avObserver = (typeof IntersectionObserver === 'function')
      ? new IntersectionObserver((ens) => ens.forEach(en => { if (en.isIntersecting) { _avObserver.unobserve(en.target); _loadAvPhoto(en.target); } }), { rootMargin: '300px' })
      : null;
    return _avObserver;
  }
  function _setBg(el, url) { el.style.backgroundImage = "url('" + url + "')"; }
  function _rememberLoaded(url) {
    if (_loadedAv.size >= 500) _loadedAv.clear();
    _loadedAv.add(url);
  }
  function _loadAvPhoto(el) {
    const url = el.dataset.avPhoto;
    if (!url || el.dataset.avLoaded === url) return;
    if (_loadedAv.has(url)) { el.dataset.avLoaded = url; _setBg(el, url); return; }
    const img = new Image();
    img.onload = () => { _rememberLoaded(url); el.dataset.avLoaded = url; _setBg(el, url); };
    img.onerror = () => { _setBg(el, avatarUrl(el.dataset.seed)); };
    img.src = url;
  }
  // 给一个 DOM 元素套上头像（优先用真实 photo，否则 DiceBear）
  function paintAvatar(el, { seed, photo, style, bg, square } = {}) {
    if (_avObserver) _avObserver.unobserve(el);
    el.classList.add('av');
    if (square) el.classList.add('sq');
    el.textContent = '';
    el.classList.add('dice');
    el.style.backgroundSize = 'cover';
    el.style.backgroundPosition = 'center';
    el.dataset.seed = seed || '';
    delete el.dataset.avLoaded;
    const url = safeUrl(photo);
    if (url && /^data:/i.test(url)) { el.dataset.avPhoto = url; el.dataset.avLoaded = url; _setBg(el, url); return; }
    if (url) {
      el.dataset.avPhoto = url;
      if (_loadedAv.has(url)) { el.dataset.avLoaded = url; _setBg(el, url); return; }
      const o = _avObs();
      if (o) o.observe(el); else _loadAvPhoto(el);
      return;
    }
    delete el.dataset.avPhoto;
    const dice = avatarUrl(seed, { style, bg });
    el.dataset.avPhoto = dice;
    if (_loadedAv.has(dice)) { el.dataset.avLoaded = dice; _setBg(el, dice); return; }
    const o = _avObs();
    if (o) o.observe(el); else _loadAvPhoto(el);
  }

  // OpenMoji 表情：常用一组（hex 码点）
  const EMOJI = ['1F600', '1F602', '1F60D', '1F60E', '1F973', '1F91D', '1F44D', '1F525',
    '2764', '1F389', '1F98A', '1F43C', '1F431', '1F680', '2615', '1F4AF',
    '1F44C', '1F64F', '1F4A1', '1F4AA', '1F389', '1F602', '1F642', '1F914'];
  function emojiUrl(hex) {
    return 'https://cdn.jsdelivr.net/npm/openmoji@15.0.0/color/svg/' + hex + '.svg';
  }
  // 头像选择器里展示的多风格样本
  function avatarSamples(seedBase) {
    const out = [];
    const pairs = [
      ['fun-emoji', '16140f'], ['fun-emoji', '2f7d57'], ['fun-emoji', 'b05c8e'],
      ['notionists', 'e6ece6'], ['lorelei', 'eee7e0'], ['lorelei', 'e9e6dd'],
      ['micah', 'e7ebe9'], ['micah', 'ece9e1'], ['bottts', '16140f'], ['bottts', '2f7d57'],
      ['big-ears', 'eee7e0'], ['adventurer', 'e9e6dd'], ['croodles', 'ece9e1'],
      ['open-peeps', 'e6ece6'], ['thumbs', '16140f'], ['personas', 'eee7e0'],
    ];
    pairs.forEach((p, i) => out.push(avatarUrl(seedBase + '-' + i, { style: p[0], bg: p[1] })));
    return out;
  }

  global.Art = { avatarUrl, paintAvatar, safeUrl, emojiUrl, EMOJI, STYLES, avatarSamples };
})(window);
