import {
    ref,
    reactive,
    getCurrentInstance,
    onUnmounted,
    nextTick,
    computed,
    onMounted,
    type ComponentInternalInstance
} from 'vue';
import { logger } from '../util/logger';

// 类型定义
interface ParentContext {
    name: string;
    addChild: (child: ChildContext) => void;
    removeChild: (childId: string) => void;
    broadcast: (event: string, data?: any, childIds?: string | string[]) => void;
    broadcastToChildren: (componentName: string, event: string, data?: any) => void;
    getChildren: () => ChildContext[];
    getExposed: () => Record<string, any>;
    getChildExposed: (childId: string) => Record<string, any>;
    getChildrenExposed: () => Array<{ id: string; name: string; exposed: Record<string, any> }>;
    getInstance: () => any;
}

interface ChildContext {
    id: string;
    name: string;
    getChildIndex: () => number;
    emitToParent: (event: string, data?: any) => void;
    getParentExposed: () => Record<string, any>;
    getInstance: () => any;
    getExposed: () => Record<string, any>;
}

// 符号定义
const PARENT_CONTEXT_SYMBOL = Symbol('parent_context');

/**
 * 生成实例唯一ID
 */
function generateInstanceId(componentName: string): string {
    return `${componentName}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}

/**
 * 查找父组件实例
 */
function findParentInstance(name: string, instance: any): any {
    if (!instance) return null;

    let parent = instance.parent;
    while (parent) {
        const parentName = parent.type?.name || parent.type?.__name;
        if (parentName === name) {
            return parent;
        }
        parent = parent.parent;
    }
    return null;
}

/**
 * 获取父组件上下文
 */
function getParentContext(name: string, instance: any): ParentContext | null {
    const parentInstance = findParentInstance(name, instance);
    return parentInstance?.proxy?.[PARENT_CONTEXT_SYMBOL] || null;
}

/**
 * 递归查找所有指定名称的子组件
 */
function findAllChildComponents(componentName: string, instance: any): any[] {
    const components: any[] = [];

    function traverse(currentInstance: any) {
        if (!currentInstance?.subTree) return;

        const subTree = currentInstance.subTree?.children || [];
        const children = Array.isArray(subTree) ? subTree : [subTree];

        children.forEach((vnode: any) => {
            const child = vnode.component;
            if (!child) return;

            const name = child.type?.name || child.type?.__name;
            if (name === componentName) {
                components.push(child);
            }
            traverse(child);
        });
    }

    traverse(instance);
    logger.log(`Found ${components.length} ${componentName} components`);
    return components;
}

/**
 * 父组件 Hook
 */
export function useParent(componentName?: string) {
    const instance = getCurrentInstance();
    if (!instance) {
        throw new Error('useParent must be called within setup function');
    }

    const name = componentName || instance.type.name || instance.type.__name;
    if (!name) {
        throw new Error('Component name is required for useParent');
    }

    const children = reactive<ChildContext[]>([]);
    const childrenMap = new Map<string, ChildContext>();

    const broadcast = (event: string, data?: any, childIds?: string | string[]) => {
        const targetChildren = childIds
            ? ((Array.isArray(childIds) ? childIds : [childIds])
                  .map(id => childrenMap.get(id))
                  .filter(Boolean) as ChildContext[])
            : Array.from(childrenMap.values());

        logger.log(`Parent ${name} broadcasting event: ${event} to ${targetChildren.length} children`);

        targetChildren.forEach(child => {
            const exposed = child.getExposed();
            if (exposed && typeof exposed[event] === 'function') {
                try {
                    exposed[event](data);
                } catch (error) {
                    logger.warn(`Error calling child method ${event}:`, error);
                }
            }
        });
    };

    const broadcastToChildren = (componentName: string, event: string, data?: any) => {
        logger.log(`Parent ${name} broadcasting event: ${event} to all ${componentName} components`);

        const childComponents = findAllChildComponents(componentName, instance);
        let successCount = 0;

        childComponents.forEach(childComponent => {
            const exposed = childComponent.exposed || childComponent.proxy;
            if (exposed && typeof exposed[event] === 'function') {
                try {
                    exposed[event](data);
                    successCount++;
                } catch (error) {
                    logger.warn(`Error calling ${componentName} method ${event}:`, error);
                }
            }
        });

        logger.log(
            `Parent ${name} successfully called ${successCount} of ${childComponents.length} ${componentName} components`
        );
    };

    const parentContext: ParentContext = {
        name,
        addChild(child: ChildContext) {
            if (!childrenMap.has(child.id)) {
                childrenMap.set(child.id, child);
                children.push(child);
                logger.log(`Parent ${name} added child: ${child.name}`);
            }
        },
        removeChild(childId: string) {
            if (childrenMap.has(childId)) {
                const child = childrenMap.get(childId)!;
                childrenMap.delete(childId);
                const index = children.findIndex(c => c.id === childId);
                if (index > -1) children.splice(index, 1);
                logger.log(`Parent ${name} removed child: ${childId}`);
            }
        },
        broadcast,
        broadcastToChildren,
        getChildren: () => Array.from(childrenMap.values()),
        getExposed: () => instance.exposed || {},
        getChildExposed(childId: string) {
            const child = childrenMap.get(childId);
            return child?.getExposed?.() || {};
        },
        getChildrenExposed() {
            return Array.from(childrenMap.values())
                .filter(child => child.getExposed)
                .map(child => ({
                    id: child.id,
                    name: child.name,
                    exposed: child.getExposed()
                }))
                .filter(item => Object.keys(item.exposed).length > 0);
        },
        getInstance: () => instance
    };

    if (instance.proxy) {
        (instance.proxy as any)[PARENT_CONTEXT_SYMBOL] = parentContext;
    }

    onUnmounted(() => {
        childrenMap.forEach((_, childId) => parentContext.removeChild(childId));
        if (instance.proxy) {
            delete (instance.proxy as any)[PARENT_CONTEXT_SYMBOL];
        }
        logger.log(`Parent ${name} unmounted and cleaned up`);
    });

    return {
        parentName: name,
        children,
        broadcast,
        broadcastToChildren,
        getChildren: parentContext.getChildren,
        getChildExposed: parentContext.getChildExposed,
        getChildrenExposed: parentContext.getChildrenExposed,
        getExposed: parentContext.getExposed,
        getInstance: parentContext.getInstance
    };
}

/**
 * 子组件 Hook
 */
export function useChildren(componentName?: string, parentName?: string) {
    const instance = getCurrentInstance();
    if (!instance) {
        throw new Error('useChildren must be called within setup function');
    }

    const name = componentName || instance.type.name || instance.type.__name;
    const instanceId = generateInstanceId(name || 'anonymous');
    const parentRef = ref<any | null>(null);
    const parentExposed = ref<Record<string, any>>({});

    const createSimulatedParentContext = (parentInstance: any): ParentContext => ({
        name: parentInstance?.type?.name || parentInstance?.type?.__name || 'unknown',
        addChild: () => logger.log('Simulated parent added child'),
        removeChild: () => logger.log('Simulated parent removed child'),
        broadcast: () => logger.log('Simulated parent broadcasting'),
        broadcastToChildren: () => logger.log('Simulated parent broadcasting to children'),
        getChildren: () => [],
        getExposed: () => parentInstance?.exposed || {},
        getChildExposed: () => ({}),
        getChildrenExposed: () => [],
        getInstance: () => parentInstance
    });

    const getParentExposed = (): Record<string, any> => {
        if (parentRef.value) {
            const exposed = parentRef.value.getExposed();
            parentExposed.value = exposed;
            return exposed;
        }
        return {};
    };

    const getExposed = (): Record<string, any> => instance.exposed || {};

    const findParent = (): ParentContext | null => {
        if (parentName) {
            const parentContext = getParentContext(parentName, instance);
            if (parentContext) {
                if (!parentContext.getInstance) {
                    parentContext.getInstance = () => findParentInstance(parentName, instance);
                }
                return parentContext;
            }

            const parentInstance = findParentInstance(parentName, instance);
            if (parentInstance) {
                return createSimulatedParentContext(parentInstance);
            }
        }

        let current = instance.parent;
        while (current) {
            const context = (current.proxy as any)?.[PARENT_CONTEXT_SYMBOL];
            if (context) {
                if (!context.getInstance) {
                    context.getInstance = () => current;
                }
                return context;
            }
            current = current.parent;
        }

        return instance.parent ? createSimulatedParentContext(instance.parent) : null;
    };

    const linkParent = (): boolean => {
        const parent = findParent();
        if (parent) {
            parentRef.value = parent;
            if (parent.addChild && childContext) {
                parent.addChild(childContext);
            }
            getParentExposed();
            logger.log(`Child ${name || 'anonymous'} linked to parent ${parent.name}`);
            return true;
        }
        logger.log(`Child ${name || 'anonymous'} no parent found, working in standalone mode`);
        return false;
    };

    const emitToParent = (event: string, data?: any) => {
        if (parentRef.value) {
            const exposed = getParentExposed();
            if (exposed && typeof exposed[event] === 'function') {
                try {
                    exposed[event](data, instanceId, name);
                } catch (error) {
                    logger.warn(`Error calling parent method ${event}:`, error);
                }
            }
        }
    };

    const getChildIndex = () => {
        if (!parentRef.value) return -1;
        try {
            const children = parentRef.value.getChildren();
            return children.findIndex((child: ChildContext) => child.id === instanceId);
        } catch (error) {
            return -1;
        }
    };

    const childContext: ChildContext = {
        id: instanceId,
        name: name || 'anonymous',
        getChildIndex,
        emitToParent,
        getParentExposed,
        getInstance: () => instance,
        getExposed
    };

    logger.log(`Child ${name || 'anonymous'} registered, looking for parent`);

    onMounted(() => {
        let connected = linkParent();
        nextTick(() => {
            connected = linkParent();
            if (!connected) {
                setTimeout(linkParent, 500);
            }
        });
    });

    onUnmounted(() => {
        if (parentRef.value?.removeChild) {
            parentRef.value.removeChild(instanceId);
        }
        logger.log(`Child ${name || 'anonymous'} unmounted`);
    });

    return {
        childId: instanceId,
        childName: name || 'anonymous',
        childIndex: computed(getChildIndex),
        parent: parentRef,
        emitToParent,
        getParentExposed,
        parentExposed: computed(() => parentExposed.value),
        getExposed
    };
}

/**
 * 检查父组件是否存在
 */
export function hasParent(parentName?: string): boolean {
    const instance = getCurrentInstance();
    if (!instance) return false;

    if (parentName) {
        return getParentContext(parentName, instance) !== null;
    }

    let current = instance.parent;
    while (current) {
        if ((current.proxy as any)?.[PARENT_CONTEXT_SYMBOL]) return true;
        current = current.parent;
    }
    return false;
}

/**
 * 获取父组件上下文
 */
export function getParentContextByName(parentName: string): ParentContext | null {
    const instance = getCurrentInstance();
    return instance ? getParentContext(parentName, instance) : null;
}

/**
 * 热更新清理函数
 */
export function cleanupComponentRelations(): void {
    logger.log('Cleaning up component relations for hot reload');
}

// 热更新处理
if (import.meta.hot) {
    import.meta.hot.accept(() => {
        logger.log('Hot reload detected, relations will be automatically reconnected');
    });
}

export default {
    useParent,
    useChildren,
    hasParent,
    getParentContextByName,
    cleanupComponentRelations
};
