import { extractTriggersFromPoint } from "./extractTriggersFromPoint";

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

    // First pass: collect all points and identify spawn actions
    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);

                    // Check for spawn actions in conveyors
                    if (point.action.actionType === 'spawn') {
                        spawnActions.push(point.action);
                    }
                });
            } else if (event.type === 'vehicle' ||
                event.type === 'machine' ||
                event.type === 'storageUnit' ||
                event.type === 'roboticArm' ||
                event.type === 'human' ||
                event.type === 'crane'
            ) {
                pointMap.set(event.point.uuid, event.point);
                allPoints.push(event.point);

                // Check for spawn actions in storage units and other types
                if (event.type === 'storageUnit') {
                    const storagePoint = event.point as StoragePointSchema;
                    storagePoint.actions.forEach(action => {
                        if (action.actionType === 'retrieve') {
                            spawnActions.push(action);
                        }
                    });
                }
            }
        });
    });

    // Build dependency graphs
    const graph = new Map<string, string[]>();
    const reverseGraph = new Map<string, string[]>();
    const allTriggeredPoints = new Set<string>();
    const actionMap = new Map<string, Action>(); // Map point UUID to its primary action

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

        // Store the primary action for this point
        if ('action' in point) {
            actionMap.set(point.uuid, point.action);
        } else if ('actions' in point && point.actions.length > 0) {
            actionMap.set(point.uuid, point.actions[0]); // Use first action as primary
        }

        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 => {
            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) {
        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 actions
    const triggeredActions = order
        .map(uuid => actionMap.get(uuid))
        .filter((action): action is Action => action !== undefined);

    // Combine triggered actions with ALL spawn actions
    const allExecutionActions = [...triggeredActions, ...spawnActions];

    // Remove duplicate actions while preserving order
    const uniqueActions = allExecutionActions.filter((action, index, array) =>
        array.findIndex(a => a.actionUuid === action.actionUuid) === index
    );

    return uniqueActions;
}