<template>
  <li :class="getClass">
    <template v-if="!getCollapse">
      <div :class="`${prefixCls}-submenu-title`" @click.stop="handleClick" :style="getItemStyle">
        <slot name="title"></slot>
        <Icon
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <CollapseTransition>
        <ul :class="prefixCls" v-show="state.opened">
          <slot></slot>
        </ul>
      </CollapseTransition>
    </template>

    <a-popover
      placement="right"
      :overlayClassName="`${prefixCls}-menu-popover`"
      v-else
      :open="getIsOpened"
      @on-open-change="handleVisibleChange"
      :overlayStyle="getOverlayStyle"
      :overlayInnerStyle="{ padding: 0 }"
      :align="{ offset: [0, 0] }"
    >
      <div :class="getSubClass" v-bind="getEvents(false)">
        <div
          :class="[
            {
              [`${prefixCls}-submenu-popup`]: !getParentSubMenu,
              [`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle,
            },
          ]"
        >
          <slot name="title"></slot>
        </div>
        <Icon
          v-if="getParentSubMenu"
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <!-- eslint-disable-next-line -->
      <template #content v-show="state.opened">
        <div v-bind="getEvents(true)">
          <ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`, `${isThemeBright && 'bright'}`]">
            <slot></slot>
          </ul>
        </div>
      </template>
    </a-popover>
  </li>
</template>
<script lang="ts" setup>
  import { type TimeoutHandle, type Recordable } from '#/utils.d';
  import type { SubMenuProvider } from './types';
  import { useDesign } from '@/hooks/web/useDesign';
  import { useMenuItem } from './useMenu';
  import { useSimpleRootMenuContext } from './useSimpleMenuContext';
  import { CollapseTransition } from '@/components/Transition';
  import { mitt } from '@/utils/mitt';

  defineOptions({ name: 'SubMenu' });

  const props = defineProps({
    name: {
      type: [String, Number] as PropType<string | number>,
      required: true,
    },
    disabled: {
      type: Boolean,
    },
    collapsedShowTitle: {
      type: Boolean,
    },
    isThemeBright: {
      type: Boolean,
      default: false,
    },
  });

  const DELAY = 200;
  const instance = getCurrentInstance();

  const state = reactive({
    active: false,
    opened: false,
  });

  const data = reactive({
    timeout: null as TimeoutHandle | null,
    mouseInChild: false,
    isChild: false,
  });

  const { prefixCls } = useDesign('menu');

  const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = useMenuItem(instance);

  const subMenuEmitter = mitt();

  const { rootMenuEmitter } = useSimpleRootMenuContext();

  const {
    addSubMenu: parentAddSubmenu,
    removeSubMenu: parentRemoveSubmenu,
    removeAll: parentRemoveAll,
    getOpenNames: parentGetOpenNames,
    isRemoveAllPopup,
    sliceIndex,
    level,
    props: rootProps,
    handleMouseleave: parentHandleMouseleave,
  } = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!;

  const getClass = computed(() => {
    return [
      `${prefixCls}-submenu`,
      {
        [`${prefixCls}-opened`]: state.opened,
        [`${prefixCls}-submenu-disabled`]: props.disabled,
        [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
        [`${prefixCls}-child-item-active`]: state.active,
      },
    ];
  });

  const getAccordion = computed(() => rootProps.accordion);
  const getCollapse = computed(() => rootProps.collapse);
  const getTheme = computed(() => rootProps.theme);

  const getOverlayStyle = computed((): CSSProperties => {
    return {
      minWidth: '150px',
    };
  });

  const getIsOpened = computed(() => {
    const name = props.name;
    if (unref(getCollapse)) {
      return parentGetOpenNames().includes(name);
    }
    return state.opened;
  });

  const getSubClass = computed(() => {
    const isActive = rootProps.activeSubMenuNames.includes(props.name);
    return [
      `${prefixCls}-submenu-title`,
      {
        [`${prefixCls}-submenu-active`]: isActive,
        [`${prefixCls}-submenu-active-border`]: isActive && level === 0,
        [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0,
      },
    ];
  });

  function getEvents(deep: boolean) {
    if (!unref(getCollapse)) {
      return {};
    }
    return {
      onMouseenter: handleMouseenter,
      onMouseleave: () => handleMouseleave(deep),
    };
  }

  function handleClick() {
    const { disabled } = props;
    if (disabled || unref(getCollapse)) return;
    const opened = state.opened;
    if (unref(getAccordion)) {
      const { uidList } = getParentList();
      rootMenuEmitter.emit('on-update-opened', {
        opened: false,
        parent: instance?.parent,
        uidList: uidList,
      });
    } else {
      rootMenuEmitter.emit('open-name-change', {
        name: props.name,
        opened: !opened,
      });
    }
    state.opened = !opened;
  }

  function handleMouseenter() {
    const disabled = props.disabled;
    if (disabled) return;
    subMenuEmitter.emit('submenu:mouse-enter-child');
    const index = parentGetOpenNames().findIndex((item) => item === props.name);
    sliceIndex(index);
    const isRoot = level === 0 && parentGetOpenNames().length === 2;
    if (isRoot) {
      parentRemoveAll();
    }
    data.isChild = parentGetOpenNames().includes(props.name);
    clearTimeout(data.timeout!);
    data.timeout = setTimeout(() => {
      parentAddSubmenu(props.name);
    }, DELAY);
  }

  function handleMouseleave(deepDispatch = false) {
    const parentName = getParentMenu.value?.props.name;
    if (!parentName) {
      isRemoveAllPopup.value = true;
    }
    if (parentGetOpenNames().slice(-1)[0] === props.name) {
      data.isChild = false;
    }
    subMenuEmitter.emit('submenu:mouse-leave-child');
    if (data.timeout) {
      clearTimeout(data.timeout!);
      data.timeout = setTimeout(() => {
        if (isRemoveAllPopup.value) {
          parentRemoveAll();
        } else if (!data.mouseInChild) {
          parentRemoveSubmenu(props.name);
        }
      }, DELAY);
    }
    if (deepDispatch) {
      if (getParentSubMenu.value) {
        parentHandleMouseleave?.(true);
      }
    }
  }

  onBeforeMount(() => {
    subMenuEmitter.on('submenu:mouse-enter-child', () => {
      data.mouseInChild = true;
      isRemoveAllPopup.value = false;
      clearTimeout(data.timeout!);
    });
    subMenuEmitter.on('submenu:mouse-leave-child', () => {
      if (data.isChild) return;
      data.mouseInChild = false;
      clearTimeout(data.timeout!);
    });
    rootMenuEmitter.on(
      'on-update-opened',
      (data: boolean | (string | number)[] | Recordable<any>) => {
        if (unref(getCollapse)) return;
        if (_isBoolean(data)) {
          state.opened = data;
          return;
        }
        if (_isObject(data) && rootProps.accordion) {
          const { opened, parent, uidList } = data as Recordable<any>;
          if (parent === instance?.parent) {
            state.opened = opened;
          } else if (!uidList || !uidList.includes(instance?.uid)) {
            state.opened = false;
          }
          return;
        }
        if (props.name && Array.isArray(data)) {
          state.opened = (data as (string | number)[]).includes(props.name);
        }
      },
    );
    rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
      if (instance?.uid) {
        state.active = data.includes(instance?.uid);
      }
    });
  });

  function handleVisibleChange(visible: boolean) {
    state.opened = visible;
  }

  // provide
  provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
    addSubMenu: parentAddSubmenu,
    removeSubMenu: parentRemoveSubmenu,
    getOpenNames: parentGetOpenNames,
    removeAll: parentRemoveAll,
    isRemoveAllPopup,
    sliceIndex,
    level: level + 1,
    handleMouseleave,
    props: rootProps,
  });
</script>
