/*
 * @Author: 赵伟
 * @Date: 2024-04-15 10:01:29
 * @Description: 自定义 hooks
 */
import { FormInstance } from 'antd';
import { debounce } from 'lodash';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
/**
 * 生成具有初始值的状态引用
 *
 * @param initialValue - 状态的初始值
 * @return 包含状态值、状态设置函数和可变引用对象的数组
 */
export function useStateRef<T>(initialValue: T) {
  const [value, setValue] = useState(initialValue);

  const ref = useRef(value);

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return [value, setValue, ref] as const;
}

/**
 * 生成一个自定义钩子，用于管理模态框的可见性状态。
 *
 * @param initialValue - 模态框的初始可见性状态。
 * @return 一个数组，包含可见性状态和打开和关闭模态框的函数。
 */
export function useVisible(initialValue: boolean) {
  const [visible, setVisible] = useState(initialValue);
  const ref = useRef(initialValue);

  const open = useCallback(() => {
    setVisible(true);
  }, []);

  const close = useCallback(() => {
    setVisible(false);
  }, []);

  useEffect(() => {
    ref.current = visible;
  }, [visible]);

  return [visible, open, close, ref] as const;
}

type Callback<T> = (state: T) => void;

/**
 * 生成一个具有回调机制的可变状态值和更新它的函数。
 *
 * @param initialValue - 初始状态值。
 * @return 一个元组，包含当前状态值和用于更新状态的函数。
 */
export function useCallbackState<T>(initialValue: T) {
  const [state, _setState] = useState<T>(initialValue);
  const callbackQueue = useRef<Callback<T>[]>([]);
  useEffect(() => {
    callbackQueue.current.forEach((cb) => cb(state));
    callbackQueue.current = [];
  }, [state]);
  const setState = (newValue: T | ((prevState: T) => T), callback?: Callback<T>) => {
    _setState(newValue);
    if (callback && typeof callback === 'function') {
      callbackQueue.current.push(callback);
    }
  };
  return [state, setState] as const;
}

/**
 * 用于追踪 DOM 元素尺寸的 hook。
 *
 * @param initialWidth - 初始宽度。
 * @param initialHeight - 初始高度。
 * @param deps - 依赖列表。
 * @return 一个元组，包含 DOM 元素的 ref、当前宽度和当前高度。
 */
export function useDomSize<T extends HTMLElement>(
  initialWidth: number,
  initialHeight: number,
  deps: React.DependencyList = [],
) {
  const domRef = useRef<T>(null);
  const [width, setWidth] = useState(initialWidth);
  const [height, setHeight] = useState(initialHeight);

  useEffect(() => {
    const setDomHeight = () => {
      if (domRef.current) {
        setHeight(domRef.current.offsetHeight);
        setWidth(domRef.current.offsetWidth);
      }
    };
    const debounceFunc = debounce(setDomHeight, 100);

    setDomHeight();
    window.addEventListener('resize', debounceFunc);

    return () => {
      window.removeEventListener('resize', debounceFunc);
    };
  }, [domRef, ...deps]);

  return [domRef, { width, height }] as const;
}

/**
 * 用于在 modal 关闭时重置 Form 表单的 hook。
 *
 * @param form - Ant Design Form 表单实例
 * @param open - modal 是否打开
 */
export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => {
  const prevOpenRef = useRef<boolean>();

  useEffect(() => {
    prevOpenRef.current = open;
  }, [open]);

  const prevOpen = prevOpenRef.current;

  useEffect(() => {
    if (!open && prevOpen) {
      form.resetFields();
    }
  }, [form, prevOpen, open]);
};

/**
 * Executes the effect function when the specified condition is true.
 *
 * @param effect - The effect function to execute.
 * @param deps - The dependencies for the effect.
 * @param when - The condition to trigger the effect.
 */
export const useEffectWhen = (effect: () => void, deps: React.DependencyList, when: boolean) => {
  const requestFns = useRef<(() => void)[]>([]);
  useEffect(() => {
    if (when) {
      effect();
    } else {
      requestFns.current.splice(0, 1, effect);
    }
  }, deps);

  useEffect(() => {
    if (when) {
      const fn = requestFns.current.pop();
      fn?.();
    }
  }, [when]);
};

// 选择、全选操作
export const useCheck = <T>(list: T[]) => {
  const [selected, setSelected] = useState<T[]>([]);

  const checked = useMemo(() => {
    return selected.length === list.length && selected.length > 0;
  }, [selected, list]);

  const indeterminate = useMemo(() => {
    return selected.length > 0 && selected.length < list.length;
  }, [selected, list]);

  const checkAll = useCallback(() => {
    setSelected(checked ? [] : list);
  }, [list, checked]);

  const isSingleChecked = useCallback((item: T) => selected.includes(item), [selected]);

  const checkSingle = useCallback(
    (item: T) => {
      setSelected((prev) => {
        if (isSingleChecked(item)) {
          return prev.filter((i) => i !== item);
        } else {
          return [...prev, item];
        }
      });
    },
    [selected, isSingleChecked],
  );

  return [
    selected,
    setSelected,
    checked,
    indeterminate,
    checkAll,
    isSingleChecked,
    checkSingle,
  ] as const;
};
