import { Divider, Space, Tree } from 'antd';
import type { DataNode, TreeProps } from 'antd/es/tree';
import type { SpaceSize } from 'antd/lib/space';
import Link from 'antd/lib/typography/Link';
import _ from 'lodash';
import moment from 'moment';
import type { CheckInfo } from 'rc-tree/lib/Tree';
import type { Key, ReactNode } from 'react';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { FormattedMessage } from 'umi';
import CustomBatchDatePicker from '../CustomDatePicker/CustomBatchDatePicker';
import CustomBatchDay from '../CustomDatePicker/CustomBatchDay';
import CustomLabel from '../CustomDatePicker/CustomLabel';
import Flex from '../Flex';
import PermissionParentShow from '../PermissionParentShow';
import { formatPostData, validate, validateDay } from '../utils';
import useAppDetail, { useBatchSetExpires, useGetAppId } from '../utils/hooks';
import { useOptionLevel, useRelation } from './hooks';
import './index.less';
import Level from './Level';
import { dealTreeData } from './util';

export type CheckedType =
  | {
      checked: Key[];
      halfChecked: Key[];
    }
  | Key[];

export interface TreeNode extends DataNode {
  id: number;
  appId?: number;
  /**
   * @description 名称
   */
  name?: string;
  /**
   * @description 有效期，这里传入的值都是秒为单位。
   */
  expiredAt?: any;
  uniqueKey?: string;
  isAllowApply?: 0 | 1;
  children?: TreeNode[];
  parentNode?: TreeNode;
  isBind?: boolean;
}

export interface CustomTreeProps extends TreeProps {
  /**
   * @description 树结构
   */
  treeData: TreeNode[];
  /**
   * @description 整棵树都禁止
   */
  disabled?: boolean;
  /**
   * @description 需要禁用的id列表
   */
  disabledIds?: string[];
  /**
   * 需要联动固定住的id列表
   */
  fixedIds?: string[];
  /**
   * @description 是否需要隐藏不允许申请的内容
   */
  isAllowApply?: boolean;
  /**
   * @description 需要区分的类型
   */
  type?: 'feature' | 'option' | 'city';
  /**
   * @description 是否添加日期，默认不加
   */
  isHaveDate?: boolean;
  /**
   * @description 是否添加天数，默认不加
   */
  isHaveDay?: boolean;
  /**
   * @description 这里是如果需要日期，需要加上老数据和现在value做对比的。
   */
  isHaveCheckBox?: boolean;
  /**
   * @description 运营端配置权限，可配置默认选中。
   */
  oldData?: TreeNode[];
  /**
   * @description 是否需要显示层级，默认显示层级
   */
  isLevel?: boolean;
  /**
   * 只有叶子节点可选，默认都可选
   */
  onlyLeafCheckable?: boolean;
  value?: any;
  onChange?: (v: any) => void;
  appId?: number;
  customRender?: (treeName: React.ReactNode, item: any) => React.ReactNode;
  checkInterceptor?: (checked: CheckedType, info: CheckInfo<any>) => Promise<boolean>;
  isOtherUser?: boolean;
  /** 扩展工具栏渲染 */
  renderToolbar?: () => ReactNode;
  /** 展开收起左侧渲染 */
  renderExpandLeft?: () => ReactNode;
  /** 工具栏间距 */
  toolbarSpaceSize?: SpaceSize | [SpaceSize, SpaceSize];
  fixedHeight?: boolean;
}

const CustomTree = (props: CustomTreeProps) => {
  const {
    value,
    onChange = () => {},
    isLevel = true,
    appId: parentId,
    customRender,
    isOtherUser,
    treeData,
    type,
    oldData,
    isHaveDate = false,
    isHaveDay = false,
    disabled = false,
    disabledIds,
    isAllowApply = false,
    isHaveCheckBox = false,
    checkInterceptor,
    onlyLeafCheckable = false,
    renderToolbar,
    renderExpandLeft,
    toolbarSpaceSize,
    fixedHeight = true,
    fixedIds,
    ...otherProps
  } = props;

  const appId = useGetAppId();
  const { appDetail } = useAppDetail(parentId || appId);
  const { defaultExpirationTime } = appDetail || {};
  const [selectIdMap, setSelectIdMap] = useState<Record<string, TreeNode>>({});
  const { checkStrictly, setCheckStrictly, getRemain } = useRelation();
  const { expandedKeys, setDefaultExpandAll, onExpand, defaultExpandAll } = useOptionLevel({
    treeData,
  });
  const { applyAll, applyNew } = useBatchSetExpires({
    selectIdMap,
    onChange,
    value,
    oldData,
  });

  const flattenTreeData = useMemo(
    () => dealTreeData(treeData)?.filter((item) => item.isAllowApply === 0),
    [treeData],
  );

  const formatTreeDate = useMemo(
    () =>
      formatPostData(treeData, (item: TreeNode): TreeNode => {
        const {
          name,
          key,
          uniqueKey,
          isAllowApply: allowApply,
          disabled: nodeDisabled,
          id: nodeId,
          children,
        } = item || {};
        const id = String(nodeId);
        const currDisabled = nodeDisabled || disabledIds?.includes(id);
        const keyText = key || uniqueKey;
        const title = `${name} ${keyText ? `（${keyText}）` : ''}`;

        return {
          ...item,
          title: isAllowApply ? (
            Boolean(allowApply) ? (
              title
            ) : (
              <FormattedMessage id="component.PermissionParentShow.unapprovalapply" />
            )
          ) : (
            title
          ),
          key: id,
          disabled: isAllowApply ? !allowApply || currDisabled : currDisabled,
          checkable: onlyLeafCheckable
            ? !Boolean(Array.isArray(children) && children.length)
            : undefined,
        };
      }),
    [treeData, isAllowApply, disabledIds, onlyLeafCheckable],
  );

  const defaultRender = useCallback(
    (item: TreeNode) => {
      const {
        id: nodeId,
        isAllowApply: allowApply,
        disabled: nodeDisabled,
        title,
        isBind,
        name,
        appId: nodeAppId,
      } = item || {};
      const id = String(nodeId);
      const currDisabled = nodeDisabled || disabledIds?.includes(id);
      const ele = (
        <>
          {selectIdMap[id] ? (
            <CustomLabel
              disabled={isAllowApply ? !allowApply || currDisabled : currDisabled}
              onChange={onChange}
              setSelectIdMap={setSelectIdMap}
              label={title}
              isHaveDate={isHaveDate}
              isHaveDay={isHaveDay}
              data={value?.data}
              selectId={nodeId}
              isHaveCheckBox={isHaveCheckBox}
              isBind={!isHaveCheckBox && isBind && !isOtherUser}
            />
          ) : (
            title
          )}
        </>
      );
      if (type && type !== 'city') {
        return (
          <>
            {selectIdMap[id] ? (
              <CustomLabel
                disabled={isAllowApply ? !allowApply || currDisabled : currDisabled}
                onChange={onChange}
                setSelectIdMap={setSelectIdMap}
                label={
                  <PermissionParentShow
                    type={type}
                    id={nodeId}
                    showApply={!isAllowApply}
                    name={
                      isAllowApply ? (
                        Boolean(allowApply) ? (
                          name
                        ) : (
                          <span style={{ color: 'yellow' }}>
                            <FormattedMessage id="component.PermissionParentShow.unapprovalapply" />
                          </span>
                        )
                      ) : (
                        name
                      )
                    }
                    appId={nodeAppId}
                    data={item}
                  >
                    {title}
                  </PermissionParentShow>
                }
                isHaveDate={isHaveDate}
                isHaveDay={isHaveDay}
                data={value?.data}
                selectId={nodeId}
                isHaveCheckBox={isHaveCheckBox}
                isBind={!isHaveCheckBox && isBind && !isOtherUser}
              />
            ) : (
              <PermissionParentShow
                type={type}
                id={nodeId}
                showApply={!isAllowApply}
                name={
                  isAllowApply ? (
                    Boolean(allowApply) ? (
                      name
                    ) : (
                      <span style={{ color: 'yellow' }}>
                        <FormattedMessage id="component.PermissionParentShow.unapprovalapply" />
                      </span>
                    )
                  ) : (
                    name
                  )
                }
                appId={nodeAppId}
                data={item}
              >
                {title}
              </PermissionParentShow>
            )}
          </>
        );
      }
      return ele;
    },
    [
      disabledIds,
      isAllowApply,
      isHaveCheckBox,
      isHaveDate,
      isHaveDay,
      isOtherUser,
      onChange,
      selectIdMap,
      type,
      value?.data,
    ],
  );

  const renderTreeNode = useCallback(
    (item: TreeNode) => {
      const ele = defaultRender(item);
      if (type && ['feature', 'city', 'option'].includes(type) && _.isFunction(customRender)) {
        return customRender(ele, item);
      }
      return ele;
    },
    [customRender, defaultRender, type],
  );

  useEffect(() => {
    setSelectIdMap(_.keyBy(value?.data || [], 'id'));
  }, [value]);

  const checkHandler = useCallback(
    async (checked: CheckedType, info: CheckInfo<any>) => {
      if (_.isFunction(checkInterceptor)) {
        const intercept = await checkInterceptor(checked, info);
        if (intercept) return;
      }
      // 触发变更事件
      const remain = getRemain(checked, info, [
        ...(disabledIds || []),
        ...(fixedIds || []),
        ...(isAllowApply ? flattenTreeData : [])?.map((item) => String(item.id)),
      ]);
      const tempSelectIdMap = {};
      remain.forEach((v) => {
        const key = String(v);
        if (selectIdMap[key]) {
          tempSelectIdMap[key] = selectIdMap[key];
        } else {
          tempSelectIdMap[key] = {
            id: v,
            expiredAt: isHaveDate
              ? defaultExpirationTime
                ? defaultExpirationTime * 1000 + moment().valueOf()
                : defaultExpirationTime
              : isHaveDay
              ? defaultExpirationTime
                ? defaultExpirationTime / 60 / 60 / 24
                : defaultExpirationTime
              : undefined,
          };
        }
      });
      setSelectIdMap(tempSelectIdMap);
      if (isHaveDay) {
        validateDay(isHaveDay, tempSelectIdMap, onChange);
      } else {
        validate(isHaveDate, tempSelectIdMap, onChange);
      }
    },
    [
      checkInterceptor,
      defaultExpirationTime,
      disabledIds,
      fixedIds,
      flattenTreeData,
      getRemain,
      isAllowApply,
      isHaveDate,
      isHaveDay,
      onChange,
      selectIdMap,
    ],
  );

  const checkedKeys = value?.data
    ?.filter?.((item: any) => item)
    .map((item: any) => String(item.id));

  return (
    <>
      {Boolean(isLevel) && (
        <Space direction={'vertical'} size={toolbarSpaceSize} style={{ width: '100%' }}>
          {renderToolbar?.()}

          <Flex>
            {!disabled && !onlyLeafCheckable && (
              <Level checkStrictly={checkStrictly} setCheckStrictly={setCheckStrictly} />
            )}

            {renderExpandLeft?.()}

            <Link onClick={() => setDefaultExpandAll(!defaultExpandAll)}>
              <FormattedMessage
                id={`component.CustomTreeSelect.ExpandAll.${Boolean(defaultExpandAll)}`}
              />
            </Link>
          </Flex>

          {Boolean(isHaveDate) && (
            <div>
              <CustomBatchDatePicker applyAll={applyAll} applyNew={applyNew} />
            </div>
          )}

          {Boolean(isHaveDay) && (
            <div>
              <CustomBatchDay applyAll={applyNew} />
            </div>
          )}
        </Space>
      )}

      <Divider />

      <Tree
        checkable
        height={fixedHeight ? window.innerHeight - 55 - 53 - 24 - 24 - 23 : undefined}
        virtual={true}
        disabled={disabled}
        checkStrictly={true}
        {...otherProps}
        treeData={formatTreeDate}
        titleRender={renderTreeNode}
        checkedKeys={checkedKeys}
        expandedKeys={expandedKeys}
        onExpand={onExpand}
        onCheck={checkHandler}
      />
    </>
  );
};
export default memo(CustomTree);
