<script lang="ts">
    import type { ModalMethods, ModalProps } from './typing'

    import { computed, defineComponent, getCurrentInstance, nextTick, ref, toRef, unref, watch, watchEffect } from 'vue'
    import Modal from './components/Modal'
    import ModalWrapper from './components/ModalWrapper.vue'
    import ModalClose from './components/ModalClose.vue'
    import ModalFooter from './components/ModalFooter.vue'
    import ModalHeader from './components/ModalHeader.vue'
    import { isFunction } from '/@/utils/is'
    import { deepMerge } from '/@/utils'
    import { basicProps } from './props'
    import { useFullScreen } from './hooks/useModalFullScreen'
    import { omit } from 'lodash-es'
    import { useDesign } from '/@/hooks/web/useDesign'

    export default defineComponent({
        name: 'BasicModal',
        components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
        inheritAttrs: false,
        props: basicProps,
        emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'],
        setup(props, { emit, attrs }) {
            const visibleRef = ref(false)
            const propsRef = ref<Partial<ModalProps> | null>(null)
            const modalWrapperRef = ref<any>(null)
            const { prefixCls } = useDesign('basic-modal')

            // modal   Bottom and top height
            const extHeightRef = ref(0)
            const modalMethods: ModalMethods = {
                setModalProps,
                emitVisible: undefined,
                redoModalHeight: () => {
                    nextTick(() => {
                        if (unref(modalWrapperRef)) {
                            ;(unref(modalWrapperRef) as any).setModalHeight()
                        }
                    })
                },
            }

            const instance = getCurrentInstance()
            if (instance) {
                emit('register', modalMethods, instance.uid)
            }

            // Custom title component: get title
            const getMergeProps = computed((): Recordable => {
                return {
                    ...props,
                    ...(unref(propsRef) as any),
                }
            })

            const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({
                modalWrapperRef,
                extHeightRef,
                wrapClassName: toRef(getMergeProps.value, 'wrapClassName'),
            })

            // modal component does not need title and origin buttons
            const getProps = computed((): Recordable => {
                const opt = {
                    ...unref(getMergeProps),
                    visible: unref(visibleRef),
                    okButtonProps: undefined,
                    cancelButtonProps: undefined,
                    title: undefined,
                }
                return {
                    ...opt,
                    wrapClassName: unref(getWrapClassName),
                }
            })

            const getBindValue = computed((): Recordable => {
                const attr = {
                    ...attrs,
                    ...unref(getMergeProps),
                    visible: unref(visibleRef),
                    wrapClassName: unref(getWrapClassName),
                }
                if (unref(fullScreenRef)) {
                    return omit(attr, ['height', 'title'])
                }
                return omit(attr, 'title')
            })

            const getWrapperHeight = computed(() => {
                if (unref(fullScreenRef)) return undefined
                return unref(getProps).height
            })

            watchEffect(() => {
                visibleRef.value = !!props.visible
                fullScreenRef.value = !!props.defaultFullscreen
            })

            watch(
                () => unref(visibleRef),
                (v) => {
                    emit('visible-change', v)
                    emit('update:visible', v)
                    instance && modalMethods.emitVisible?.(v, instance.uid)
                    nextTick(() => {
                        if (props.scrollTop && v && unref(modalWrapperRef)) {
                            ;(unref(modalWrapperRef) as any).scrollTop()
                        }
                    })
                },
                {
                    immediate: false,
                },
            )

            // 取消事件
            async function handleCancel(e: Event) {
                e?.stopPropagation()
                // 过滤自定义关闭按钮的空白区域
                if ((e.target as HTMLElement)?.classList?.contains(`${prefixCls}-close--custom`)) return
                if (props.closeFunc && isFunction(props.closeFunc)) {
                    const isClose: boolean = await props.closeFunc()
                    visibleRef.value = !isClose
                    return
                }

                visibleRef.value = false
                emit('cancel', e)
            }

            /**
             * @description: 设置modal参数
             */
            function setModalProps(props: Partial<ModalProps>): void {
                // Keep the last setModalProps
                propsRef.value = deepMerge(unref(propsRef) || ({} as any), props)
                if (Reflect.has(props, 'visible')) {
                    visibleRef.value = !!props.visible
                }
                if (Reflect.has(props, 'defaultFullscreen')) {
                    fullScreenRef.value = !!props.defaultFullscreen
                }
            }

            function handleOk(e: Event) {
                emit('ok', e)
            }

            function handleHeightChange(height: string) {
                emit('height-change', height)
            }

            function handleExtHeight(height: number) {
                extHeightRef.value = height
            }

            function handleTitleDbClick(e) {
                if (!props.canFullscreen) return
                e.stopPropagation()
                handleFullScreen(e)
            }

            return {
                handleCancel,
                getBindValue,
                getProps,
                handleFullScreen,
                fullScreenRef,
                getMergeProps,
                handleOk,
                visibleRef,
                omit,
                modalWrapperRef,
                handleExtHeight,
                handleHeightChange,
                handleTitleDbClick,
                getWrapperHeight,
            }
        },
    })
</script>

<template>
    <Modal v-bind="getBindValue" @cancel="handleCancel">
        <template #closeIcon v-if="!$slots.closeIcon">
            <ModalClose
                :canFullscreen="getProps.canFullscreen"
                :fullScreen="fullScreenRef"
                @cancel="handleCancel"
                @fullscreen="handleFullScreen"
            />
        </template>

        <template #title v-if="!$slots.title">
            <ModalHeader
                :helpMessage="getProps.helpMessage"
                :title="getMergeProps.title"
                @dblclick="handleTitleDbClick"
            />
        </template>

        <template #footer v-if="!$slots.footer">
            <ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel">
                <template #[item]="data" v-for="item in Object.keys($slots)">
                    <slot :name="item" v-bind="data || {}"></slot>
                </template>
            </ModalFooter>
        </template>

        <ModalWrapper
            :useWrapper="getProps.useWrapper"
            :footerOffset="wrapperFooterOffset"
            :fullScreen="fullScreenRef"
            ref="modalWrapperRef"
            :loading="getProps.loading"
            :loading-tip="getProps.loadingTip"
            :minHeight="getProps.minHeight"
            :height="getWrapperHeight"
            :visible="visibleRef"
            :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
            v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')"
            @ext-height="handleExtHeight"
            @height-change="handleHeightChange"
        >
            <slot></slot>
        </ModalWrapper>

        <template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))">
            <slot :name="item" v-bind="data || {}"></slot>
        </template>
    </Modal>
</template>
