import { type Ref, nextTick, onMounted, onUnmounted } from 'vue';
import { getTargetScrollBarSize } from 'ant-design-vue/es/_util/getScrollBarSize';
import ResizeObserver from 'resize-observer-polyfill';

type MyHTMLElement = HTMLElement | null;

const TABLE_LAYOUT_FIXED_CLS_NAME = 'table-layout--fixed';
const ANT_TABLE_HEADER_CLS_NAME = 'ant-table-header';
const ANT_TABLE_HEADER_TRUE_CLS_NAME = 'ant-table-thead';
const ANT_TABLE_BODY_CLS_NAME = 'ant-table-body';

// antd 是如何实现 colgroup 页面 resize 后，动态更新 width？

// antd 3.20.x 源码搜索
// ant-design-vue-3.2.20/components/vc-table/Body/MeasureCell.tsx，<VCResizeObserver />
// useProvideResize
// useInjectResize
// updateColsWidths

// 解决两种场景：
// 1. 没有 fixed: 'right' 的 column，只要控制 .ant-table-header > table > colgroup > col 最后一个 col 样式显示隐藏即可
// 2. 有 fixed: 'right' 的 column，且不止一个，控制多个 fixed: 'right' 的 th right 减去滚动条的宽度

// TODO: 考虑 ant-table-summary 和 复杂表头？
// TODO: 考虑空数据场景？✅
// TODO: 考虑缩小（抽屉）✅
// TODO: 考虑性能，暂无发现性能相关问题 ✅

//  a-table 设置了 scroll.x 固定了之后，带有 fixed right 的 columns 不会进行动态计算，没有设置 scroll.x 才会跟随屏幕大小动态计算

export const useDetectOverflow = (tableLayoutEl: Ref<HTMLElement | null>) => {
  onMounted(async () => {
    await nextTick();

    if (
      tableLayoutEl.value &&
      // ✨ 被 table-layout--fixed 包含，肯定是弹性伸缩表格，需要计算滚动条显示隐藏后表格的一些处理
      tableLayoutEl.value.classList.contains(TABLE_LAYOUT_FIXED_CLS_NAME)
    ) {
      const tableEl =
        (tableLayoutEl.value.querySelector(
          '.table-layout__main > .table-layout__main-inner > .table-layout__table > .ant-table-wrapper'
        ) as MyHTMLElement) ||
        (tableLayoutEl.value.querySelector(
          '.table-layout__table > .ant-table-wrapper'
        ) as MyHTMLElement);

      if (tableEl) {
        let tableBodyEl: MyHTMLElement,
          colgroupLastColEl: MyHTMLElement,
          scrollBarWidth = 0;

        const getTableBodyEl = (tableEl: MyHTMLElement) => {
          if (tableBodyEl) return tableBodyEl;
          tableBodyEl = tableEl?.querySelector(
            `.${ANT_TABLE_BODY_CLS_NAME}`
          ) as MyHTMLElement;
          return tableBodyEl;
        };

        const getColgroupLastColEl = (tableEl: MyHTMLElement) => {
          if (colgroupLastColEl) return colgroupLastColEl;
          const colEls = tableEl?.querySelectorAll(
            `.${ANT_TABLE_HEADER_CLS_NAME} > table > colgroup > col`
          );
          if (colEls && colEls.length > 0) {
            colgroupLastColEl = colEls[colEls.length - 1] as MyHTMLElement;
          }
          return colgroupLastColEl;
        };

        const getScrollBarWidth = () => {
          if (scrollBarWidth) return scrollBarWidth;
          tableBodyEl = getTableBodyEl(tableEl);
          if (tableBodyEl)
            scrollBarWidth = getTargetScrollBarSize(tableBodyEl).width;
          return scrollBarWidth;
        };

        // 监听是否 table body 是否有滚动条
        const roHasScrollbar = new ResizeObserver((entries, observer) => {
          const entry = entries[0];
          // 获取最后一个 colgroupLastColEl
          colgroupLastColEl = getColgroupLastColEl(tableEl);
          if (colgroupLastColEl) {
            if (entry.target.scrollHeight > entry.target.clientHeight) {
              // console.log('出现滚动条 :>> ', '出现滚动条');
              colgroupLastColEl.style.display = '';
              tableLayoutEl.value!.classList.remove(
                'table-layout--not-scrollbar'
              );
            } else {
              // console.log('没有出现滚动条 :>> ', '没有出现滚动条');
              colgroupLastColEl.style.display = 'none';
              tableLayoutEl.value!.classList.add('table-layout--not-scrollbar');
            }
          }
        });

        tableBodyEl = getTableBodyEl(tableEl);
        roHasScrollbar.observe(tableBodyEl as Element);

        // ✨ 生成 fixed right 的 column，antd 底层也用了宏任务，这里也需要加上，否则会获取不到
        setTimeout(() => {
          scrollBarWidth = getScrollBarWidth();
          // ✨ 监听最外层 tableHeader，一旦 columns 有变化即可感知处理（覆盖 columns 动态增减，columns 动态配置 fixed: right）
          const tableHeaderEl = tableEl.querySelector(
            `.${ANT_TABLE_HEADER_CLS_NAME}`
          );
          console.log('tableHeaderEl :>> ', tableHeaderEl);
          // 开始监听 fixed: 'right' width 的变化

          const tableLayoutElStyle = tableLayoutEl.value!.style;
          const ro = new ResizeObserver(
            // ✨ debounce 1. 提升性能 2. 延迟获取 item.style.right，更准确
            // debounce((entries, observer) => {
            //   console.log('resize :>> ', 'resize');
            //   let thEls = entries[0].target.querySelectorAll(
            //     `table th.ant-table-cell-fix-right:not(.ant-table-cell-scrollbar)`
            //   ) as unknown as HTMLElement[];
            //   thEls = [...thEls].reverse();
            //   thEls.forEach((item, i) => {
            //     console.log('item.style.right :>> ', item.style.right);
            //     // 使用动态变量赋值的方式处理
            //     tableLayoutElStyle.setProperty(
            //       `--ant-table-cell-fix-right--th-last-${i + 2}`,
            //       `${parseInt(item.style.right) - scrollBarWidth}px`
            //     );
            //   });
            // }, 0)

            (entries, observer) => {
              console.log('resize :>> ', 'resize');
              let thEls = entries[0].target.querySelectorAll(
                `table th.ant-table-cell-fix-right:not(.ant-table-cell-scrollbar)`
              ) as unknown as HTMLElement[];
              thEls = [...thEls].reverse();
              thEls.forEach((item, i) => {
                console.log('item.style.right :>> ', item.style.right);
                // 使用动态变量赋值的方式处理
                tableLayoutElStyle.setProperty(
                  `--ant-table-cell-fix-right--th-last-${i + 2}`,
                  `${parseInt(item.style.right) - scrollBarWidth}px`
                );
              });
            }
          );
          tableHeaderEl && ro.observe(tableHeaderEl);
        });
      }
    }
  });
  onUnmounted(() => {});
};

function throttle<T extends (...args: any[]) => void>(
  func: T,
  delay: number
): (...args: Parameters<T>) => void {
  let waiting = false;
  return function (this: ThisParameterType<T>, ...args: Parameters<T>): void {
    if (!waiting) {
      func.apply(this, args);
      waiting = true;
      setTimeout(() => {
        waiting = false;
      }, delay);
    }
  };
}

function debounce<T extends (...args: any[]) => void>(
  func: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timer: number;
  return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
    clearTimeout(timer);
    timer = window.setTimeout(() => {
      func.apply(this, args);
    }, delay);
  };
}
