import { h, vShow, withDirectives, Transition, ref, defineComponent, computed, mergeProps, inject, onBeforeUnmount, watch, toRef, provide, renderSlot } from 'vue';
import { VFollower } from 'vueuc';
import { clickoutside, mousemoveoutside } from 'vdirs';
import { useTheme, useConfig } from '../../_mixins';
import { formatLength, useAdjustedTo } from '../../_utils';
import { popoverLight } from '../styles';
import style from './styles/index.cssr';
import { popoverBodyInjectionKey } from './interface';
import { drawerBodyInjectionKey } from '../../drawer/src/interface';
import { modalBodyInjectionKey } from '../../modal/src/interface';
export const popoverBodyProps = Object.assign(Object.assign({}, useTheme.props), { to: useAdjustedTo.propTo, show: Boolean, trigger: String, showArrow: Boolean, delay: Number, duration: Number, raw: Boolean, arrowStyle: [String, Object], displayDirective: String, x: Number, y: Number, filp: Boolean, overlap: Boolean, placement: String, width: [Number, String], 
    // private
    shadow: Boolean, padded: Boolean, animated: Boolean, 
    /** @deprecated */
    minWidth: Number, maxWidth: Number });
export default defineComponent({
    name: 'PopoverBody',
    inheritAttrs: false,
    props: popoverBodyProps,
    setup(props, { slots, attrs }) {
        const { namespaceRef, mergedClsPrefixRef } = useConfig(props);
        const themeRef = useTheme('Popover', 'Popover', style, popoverLight, props, mergedClsPrefixRef);
        const followerRef = ref(null);
        const NPopover = inject('NPopover');
        const bodyRef = ref(null);
        const followerEnabledRef = ref(props.show);
        const directivesRef = computed(() => {
            const { trigger } = props;
            const directives = [];
            const { positionManuallyRef: { value: positionManually } } = NPopover;
            if (!positionManually) {
                if (trigger === 'click') {
                    directives.push([clickoutside, handleClickOutside]);
                }
                if (trigger === 'hover') {
                    directives.push([mousemoveoutside, handleMouseMoveOutside]);
                }
            }
            if (props.displayDirective === 'show') {
                directives.push([vShow, props.show]);
            }
            return directives;
        });
        const styleRef = computed(() => {
            return [
                {
                    width: props.width === 'trigger' ? '' : formatLength(props.width),
                    maxWidth: formatLength(props.maxWidth),
                    minWidth: formatLength(props.minWidth)
                },
                cssVarsRef.value
            ];
        });
        const cssVarsRef = computed(() => {
            const { common: { cubicBezierEaseInOut, cubicBezierEaseIn, cubicBezierEaseOut }, self: { space, spaceArrow, padding, fontSize, textColor, color, boxShadow, borderRadius, arrowHeight, arrowOffset, arrowOffsetVertical } } = themeRef.value;
            return {
                '--box-shadow': boxShadow,
                '--bezier': cubicBezierEaseInOut,
                '--bezier-ease-in': cubicBezierEaseIn,
                '--bezier-ease-out': cubicBezierEaseOut,
                '--font-size': fontSize,
                '--text-color': textColor,
                '--color': color,
                '--border-radius': borderRadius,
                '--arrow-height': arrowHeight,
                '--arrow-offset': arrowOffset,
                '--arrow-offset-vertical': arrowOffsetVertical,
                '--padding': padding,
                '--space': space,
                '--space-arrow': spaceArrow
            };
        });
        NPopover.setBodyInstance({
            syncPosition
        });
        onBeforeUnmount(() => {
            NPopover.setBodyInstance(null);
        });
        watch(toRef(props, 'show'), (value) => {
            // If no animation, no transition component will be applied to the
            // component. So we need to trigger follower manaully.
            if (props.animated)
                return;
            if (value) {
                followerEnabledRef.value = true;
            }
            else {
                followerEnabledRef.value = false;
            }
        });
        function syncPosition() {
            var _a;
            // eslint-disable-next-line no-unused-expressions
            (_a = followerRef.value) === null || _a === void 0 ? void 0 : _a.syncPosition();
        }
        function handleMouseEnter(e) {
            if (props.trigger === 'hover') {
                NPopover.handleMouseEnter(e);
            }
        }
        function handleMouseLeave(e) {
            if (props.trigger === 'hover') {
                NPopover.handleMouseLeave(e);
            }
        }
        function handleMouseMoveOutside(e) {
            if (props.trigger === 'hover' &&
                !getTriggerElement().contains(e.target)) {
                NPopover.handleMouseMoveOutside(e);
            }
        }
        function handleClickOutside(e) {
            if (props.trigger === 'click' &&
                !getTriggerElement().contains(e.target)) {
                NPopover.handleClickOutside(e);
            }
        }
        function getTriggerElement() {
            return NPopover.getTriggerElement();
        }
        provide(popoverBodyInjectionKey, bodyRef);
        provide(drawerBodyInjectionKey, null);
        provide(modalBodyInjectionKey, null);
        function renderContentNode() {
            const mergedClsPrefix = mergedClsPrefixRef.value;
            const extraClass = NPopover.extraClassRef.value;
            return props.displayDirective === 'show' || props.show
                ? withDirectives(h('div', mergeProps({
                    class: [
                        `${mergedClsPrefix}-popover`,
                        extraClass && `${mergedClsPrefix}-${extraClass}`,
                        {
                            [`${mergedClsPrefix}-popover--overlap`]: props.overlap,
                            [`${mergedClsPrefix}-popover--no-arrow`]: !props.showArrow,
                            [`${mergedClsPrefix}-popover--shadow`]: props.shadow,
                            [`${mergedClsPrefix}-popover--padded`]: props.padded,
                            [`${mergedClsPrefix}-popover--raw`]: props.raw
                        }
                    ],
                    ref: bodyRef,
                    style: styleRef.value,
                    onMouseenter: handleMouseEnter,
                    onMouseleave: handleMouseLeave
                }, attrs), [
                    renderSlot(slots, 'default'),
                    props.showArrow ? (h("div", { class: `${mergedClsPrefix}-popover-arrow-wrapper`, key: "__popover-arrow__" },
                        h("div", { class: `${mergedClsPrefix}-popover-arrow`, style: props.arrowStyle }))) : null
                ]), directivesRef.value)
                : null;
        }
        return {
            namespace: namespaceRef,
            NPopover,
            followerRef,
            adjustedTo: useAdjustedTo(props),
            followerEnabled: followerEnabledRef,
            renderContentNode
        };
    },
    render() {
        return h(VFollower, {
            show: this.show,
            enabled: this.followerEnabled,
            to: this.adjustedTo,
            x: this.x,
            y: this.y,
            placement: this.placement,
            containerClass: this.namespace,
            ref: 'followerRef',
            overlap: this.overlap,
            width: this.width === 'trigger' ? 'target' : undefined,
            teleportDisabled: this.adjustedTo === useAdjustedTo.tdkey
        }, {
            default: () => {
                return this.animated
                    ? h(Transition, {
                        name: 'popover-transition',
                        appear: this.NPopover.isMountedRef.value,
                        // Don't use watch to enable follower, since the transition may
                        // make position sync timing very subtle and buggy.
                        onEnter: () => {
                            this.followerEnabled = true;
                        },
                        onAfterLeave: () => {
                            this.followerEnabled = false;
                        }
                    }, {
                        default: this.renderContentNode
                    })
                    : this.renderContentNode();
            }
        });
    }
});
