import { useEffect } from 'react';
import { useProductStore } from '../../../store/simulation/useProductStore';
import { useActionHandler } from '../actions/useActionHandler';

function Simulator() {
    const { products } = useProductStore();
    const { handleAction } = useActionHandler();

    useEffect(() => {
        const executionOrder = determineExecutionOrder(products);
        executionOrder.forEach(point => {
            if ('actions' in point) {
                handleAction(point.actions[0]);
            } else {
                handleAction(point.action);
            }
        });
    }, [products, handleAction]);

    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') {
                    pointMap.set(event.point.uuid, event.point);
                    allPoints.push(event.point);
                } else if (event.type === 'roboticArm') {
                    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;
    }

    function determineExecutionSequences(products: productsSchema): PointsScheme[][] {
        // Create maps for all points
        const pointMap = new Map<string, PointsScheme>();
        const allPoints: PointsScheme[] = [];

        // First pass: collect all points
        products.forEach(product => {
            product.eventDatas.forEach(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') {
                    pointMap.set(event.point.uuid, event.point);
                    allPoints.push(event.point);
                }
            });
        });

        // Build complete 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 && pointMap.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);
        });

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

        // For each root point, build its complete trigger chain
        const executionSequences: PointsScheme[][] = [];

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

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

                const point = pointMap.get(uuid);
                if (point) {
                    sequence.push(point);
                }

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

            traverse(startUuid);
            return sequence;
        }

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

        // Handle any triggered points not reachable from roots (isolated chains)
        const processedPoints = new Set(
            executionSequences.flat().map(p => p.uuid)
        );

        allPoints.forEach(point => {
            if (triggeredPoints.has(point.uuid) && !processedPoints.has(point.uuid)) {
                executionSequences.push(buildSequence(point.uuid));
            }
        });

        return executionSequences;
    }

    function extractTriggersFromPoint(point: PointsScheme): TriggerSchema[] {
        if ('actions' in point) {
            return point.actions.flatMap(action => action.triggers);
        } else if ('action' in point) {
            return point.action.triggers;
        }
        return [];
    }

    return <></>;
}

export default Simulator;