/**
 * v-scroll-proxy
 *
 * 唯一职责: 把子元素 (.child-scroll) 的原生滚动条样式同步绘制到父元素上
 *
 * 公式 (重要):
 *   ratio = ch / sh           (子可视 / 子内容总高, 表示 "能看到多少比例的内容")
 *   thumbHeight = eh × ratio  (父可视 × ratio, 但限制在 [20, eh])
 *   maxThumbTop = eh - thumbHeight
 *   ratioTop = scrollTop / (sh - ch)
 *   translateY = ratioTop × maxThumbTop
 *
 * 不做的事:
 *   - 不拦截 wheel / touch 事件
 *   - 不修改 scrollTop
 *   - 不修改 child 的任何样式
 *
 * 只做的事:
 *   - 在父元素右上角绘制一个自定义滚动条
 *   - 监听所有可能改变内容高度的事件, 动态更新
 */
export const vScrollProxy = {
  mounted(el) {
    const child = el.querySelector('.child-scroll');
    if (!child) return;

    // ---------- 滚动条 DOM ----------
    const scrollbar = document.createElement('div');
    const thumb = document.createElement('div');
    scrollbar.className = '__v-scroll-proxy-scrollbar';
    thumb.className = '__v-scroll-proxy-thumb';
    scrollbar.appendChild(thumb);

    scrollbar.style.cssText =
      'position:absolute;top:0;right:0;width:8px;height:100%;' +
      'background:transparent;border-radius:4px;z-index:9999;' +
      'opacity:0;transition:opacity .25s ease;pointer-events:none;' +
      'overflow:hidden;';

    thumb.style.cssText =
      'position:absolute;top:0;right:1px;width:6px;' +
      'background:rgba(0,0,0,.35);border-radius:3px;' +
      'will-change:transform;';

    if (getComputedStyle(el).position === 'static') {
      el.style.position = 'relative';
    }
    el.appendChild(scrollbar);

    // ---------- 显示/隐藏 ----------
    let hideTimer = 0;
    const showScrollbar = () => {
      scrollbar.style.opacity = '1';
      clearTimeout(hideTimer);
      hideTimer = window.setTimeout(() => {
        scrollbar.style.opacity = '0';
      }, 900);
    };

    // ---------- 布局缓存 ----------
    let scrollable = 0;
    let thumbHeight = 20;
    let maxThumbTop = 0;

    /**
     * 重新计算布局 (核心)
     *
     * 比例 = 子可视 / 子内容总高 = ch / sh
     * thumb 高度 = 父可视 × 比例 = eh × (ch / sh)
     */
    const refreshLayout = () => {
      const sh = child.scrollHeight;
      const ch = child.clientHeight;
      const eh = el.clientHeight;
      scrollable = Math.max(sh - ch, 0);

      let ratio;
      if (sh > 0 && ch > 0) {
        ratio = ch / sh;
      } else {
        ratio = 1;
      }

      let h = eh * ratio;
      // 限制: [20px, eh]
      thumbHeight = Math.min(Math.max(h, 20), eh);
      maxThumbTop = Math.max(eh - thumbHeight, 0);
    };

    // ---------- 更新 thumb 位置 ----------
    const updateThumb = () => {
      if (scrollable <= 0) {
        scrollbar.style.display = 'none';
        return;
      }
      scrollbar.style.display = 'block';

      if (thumb._h !== thumbHeight) {
        thumb.style.height = thumbHeight + 'px';
        thumb._h = thumbHeight;
      }

      const ratio = child.scrollTop / scrollable;
      const ty = (ratio * maxThumbTop).toFixed(2);
      thumb.style.transform = `translateY(${ty}px)`;
    };

    const fullUpdate = () => {
      refreshLayout();
      updateThumb();
    };

    // ---------- RAF 调度 ----------
    let layoutRafId = 0;
    let scrollRafId = 0;

    const scheduleLayout = () => {
      if (layoutRafId) return;
      layoutRafId = requestAnimationFrame(() => {
        layoutRafId = 0;
        fullUpdate();
      });
    };

    // scroll 事件: 既要更新 thumb 位置, 也要重算 thumb 高度
    // (滚动过程中内容可能动态变化, 例如虚拟滚动到底加载)
    const onChildScroll = () => {
      if (scrollRafId) return;
      scrollRafId = requestAnimationFrame(() => {
        scrollRafId = 0;
        fullUpdate();
      });
      showScrollbar();
    };
    child.addEventListener('scroll', onChildScroll, { passive: true });

    // ---------- ResizeObserver: 监听尺寸变化 ----------
    const ro = new ResizeObserver(() => {
      scheduleLayout();
    });
    ro.observe(el);
    ro.observe(child);
    // 也观察子元素 (列表项图片加载完会撑高)
    const observeKids = () => {
      const kids = child.children;
      for (let i = 0; i < kids.length; i++) {
        ro.observe(kids[i]);
      }
    };
    observeKids();

    // ---------- MutationObserver: 监听 DOM 变化 ----------
    const mo = new MutationObserver(() => {
      scheduleLayout();
      // 重新挂载观察 (新加的列表项)
      observeKids();
    });
    mo.observe(child, {
      childList: true,
      subtree: true,
      characterData: true,
    });

    // ---------- 图片加载: 也是导致高度变化的常见原因 ----------
    const onImgLoad = () => scheduleLayout();
    child.addEventListener('load', onImgLoad, true);

    // ---------- 初始化: 延迟两帧 + 多重兜底 ----------
    // 第一帧: DOM 已挂载
    // 第二帧: Vue 子组件 mount 完
    // 100ms: 网络请求回来的内容大致渲染完
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        fullUpdate();
      });
    });
    setTimeout(() => fullUpdate(), 100);
    setTimeout(() => fullUpdate(), 500);
    setTimeout(() => fullUpdate(), 1500);

    // ---------- 卸载清理 ----------
    el.__scrollProxyCleanup__ = () => {
      if (scrollRafId) cancelAnimationFrame(scrollRafId);
      if (layoutRafId) cancelAnimationFrame(layoutRafId);
      clearTimeout(hideTimer);
      ro.disconnect();
      mo.disconnect();
      child.removeEventListener('scroll', onChildScroll);
      child.removeEventListener('load', onImgLoad, true);
      scrollbar.remove();
    };
  },

  unmounted(el) {
    if (typeof el.__scrollProxyCleanup__ === 'function') {
      el.__scrollProxyCleanup__();
      delete el.__scrollProxyCleanup__;
    }
  },
};

export default vScrollProxy;