// 从相对路径 '../common/utils' 导入名为 'isObj' 的函数，该函数可能用于判断传入的值是否为对象类型，以便后续在代码中进行相关类型判断的逻辑处理。
import { isObj } from '../common/utils';

// 定义一个函数 'getClassNames'，它接收一个参数 'name'，用于根据给定的名称生成一系列与过渡效果相关的类名字符串。
// 返回一个对象，对象的每个属性对应不同过渡阶段的类名组合，方便后续在组件样式相关的逻辑中使用这些类名来实现动画效果。
const getClassNames = (name) => ({
    // 'enter' 阶段的类名组合，用于表示元素进入时的过渡状态，包含了多个类名，可能会在 CSS 中定义相应的样式来实现动画效果。
    enter: `van-${name}-enter van-${name}-enter-active enter-class enter-active-class`,
    // 'enter-to' 阶段的类名组合，通常用于表示元素进入过渡的目标状态，同样结合多个类名用于样式定义和动画实现。
    'enter-to': `van-${name}-enter-to van-${name}-enter-active enter-to-class enter-active-class`,
    // 'leave' 阶段的类名组合，对应元素离开时的过渡起始状态相关类名，通过这些类名关联的 CSS 样式来展示离开的动画效果。
    leave: `van-${name}-leave van-${name}-leave-active leave-class leave-active-class`,
    // 'leave-to' 阶段的类名组合，用于表示元素离开过渡的目标状态，配合 CSS 可以呈现完整的离开动画效果。
    'leave-to': `van-${name}-leave-to van-${name}-leave-active leave-to-class leave-active-class`
});

// 定义 'nextTick' 函数，它返回一个 Promise，用于模拟下一次事件循环的执行时机，通过设置一个短暂的定时器（这里设置为每 20 分之一秒，即 50 毫秒，对应帧率 20fps）来实现类似浏览器的下一次重绘时机的效果，常用于等待 DOM 更新等异步操作场景。
const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 20));

// 定义 'transition' 函数，它返回一个通过 'Behavior' 创建的行为对象，该行为对象用于在微信小程序组件等环境中实现过渡动画相关的功能逻辑，并且可以接收一个参数'showDefaultValue' 用于设置默认显示状态。
export const transition = function (showDefaultValue) {
    return Behavior({
        // 'properties' 属性用于定义该行为对象所关联组件能够接收的外部属性，也就是组件对外暴露的可配置项，外部使用组件时可以传入相应的值来控制组件的行为和外观等方面。
        properties: {
            // 'customStyle' 属性，其值被限定为字符串类型（`String`），外部可传入符合字符串格式的数据，用于自定义组件的样式，提供额外的样式定制灵活性。
            customStyle: String,
            //'show' 属性，定义其类型为布尔类型（`Boolean`），默认值由传入 'transition' 函数的'showDefaultValue' 参数决定，并且设置了一个名为 'observer' 的回调函数 'observeShow'，意味着当该属性的值发生变化时，会自动触发 'observeShow' 函数进行相应的处理，该属性通常用于控制组件的显示与隐藏状态。
            show: {
                type: Boolean,
                value: showDefaultValue,
                observer: 'observeShow'
            },
            // 'duration' 属性，其类型可以是数字类型（`Number`）或者对象类型（`Object`），默认值为 300，同样设置了 'observer' 回调函数 'observeDuration'，用于在属性值变化时执行相应逻辑，该属性可能用于控制过渡动画的时长相关设置。
            duration: {
                type: [Number, Object],
                value: 300,
                observer: 'observeDuration'
            },
            // 'name' 属性，类型为字符串类型（`String`），默认值为 'fade'，并且设置了 'observer' 回调函数 'updateClasses'，当该属性值改变时会触发 'updateClasses' 函数，该属性大概率用于指定过渡动画的名称或者类型，不同名称可能对应不同的样式和动画效果。
            name: {
                type: String,
                value: 'fade',
                observer: 'updateClasses'
            }
        },
        // 'data' 属性用于定义组件内部的数据状态，这些数据可以在组件的各个方法中使用和修改，用于记录组件的一些状态信息以及与动画相关的一些临时数据等。
        data: {
            // 'type' 用于记录当前过渡的类型相关信息，初始值为空字符串，后续可能根据不同的操作（如显示、隐藏操作）来更新其值以表示相应的过渡状态。
            type: '',
            // 'inited' 用于标记组件是否已经完成初始化，初始值为 false，在组件进行一些初始化操作（如首次显示时）会将其设置为 true。
            inited: false,
            // 'display' 用于控制组件是否显示，初始值为 false，根据'show' 属性以及过渡动画的执行情况来动态改变其值，以实现组件的显示隐藏效果。
            display: false,
            // 'classNames' 通过调用 'getClassNames' 函数，传入默认的 'fade' 作为参数来获取初始的与过渡相关的类名对象，后续会根据 'name' 属性的变化来更新这些类名，用于应用相应的动画样式。
            classNames: getClassNames('fade')
        },
        // 'attached' 是微信小程序组件生命周期中的一个钩子函数，会在组件被添加到页面时执行，在这里用于判断组件初始状态是否为显示状态，如果是则调用'show' 方法来展示组件并触发相应的过渡动画效果。
        attached() {
            if (this.data.show) {
                this.show();
            }
        },
        //'methods' 属性用于定义该行为对象中包含的一系列方法，这些方法可以被使用了这个行为的组件所调用，用于实现各种与过渡动画相关的具体功能逻辑，如显示、隐藏操作以及过渡状态变化的处理等。
        methods: {
            // 'observeShow' 方法是'show' 属性的观察者函数，当'show' 属性的值发生变化时会被调用，根据新传入的 'value'（即'show' 属性的新值）来决定是显示组件（调用'show' 方法）还是隐藏组件（调用 'leave' 方法）。
            observeShow(value) {
                if (value) {
                    this.show();
                } else {
                    this.leave();
                }
            },
            // 'updateClasses' 方法是 'name' 属性的观察者函数，当 'name' 属性的值改变时会被调用，通过调用 'getClassNames' 函数并传入新的 'name' 值来获取对应的类名对象，然后使用'set' 方法（这里假设是微信小程序组件中用于更新数据的方法）更新组件内部的 'classNames' 数据，以应用新的过渡动画相关类名样式。
            updateClasses(name) {
                this.set({
                    classNames: getClassNames(name)
                });
            },
            //'show' 方法用于处理组件显示的逻辑，实现显示时的过渡动画效果，涉及到更新组件的数据状态以及按顺序执行多个异步操作来逐步应用不同阶段的动画类名，从而实现平滑的过渡动画展示。
            show() {
                // 解构出组件数据中的 'classNames'（过渡相关类名对象）和 'duration'（过渡时长相关数据）属性，方便后续使用。
                const { classNames, duration } = this.data;
                // 根据 'duration' 的类型判断当前过渡时长，如果 'duration' 是对象类型，则取其 'leave' 属性的值作为过渡时长，否则直接使用 'duration' 的值作为过渡时长，这样可以支持更灵活的时长设置方式（比如针对进入和离开动画设置不同时长）。
                const currentDuration = isObj(duration)? duration.leave : duration;
                // 创建一个立即resolved的Promise，用于开始链式调用后续的异步操作，确保按顺序执行动画相关的状态更新步骤。
                Promise.resolve()
                    // 调用 'nextTick' 函数，等待下一次事件循环时机，模拟浏览器下一次重绘之前的操作，常用于确保在 DOM 更新后进行下一步操作，在这里是过渡动画的一个时间节点控制。
                    .then(nextTick)
                    // 使用'set' 方法更新组件的多个数据属性，包括将 'inited' 设置为 true（表示组件已初始化），'display' 设置为 true（显示组件），'classes' 设置为 'classNames.enter'（应用进入动画的起始类名），以及更新 'currentDuration'（过渡时长），实现显示动画的第一步状态更新。
                    .then(() => this.set({
                        inited: true,
                        display: true,
                        classes: classNames.enter,
                        currentDuration
                    }))
                    // 再次调用 'nextTick' 函数，等待下一个合适的时间节点，继续过渡动画的下一步操作。
                    .then(nextTick)
                    // 使用'set' 方法更新组件的 'classes' 属性为 'classNames['enter-to']'，应用进入动画的目标类名，完成进入动画的完整过渡过程。
                    .then(() => this.set({
                        classes: classNames['enter-to']
                    }));
            },
            // 'leave' 方法用于处理组件隐藏的逻辑，实现隐藏时的过渡动画效果，同样涉及到更新组件数据状态以及通过多个异步操作配合定时器等方式来逐步应用不同阶段的动画类名，以实现平滑的过渡动画隐藏效果。
            leave() {
                const { classNames, duration } = this.data;
                const currentDuration = isObj(duration)? duration.leave : duration;
                Promise.resolve()
                    .then(nextTick)
                    // 使用'set' 方法更新组件的 'classes' 属性为 'classNames.leave'（应用离开动画的起始类名），同时更新 'currentDuration'（过渡时长），开启隐藏动画的第一步操作。
                    .then(() => this.set({
                        classes: classNames.leave,
                        currentDuration
                    }))
                    // 设置一个定时器，在 'currentDuration'（过渡时长）之后调用 'onTransitionEnd' 方法，用于在动画结束后执行一些收尾操作，比如完全隐藏组件等。
                    .then(() => setTimeout(() => this.onTransitionEnd(), currentDuration))
                    // 再次调用 'nextTick' 函数，等待下一个合适的时间节点，进行隐藏动画后续的类名更新操作。
                    .then(nextTick)
                    // 使用'set' 方法更新组件的 'classes' 属性为 'classNames['leave-to']'（应用离开动画的目标类名），完成隐藏动画的完整过渡过程。
                    .then(() => this.set({
                        classes: classNames['leave-to']
                    }));
            },
            // 'onTransitionEnd' 方法在过渡动画结束时被调用，用于处理动画结束后的相关逻辑，比如根据组件当前的'show' 属性值决定是否彻底隐藏组件（将 'display' 属性设置为 false），并且通过 '$emit' 方法（这里假设是微信小程序组件中用于触发自定义事件的方式）向外触发一个名为 'transitionEnd' 的自定义事件，方便外部组件监听该事件并进行相应的后续操作。
            onTransitionEnd() {
                if (!this.data.show) {
                    this.set({ display: false });
                    this.$emit('transitionEnd');
                }
            }
        }
    });
};