<script lang="ts">
    import type { CSSProperties, PropType } from 'vue'
    import type { SubMenuProvider } from './types'
    import {
        computed,
        defineComponent,
        getCurrentInstance,
        inject,
        onBeforeMount,
        provide,
        reactive,
        toRefs,
        unref,
    } from 'vue'
    import { useDesign } from '/@/hooks/web/useDesign'
    import { propTypes } from '/@/utils/propTypes'
    import { useMenuItem } from './useMenu'
    import { useSimpleRootMenuContext } from './useSimpleMenuContext'
    import { CollapseTransition } from '/@/components/Transition'
    import Icon from '/@/components/Icon'
    import { Popover } from 'ant-design-vue'
    import { isBoolean, isObject } from '/@/utils/is'
    import mitt from '/@/utils/mitt'

    const DELAY = 200
    export default defineComponent({
        name: 'SubMenu',
        components: {
            Icon,
            CollapseTransition,
            Popover,
        },
        props: {
            name: {
                type: [String, Number] as PropType<string | number>,
                required: true,
            },
            disabled: propTypes.bool,
            collapsedShowTitle: propTypes.bool,
        },
        setup(props) {
            const instance = getCurrentInstance()

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

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

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

            const { prefixCls } = useDesign('menu')

            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}-item-active`]: state.active,
                        [`${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: '200px',
                }
            })

            const getIsOpend = 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', {
                        opend: false,
                        parent: instance?.parent,
                        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) => {
                    if (unref(getCollapse)) return
                    if (isBoolean(data)) {
                        state.opened = data
                        return
                    }
                    if (isObject(data) && rootProps.accordion) {
                        const { opend, parent, uidList } = data as Recordable
                        if (parent === instance?.parent) {
                            state.opened = opend
                        } else if (!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,
            })

            return {
                getClass,
                prefixCls,
                getCollapse,
                getItemStyle,
                handleClick,
                handleVisibleChange,
                getParentSubMenu,
                getOverlayStyle,
                getTheme,
                getIsOpend,
                getEvents,
                getSubClass,
                ...toRefs(state),
                ...toRefs(data),
            }
        },
    })
</script>

<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="opened">
                    <slot></slot>
                </ul>
            </CollapseTransition>
        </template>

        <Popover
            placement="right"
            :overlayClassName="`${prefixCls}-menu-popover`"
            v-else
            :visible="getIsOpend"
            @visible-change="handleVisibleChange"
            :overlayStyle="getOverlayStyle"
            :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="opened">
                <div v-bind="getEvents(true)">
                    <ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]">
                        <slot></slot>
                    </ul>
                </div>
            </template>
        </Popover>
    </li>
</template>
