import {
  createPageContext,
  createSearchContext,
  useContextSection,
  usePage,
  useTableSearchState,
} from '@hooks';
import {
  BaseListResult,
  GetBaseListType,
  ListParams,
  OriginalListParams,
} from '@models';
import {useQuery} from '@tanstack/react-query';
import {shallowEqual} from '@utils';
import {useLatest} from 'ahooks';
import {useEffect, useRef, useState} from 'react';

type UseQueryListResult<
  D extends BaseListResult,
  T extends unknown[] = GetBaseListType<D>[],
> = [
  {
    count: number;
    data: T;
    isFetching: boolean;
  },
  {refetch: () => void},
];

type UseQueryListOptions<
  P extends ListParams,
  R extends BaseListResult,
  T extends Array<unknown> = GetBaseListType<R>[],
> = {
  queryFn: (params: P, signal?: AbortSignal) => R;
  params: OriginalListParams<P>;
  pageContext: ReturnType<typeof createPageContext>;
  searchContext: ReturnType<typeof createSearchContext>;
  formatResult?: (data: Awaited<R>) => T;
};

export function useQueryTableList<
  P extends ListParams,
  R extends BaseListResult,
  T extends Array<unknown> = GetBaseListType<R>[],
>({
  queryFn,
  params,
  pageContext,
  searchContext,
  formatResult,
}: UseQueryListOptions<P, R, T>): UseQueryListResult<R, T> {
  const [count, setCount] = useState(0);
  const [{page, pageSize}, {onCurrentPageChange}] = usePage(pageContext);
  const prevData = useRef<T>([] as unknown as T);
  const prevParams = useRef(params);
  const formatResultFn = useLatest(formatResult);

  const {
    data = [] as unknown as T,
    isFetching,
    refetch,
  } = useQuery<T>({
    queryKey: [queryFn.name, page, pageSize, ...Object.values(params)],
    async queryFn({signal}) {
      /**
       * 当参数发生改变说明请求内容发生变化
       * 如果当前页码不为1需要跳转到第1页查询
       */
      if (page !== 1 && !shallowEqual(prevParams.current, params)) {
        prevParams.current = params;
        onCurrentPageChange(1);
        // 这里可以返回一个空数组
        // 记录上一次数据是为了展示 防止闪烁
        return prevData.current;
      }

      /**
       * 有可能参数发生变化但是页码为1 第一步不会拦截
       * 这里确保参数是最新的参数
       */
      prevParams.current = params;

      const data = await queryFn(
        {
          ...params,
          page: page.toString(),
          limit: pageSize.toString(),
        } as P,
        signal,
      );

      if (data.msg === '200') {
        const {total, list} = data.data;

        /**
         * 场景：当页面只有一个数据的时候 删除了页码改为前一个 但是数据请求还是当前页码
         * 需要判断当前是否无数据并且页码大于1 满足时跳转到前一个页面
         */
        page > 1 && list.length === 0 && onCurrentPageChange(page - 1);

        setCount(total);
      }

      let res: T;

      formatResultFn.current
        ? (res = formatResultFn.current(data))
        : (res =
            data.msg === '200'
              ? (data.data.list as unknown as T)
              : ([] as unknown as T));

      return (prevData.current = res);
    },
    placeholderData: [] as unknown as T,
    keepPreviousData: true,
  });

  const [, searchChange] = useTableSearchState(searchContext);

  // 保持更新搜索表格的查询状态
  useEffect(
    function () {
      searchChange(isFetching);
    },
    [isFetching, searchChange],
  );

  const searchDispatch = useContextSection(searchContext, state => state[1]);

  useEffect(
    function () {
      searchDispatch({type: 'CHANGE_SEARCH_FUNCTION', payload: refetch});
    },
    [refetch, searchDispatch],
  );

  return [
    {
      count,
      data,
      isFetching,
    },
    {refetch},
  ];
}
