import { getCurrentInstance, type ComponentInternalInstance } from 'vue';

/**
 * 适用于 uni-app Vue3 的事件派发/广播工具
 * 用法：import { dispatch, broadcast } from './emitter'
 */

/**
 * 向上查找父组件并派发事件
 * @param instance 当前组件实例（setup中可用getCurrentInstance()）
 * @param componentName 目标组件名
 * @param eventName 事件名
 * @param params 参数
 */

// 将事件名转换为驼峰格式
// 例如：on-form-change -> onFormChange
function formatToCamelCase(str: string): string {
    return str.replace(/-([a-z])/g, function (g) {
        return g[1].toUpperCase();
    });
}

/**
 * 向上查找父组件
 * @param instance 当前组件实例（setup中可用getCurrentInstance()）
 * @param componentName 目标组件名
 * @returns 父组件实例
 */
function parent(instance: ComponentInternalInstance | null | undefined = undefined, componentName: string = '') {
    if (!instance) {
        instance = getCurrentInstance();
    }
    let parent = instance && (instance.parent as ComponentInternalInstance | null | undefined);

    if (!componentName) return parent;
    while (parent) {
        const name = (parent.type as any)?.name as string | undefined;
        if (name === componentName) {
            return parent;
        }
        parent = parent.parent;
    }
    return null;
}

/** * 向上查找父组件并派发事件
 * @param instance 当前组件实例（setup中可用getCurrentInstance()）
 * @param componentName 目标组件名
 * @param eventName 事件名
 * @param params 参数
 */
function dispatch(
    instance: ComponentInternalInstance | null | undefined,
    componentName: string,
    eventName: string,
    ...params: any[]
) {
    let parent = instance && (instance.parent as ComponentInternalInstance | null | undefined);
    while (parent) {
        const name = (parent.type as any)?.name as string | undefined;
        if (name === componentName) {
            // 找到目标组件，派发事件
            // Vue3未解决，目标组件事件监听失效，待优化，暂时使用下面的方式解决，如果你有好的方式也可以告诉我或者提PR
            parent.emit && parent.emit(eventName, ...params);
            // 如果有对应的方法，执行方法
            // 这里可以考虑将 eventName 转换为驼峰格式
            // 例如：on-form-change -> onFormChange
            parent.exposed?.[formatToCamelCase(eventName)] && parent.exposed[formatToCamelCase(eventName)](...params);
            break;
        }
        parent = parent.parent;
    }
}

/**
 * 向下递归查找子组件并广播事件
 * @param instance 当前组件实例（setup中可用getCurrentInstance()）
 * @param componentName 目标组件名
 * @param eventName 事件名
 * @param params 参数
 */
function broadcast(
    instance: ComponentInternalInstance | null | undefined,
    componentName: string,
    eventName: string,
    ...params: any[]
) {
    if (!instance) return;
    const subTree = (instance.subTree as any)?.children || [];
    const children = Array.isArray(subTree) ? subTree : [subTree];
    children.forEach((vnode: any) => {
        const child = vnode.component as ComponentInternalInstance | undefined;

        if (child) {
            const name = (child.type as any)?.name as string | undefined;
            if (name === componentName) {
                // 找到目标组件，广播事件
                // Vue3未解决，目标组件事件监听失效，待优化，暂时使用下面的方式解决，如果你有好的方式也可以告诉我或者提PR
                child.emit && child.emit(eventName, ...params);
                // 如果有对应的方法，执行方法
                // 这里可以考虑将 eventName 转换为驼峰格式
                // 例如：on-form-change -> onFormChange
                child.exposed?.[formatToCamelCase(eventName)] && child.exposed[formatToCamelCase(eventName)](...params);
            } else {
                broadcast(child, componentName, eventName, ...params);
            }
        }
    });
}

export { dispatch, broadcast, parent };
