import { extractTriggersFromPoint } from "./extractTriggersFromPoint";

export function determineExecutionOrder(products: productsSchema): PointsScheme[] {
    // Create maps for all events and points
    const eventMap = new Map<string, EventsSchema>();
    const pointMap = new Map<string, PointsScheme>();
    const allPoints: PointsScheme[] = [];

    // First pass: collect all points
    products.forEach(product => {
        product.eventDatas.forEach(event => {
            eventMap.set(event.modelUuid, event);

            if (event.type === 'transfer') {
                event.points.forEach(point => {
                    pointMap.set(point.uuid, point);
                    allPoints.push(point);
                });
            } else if (event.type === 'vehicle' ||
                event.type === 'machine' ||
                event.type === 'storageUnit' ||
                event.type === 'roboticArm' ||
                event.type === 'human'
            ) {
                pointMap.set(event.point.uuid, event.point);
                allPoints.push(event.point);
            }
        });
    });

    // Build dependency graphs
    const graph = new Map<string, string[]>();
    const reverseGraph = new Map<string, string[]>();
    const allTriggeredPoints = new Set<string>();

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

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

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

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

    // Identify root points (points that trigger others but aren't triggered themselves)
    const rootPoints = allPoints
        .filter(point => !allTriggeredPoints.has(point.uuid))
        .filter(point => {
            // Only include roots that actually have triggers pointing FROM them
            const triggers = extractTriggersFromPoint(point);
            return triggers.some(t => t.triggeredAsset?.triggeredPoint?.pointUuid);
        });

    // If no root points found but we have triggered points, find the earliest triggers
    if (rootPoints.length === 0 && allTriggeredPoints.size > 0) {
        // This handles cases where we have circular dependencies
        // but still want to include the triggered points
        const minTriggerCount = Math.min(
            ...Array.from(allTriggeredPoints)
                .map(uuid => (graph.get(uuid) || []).length)
        );
        const potentialRoots = Array.from(allTriggeredPoints)
            .filter(uuid => (graph.get(uuid) || []).length === minTriggerCount);

        rootPoints.push(...potentialRoots.map(uuid => pointMap.get(uuid)!));
    }

    // Topological sort only for triggered points
    const visited = new Set<string>();
    const temp = new Set<string>();
    const order: string[] = [];
    let hasCycle = false;

    function visit(node: string) {
        if (temp.has(node)) {
            hasCycle = true;
            return;
        }
        if (visited.has(node)) return;

        temp.add(node);

        const dependencies = reverseGraph.get(node) || [];
        for (const dep of dependencies) {
            visit(dep);
        }

        temp.delete(node);
        visited.add(node);
        order.push(node);
    }

    // Start processing from root points
    rootPoints.forEach(root => visit(root.uuid));

    // Convert UUIDs back to points and filter out untriggered points
    const triggeredPoints = order
        .map(uuid => pointMap.get(uuid)!)
        .filter(point => allTriggeredPoints.has(point.uuid) ||
            rootPoints.some(root => root.uuid === point.uuid));

    return triggeredPoints;
}