import { extractTriggersFromPoint } from "./extractTriggersFromPoint";

export async function determineExecutionMachineSequences(products: productsSchema): Promise<EventsSchema[][]> {
    const pointToEventMap = new Map<string, EventsSchema>();
    const allPoints: PointsScheme[] = [];

    // First pass: map points to their corresponding events
    products.forEach(product => {
        product.eventDatas.forEach(event => {
            if (event.type === 'transfer') {
                event.points.forEach(point => {
                    pointToEventMap.set(point.uuid, event);
                    allPoints.push(point);
                });
            } else if (
                event.type === 'vehicle' ||
                event.type === 'machine' ||
                event.type === 'storageUnit' ||
                event.type === 'roboticArm' ||
                event.type === 'human' ||
                event.type === 'crane'
            ) {
                pointToEventMap.set(event.point.uuid, event);
                allPoints.push(event.point);
            }
        });
    });

    // Build dependency graph
    const dependencyGraph = new Map<string, string[]>();
    const reverseDependencyGraph = new Map<string, string[]>();
    const triggeredPoints = new Set<string>();

    allPoints.forEach(point => {
        const triggers = extractTriggersFromPoint(point);
        const dependencies: string[] = [];

        triggers.forEach(trigger => {
            const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid;
            if (targetUuid && pointToEventMap.has(targetUuid)) {
                dependencies.push(targetUuid);
                triggeredPoints.add(targetUuid);

                if (!reverseDependencyGraph.has(targetUuid)) {
                    reverseDependencyGraph.set(targetUuid, []);
                }
                reverseDependencyGraph.get(targetUuid)!.push(point.uuid);
            }
        });

        dependencyGraph.set(point.uuid, dependencies);
    });

    // Find root points (points that trigger others but are not triggered themselves)
    const rootPoints = allPoints.filter(point => {
        const hasOutgoingTriggers = extractTriggersFromPoint(point).some(
            t => t.triggeredAsset?.triggeredPoint?.pointUuid
        );
        return hasOutgoingTriggers && !triggeredPoints.has(point.uuid);
    });

    const executionSequences: EventsSchema[][] = [];

    function buildSequence(startUuid: string): EventsSchema[] {
        const sequence: EventsSchema[] = [];
        const visited = new Set<string>();

        function traverse(uuid: string) {
            if (visited.has(uuid)) return;
            visited.add(uuid);

            const event = pointToEventMap.get(uuid);
            if (event && !sequence.includes(event)) {
                sequence.push(event);
            }

            const nextPoints = dependencyGraph.get(uuid) || [];
            nextPoints.forEach(nextUuid => traverse(nextUuid));
        }

        traverse(startUuid);
        return sequence;
    }

    // Build sequences from root points
    rootPoints.forEach(root => {
        executionSequences.push(buildSequence(root.uuid));
    });

    // Handle any isolated triggered points
    const processedEvents = new Set(
        executionSequences.flat().map(event => event)
    );

    allPoints.forEach(point => {
        const event = pointToEventMap.get(point.uuid);
        if (triggeredPoints.has(point.uuid) && event && !processedEvents.has(event)) {
            executionSequences.push(buildSequence(point.uuid));
        }
    });

    return executionSequences;
}
