<script lang="ts">
    import type { DrawerInstance, DrawerProps } from './typing'
    import type { CSSProperties } from 'vue'
    import { computed, defineComponent, getCurrentInstance, nextTick, ref, toRaw, unref, watch } from 'vue'
    import { Drawer } from 'ant-design-vue'
    import { useI18n } from '/@/hooks/web/useI18n'
    import { isFunction, isNumber } from '/@/utils/is'
    import { deepMerge } from '/@/utils'
    import DrawerFooter from './components/DrawerFooter.vue'
    import DrawerHeader from './components/DrawerHeader.vue'
    import { ScrollContainer } from '/@/components/Container'
    import { basicProps } from './props'
    import { useDesign } from '/@/hooks/web/useDesign'
    import { useAttrs } from '/@/hooks/core/useAttrs'

    export default defineComponent({
        components: { Drawer, ScrollContainer, DrawerFooter, DrawerHeader },
        inheritAttrs: false,
        props: basicProps,
        emits: ['visible-change', 'ok', 'close', 'register'],
        setup(props, { emit }) {
            const visibleRef = ref(false)
            const attrs = useAttrs()
            const propsRef = ref<Partial<Nullable<DrawerProps>>>(null)

            const { t } = useI18n()
            const { prefixVar, prefixCls } = useDesign('basic-drawer')

            const drawerInstance: DrawerInstance = {
                setDrawerProps,
                emitVisible: undefined,
            }

            const instance = getCurrentInstance()

            instance && emit('register', drawerInstance, instance.uid)

            const getMergeProps = computed((): DrawerProps => {
                return deepMerge(toRaw(props), unref(propsRef))
            })

            const getProps = computed((): DrawerProps => {
                const opt = {
                    placement: 'right',
                    ...unref(attrs),
                    ...unref(getMergeProps),
                    visible: unref(visibleRef),
                }
                opt.title = undefined
                const { isDetail, width, wrapClassName, getContainer } = opt
                if (isDetail) {
                    if (!width) {
                        opt.width = '100%'
                    }
                    const detailCls = `${prefixCls}__detail`
                    opt.class = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls

                    if (!getContainer) {
                        // TODO type error?
                        opt.getContainer = `.${prefixVar}-layout-content` as any
                    }
                }
                return opt as DrawerProps
            })

            const getBindValues = computed((): DrawerProps => {
                return {
                    ...attrs,
                    ...unref(getProps),
                }
            })

            // Custom implementation of the bottom button,
            const getFooterHeight = computed(() => {
                const { footerHeight, showFooter } = unref(getProps)
                if (showFooter && footerHeight) {
                    return isNumber(footerHeight) ? `${footerHeight}px` : `${footerHeight.replace('px', '')}px`
                }
                return `0px`
            })

            const getScrollContentStyle = computed((): CSSProperties => {
                const footerHeight = unref(getFooterHeight)
                return {
                    position: 'relative',
                    height: `calc(100% - ${footerHeight})`,
                }
            })

            const getLoading = computed(() => {
                return !!unref(getProps)?.loading
            })

            watch(
                () => props.visible,
                (newVal, oldVal) => {
                    if (newVal !== oldVal) visibleRef.value = newVal
                },
                { deep: true },
            )

            watch(
                () => visibleRef.value,
                (visible) => {
                    nextTick(() => {
                        emit('visible-change', visible)
                        instance && drawerInstance.emitVisible?.(visible, instance.uid)
                    })
                },
            )

            // Cancel event
            async function onClose(e: Recordable) {
                const { closeFunc } = unref(getProps)
                emit('close', e)
                if (closeFunc && isFunction(closeFunc)) {
                    const res = await closeFunc()
                    visibleRef.value = !res
                    return
                }
                visibleRef.value = false
            }

            function setDrawerProps(props: Partial<DrawerProps>): void {
                // Keep the last setDrawerProps
                propsRef.value = deepMerge(unref(propsRef) || ({} as any), props)

                if (Reflect.has(props, 'visible')) {
                    visibleRef.value = !!props.visible
                }
            }

            function handleOk() {
                emit('ok')
            }

            return {
                onClose,
                t,
                prefixCls,
                getMergeProps: getMergeProps as any,
                getScrollContentStyle,
                getProps: getProps as any,
                getLoading,
                getBindValues,
                getFooterHeight,
                handleOk,
            }
        },
    })
</script>

<template>
    <Drawer :class="prefixCls" @close="onClose" v-bind="getBindValues">
        <template #title v-if="!$slots.title">
            <DrawerHeader
                :title="getMergeProps.title"
                :isDetail="isDetail"
                :showDetailBack="showDetailBack"
                @close="onClose"
            >
                <template #titleToolbar>
                    <slot name="titleToolbar"></slot>
                </template>
            </DrawerHeader>
        </template>
        <template v-else #title>
            <slot name="title"></slot>
        </template>

        <ScrollContainer
            :style="getScrollContentStyle"
            v-loading="getLoading"
            :loading-tip="loadingText || t('common.loadingText')"
        >
            <slot></slot>
        </ScrollContainer>
        <DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight">
            <template #[item]="data" v-for="item in Object.keys($slots)">
                <slot :name="item" v-bind="data || {}"></slot>
            </template>
        </DrawerFooter>
    </Drawer>
</template>

<style lang="less">
    @header-height: 60px;
    @detail-header-height: 40px;
    @prefix-cls: ~'@{namespace}-basic-drawer';
    @prefix-cls-detail: ~'@{namespace}-basic-drawer__detail';

    .@{prefix-cls} {
        .ant-drawer-wrapper-body {
            overflow: hidden;
        }

        .ant-drawer-close {
            &:hover {
                color: @error-color;
            }
        }

        .ant-drawer-body {
            height: calc(100% - @header-height);
            padding: 0;
            background-color: @component-background;

            .scrollbar__wrap {
                padding: 16px !important;
                margin-bottom: 0 !important;
            }

            > .scrollbar > .scrollbar__bar.is-horizontal {
                display: none;
            }
        }
    }

    .@{prefix-cls-detail} {
        position: absolute;

        .ant-drawer-header {
            width: 100%;
            height: @detail-header-height;
            padding: 0;
            border-top: 1px solid @border-color-base;
            box-sizing: border-box;
        }

        .ant-drawer-title {
            height: 100%;
        }

        .ant-drawer-close {
            height: @detail-header-height;
            line-height: @detail-header-height;
        }

        .scrollbar__wrap {
            padding: 0 !important;
        }

        .ant-drawer-body {
            height: calc(100% - @detail-header-height);
        }
    }
</style>
