/**
 * 懒加载工具模块
 *
 * 提供按需动态导入辅助函数，支持：
 * 1. 首次加载时自动调用初始化函数（如 bind*Controls）
 * 2. 包裹目标函数，实现 window.xxx = (...args) => 自动加载并调用
 * 3. 模块缓存机制，避免重复加载
 * 4. Alpine 组件懒加载：返回同步 stub，在 $watch 触发时异步加载真实组件
 */

/**
 * 创建一个可延迟加载的模块引用。
 *
 * @param {() => Promise<*>} importFn 动态 import 工厂函数
 * @param {(module: *) => void} [initFn] 首次加载成功后调用的初始化回调
 * @returns {{ load: () => Promise<*>, wrap: (name: string) => Function, get: (name: string) => Function }}
 */
export function createLazyModule(importFn, initFn) {
    /** @type {Promise<*>|null} */
    let promise = null;
    let initialized = false;

    return {
        /**
         * 确保模块已加载，返回模块对象。
         * @returns {Promise<*>}
         */
        async load() {
            if (!promise) {
                promise = importFn().then((mod) => {
                    if (!initialized && initFn) {
                        initialized = true;
                        initFn(mod);
                    }
                    return mod;
                });
            }
            return promise;
        },

        /**
         * 创建一个延迟执行的目标函数包装器。
         * 首次调用时自动加载模块，之后直接调用缓存。
         *
         * @param {string} fnName 模块中导出的函数名
         * @returns {Function}
         */
        wrap(fnName) {
            return async (...args) => {
                const mod = await this.load();
                const fn = mod[fnName];
                if (typeof fn !== "function") {
                    throw new Error(
                        `懒加载模块中找不到函数 "${fnName}"`,
                    );
                }
                return fn(...args);
            };
        },

        /**
         * 返回一个返回模块导出的 getter 函数。
         * 适用于返回非函数值（如 Alpine 组件对象）。
         *
         * @param {string} name 模块中导出的名称
         * @returns {Function}
         */
        get(name) {
            return async () => {
                const mod = await this.load();
                return mod[name];
            };
        },
    };
}

/**
 * 创建 Alpine 组件延迟加载包装器。
 *
 * Alpine 的 x-data="ComponentName()" 要求工厂函数返回一个同步对象。
 * 本函数返回一个函数，该函数：
 * 1. 立即返回一个包含安全默认值的 stub 对象
 * 2. 通过 Alpine 的 $watch 监听显示状态变化
 * 3. 仅当组件变为可见（show/showUserModal 变为 true）时，才异步加载真实模块
 * 4. 通过 Alpine 的响应式代理（this）写入真实数据，触发模板重新渲染
 *
 * 这实现了"真懒加载"——用户若不打开面板，对应代码块永远不会下载。
 *
 * @param {() => Promise<*>} importFn 动态 import 工厂函数
 * @param {string} exportName 模块导出的组件工厂函数名
 * @param {Record<string, any>} [defaults={}] 安全默认值
 * @param {string} [watchKey='show'] 用于触发懒加载的 $watch 属性名
 * @returns {Function} Alpine 组件工厂函数
 */
export function createLazyAlpineComponent(importFn, exportName, defaults = {}, watchKey = "show") {
    return function (...args) {
        const stub = {
            [watchKey]: false,
            ...defaults,
            init() {
                const proxy = this;
                let loaded = false;

                if (
                    watchKey in proxy &&
                    typeof proxy.$watch === "function"
                ) {
                    proxy.$watch(watchKey, (value) => {
                        if (value && !loaded) {
                            loaded = true;
                            importFn()
                                .then((mod) => {
                                    const componentFn = mod[exportName];
                                    const realData =
                                        typeof componentFn === "function"
                                            ? componentFn(...args)
                                            : componentFn;

                                    Object.keys(realData).forEach((key) => {
                                        if (key !== "init") {
                                            proxy[key] = realData[key];
                                        }
                                    });

                                    for (const key in realData) {
                                        if (!(key in proxy)) {
                                            proxy[key] = realData[key];
                                        }
                                    }

                                    if (
                                        typeof proxy.init === "function" &&
                                        proxy.init !== stub.init
                                    ) {
                                        proxy.init.call(proxy);
                                    }
                                })
                                .catch((err) => {
                                    console.error(
                                        `[懒加载] Alpine 组件 "${exportName}" 加载失败:`,
                                        err,
                                    );
                                    loaded = false;
                                });
                        }
                    });
                }
            },
        };

        return stub;
    };
}
