import { useCallback, useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh, useSelectedPath } from '../../../../../store/builder/store';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
import InteractivePoints from '../animator/interactivePoint';

import MaterialAnimator from '../animator/materialAnimator';
import VehicleAnimator from '../animator/vehicleAnimator';

import { useHumanEventManager } from '../../../human/eventManager/useHumanEventManager';

function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
    const { navMesh } = useNavMesh();
    const { isPlaying } = usePlayButtonStore();
    const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore, assetStore } = useSceneContext();
    const { removeMaterial, setEndTime, setIsVisible } = materialStore();
    const { getStorageUnitById } = storageUnitStore();
    const { getHumanById, addCurrentAction } = humanStore();
    const { getArmBotById } = armBotStore();
    const { getConveyorById } = conveyorStore();
    const { triggerPointActions } = useTriggerHandler();
    const { setCurrentAnimation, getAssetById } = assetStore();
    const { addHumanToMonitor } = useHumanEventManager();
    const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
    const { selectedProductStore } = useProductContext();
    const { selectedProduct } = selectedProductStore();
    const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = vehicleStore();
    const [currentPhase, setCurrentPhase] = useState<string>('stationed');
    const [path, setPath] = useState<[number, number, number][]>([]);
    const pauseTimeRef = useRef<number | null>(null);
    const idleTimeRef = useRef<number>(0);
    const activeTimeRef = useRef<number>(0);
    const isPausedRef = useRef<boolean>(false);
    const isSpeedRef = useRef<number>(0);
    let startTime: number;
    let fixedInterval: number;
    const { speed } = useAnimationPlaySpeed();
    const { isPaused } = usePauseButtonStore();
    const previousTimeRef = useRef<number | null>(null);
    const animationFrameIdRef = useRef<number | null>(null);
    const { selectedPath, setSelectedPath } = useSelectedPath();

    useEffect(() => {
        isPausedRef.current = isPaused;
    }, [isPaused]);

    useEffect(() => {
        isSpeedRef.current = speed;
    }, [speed]);

    const computePath = useCallback(
        (start: any, end: any) => {
            try {
                const navMeshQuery = new NavMeshQuery(navMesh);
                const { path: segmentPath } = navMeshQuery.computePath(start, end);
                if (
                    segmentPath.length > 0 &&
                    Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(end.x) &&
                    Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(end.z)
                ) {
                    return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
                } else {
                    const { path: segmentPaths } = navMeshQuery.computePath(start, start);
                    return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
                }

            } catch {
                console.error("Failed to compute path");
                return [];
            }
        }, [navMesh]);

    function vehicleStatus(modelId: string, status: string) {
        // console.log(`${modelId} , ${status}`);
    }

    // Function to reset everything
    function reset() {
        setCurrentPhase('stationed');
        setVehicleActive(agvDetail.modelUuid, false);
        setVehiclePicking(agvDetail.modelUuid, false);
        setVehicleState(agvDetail.modelUuid, 'idle');
        setVehicleLoad(agvDetail.modelUuid, 0);
        setPath([]);
        startTime = 0;
        isPausedRef.current = false;
        pauseTimeRef.current = 0;
        resetTime(agvDetail.modelUuid)
        activeTimeRef.current = 0
        idleTimeRef.current = 0
        previousTimeRef.current = null
        if (animationFrameIdRef.current !== null) {
            cancelAnimationFrame(animationFrameIdRef.current)
            animationFrameIdRef.current = null
        }
    }

    useEffect(() => {
        if (isPlaying && selectedPath === "auto") {
            if (!agvDetail.point.action.unLoadPoint || !agvDetail.point.action.pickUpPoint) return;

            if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
                const toPickupPath = computePath(
                    new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]),
                    agvDetail?.point?.action?.pickUpPoint?.position
                );

                setPath(toPickupPath);
                setCurrentPhase('stationed-pickup');
                setVehicleState(agvDetail.modelUuid, 'running');
                setVehiclePicking(agvDetail.modelUuid, false);
                setVehicleActive(agvDetail.modelUuid, true);
                vehicleStatus(agvDetail.modelUuid, 'Started from station, heading to pickup');
                return;
            } else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'picking') {
                if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.currentMaterials.length > 0) {
                    if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) {
                        const toDrop = computePath(
                            agvDetail.point.action.pickUpPoint.position,
                            agvDetail.point.action.unLoadPoint.position
                        );
                        setPath(toDrop);
                        setCurrentPhase('pickup-drop');
                        setVehicleState(agvDetail.modelUuid, 'running');
                        setVehiclePicking(agvDetail.modelUuid, false);
                        setVehicleActive(agvDetail.modelUuid, true);
                        vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point');
                    }
                }
            } else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'dropping' && agvDetail.currentLoad === 0) {
                if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) {
                    const dropToPickup = computePath(
                        agvDetail.point.action.unLoadPoint.position,
                        agvDetail.point.action.pickUpPoint.position
                    );
                    setPath(dropToPickup);
                    setCurrentPhase('drop-pickup');
                    setVehicleState(agvDetail.modelUuid, 'running');
                    setVehiclePicking(agvDetail.modelUuid, false);
                    setVehicleActive(agvDetail.modelUuid, true);
                    vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
                }
            }
        }
        else {
            reset()
        }
    }, [vehicles, currentPhase, path, isPlaying, selectedPath]);

    function animate(currentTime: number) {
        if (previousTimeRef.current === null) {
            previousTimeRef.current = currentTime;
        }

        const deltaTime = (currentTime - previousTimeRef.current) / 1000;
        previousTimeRef.current = currentTime;

        if (agvDetail.isActive) {
            if (!isPausedRef.current) {
                activeTimeRef.current += deltaTime * isSpeedRef.current;
            }
        } else {
            if (!isPausedRef.current) {
                idleTimeRef.current += deltaTime * isSpeedRef.current; // Scale idle time by speed
            }
        }
        animationFrameIdRef.current = requestAnimationFrame(animate);
    }

    useEffect(() => {
        if (!isPlaying) return
        if (!agvDetail.isActive) {
            const roundedActiveTime = Math.round(activeTimeRef.current);
            // console.log('Final Active Time:', roundedActiveTime, 'seconds');
            incrementActiveTime(agvDetail.modelUuid, roundedActiveTime);
            activeTimeRef.current = 0;
        } else {
            const roundedIdleTime = Math.round(idleTimeRef.current);
            // console.log('Final Idle Time:', roundedIdleTime, 'seconds');
            incrementIdleTime(agvDetail.modelUuid, roundedIdleTime);
            idleTimeRef.current = 0;
        }

        if (animationFrameIdRef.current === null) {
            animationFrameIdRef.current = requestAnimationFrame(animate);
        }

        return () => {
            if (animationFrameIdRef.current !== null) {
                cancelAnimationFrame(animationFrameIdRef.current);
                animationFrameIdRef.current = null;
            }
        };
    }, [agvDetail, isPlaying]);

    function handleCallBack() {
        if (currentPhase === 'stationed-pickup') {
            setCurrentPhase('picking');
            setVehicleState(agvDetail.modelUuid, 'idle');
            setVehiclePicking(agvDetail.modelUuid, true);
            setVehicleActive(agvDetail.modelUuid, false);
            vehicleStatus(agvDetail.modelUuid, 'Reached pickup point, waiting for material');
            setPath([]);
        } else if (currentPhase === 'pickup-drop') {
            setCurrentPhase('dropping');
            setVehicleState(agvDetail.modelUuid, 'idle');
            setVehiclePicking(agvDetail.modelUuid, false);
            setVehicleActive(agvDetail.modelUuid, false);
            vehicleStatus(agvDetail.modelUuid, 'Reached drop point');
            setPath([]);
        } else if (currentPhase === 'drop-pickup') {
            setCurrentPhase('picking');
            setVehicleState(agvDetail.modelUuid, 'idle');
            setVehiclePicking(agvDetail.modelUuid, true);
            setVehicleActive(agvDetail.modelUuid, false);
            setPath([]);
            clearCurrentMaterials(agvDetail.modelUuid)
            vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete');
        }
    }

    function startUnloadingProcess() {
        if (agvDetail.point.action.triggers.length > 0) {
            const trigger = getTriggerByUuid(selectedProduct.productUuid, agvDetail.point.action.triggers[0]?.triggerUuid);
            const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
            const triggeredAction = getActionByUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredAction?.actionUuid || '');

            if (trigger && model) {
                if (model.type === 'transfer') {
                    const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
                    if (action) {
                        handleMaterialDropToConveyor(model);
                    }
                } else if (model.type === 'machine') {
                    // 
                } else if (model.type === 'roboticArm') {
                    const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
                    if (action) {
                        handleMaterialDropToArmBot(model);
                    }
                } else if (model.type === 'storageUnit') {
                    const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
                    if (action) {
                        handleMaterialDropToStorageUnit(model);
                    }
                } else if (model.type === 'human') {
                    const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
                    if (action && (triggeredAction?.actionType === 'assembly' || triggeredAction?.actionType === 'worker')) {
                        handleMaterialDropToHuman(model, triggeredAction);
                    }
                }
            } else {
                const droppedMaterial = agvDetail.currentLoad;
                startTime = performance.now();
                handleMaterialDropByDefault(droppedMaterial);
            }
        } else {
            const droppedMaterial = agvDetail.currentLoad;
            startTime = performance.now();
            handleMaterialDropByDefault(droppedMaterial);
        }
    }

    function handleMaterialDropToHuman(model: HumanEventSchema, action: HumanAction) {

        if (model) {
            if (action.actionType === 'worker') {
                addHumanToMonitor(model.modelUuid, () => {
                    loopMaterialDropToHuman(
                        agvDetail,
                        model.modelUuid,
                        agvDetail.point.action,
                        action.actionUuid
                    );
                }, action.actionUuid || '')
            }
        }
    }

    function loopMaterialDropToHuman(
        vehicle: VehicleStatus,
        humanId: string,
        vehicleAction: VehicleAction,
        humanActionId: string
    ) {
        let currentVehicleLoad = vehicle.currentLoad;

        const unloadLoop = () => {
            const human = getHumanById(humanId);
            const humanAsset = getAssetById(humanId);
            const humanAction = human?.point.actions.find((action) => action.actionUuid === humanActionId);

            if (!human || human.currentAction?.actionUuid !== humanAction?.actionUuid) return;

            if (humanAsset && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
                decrementVehicleLoad(vehicle.modelUuid, 1);
                currentVehicleLoad -= 1;
                const material = removeLastMaterial(vehicle.modelUuid);
                if (material) {
                    setIsVisible(material.materialId, false);
                }
            } else if (!human.isActive && human.currentLoad < (humanAction?.loadCapacity || 0) && humanAsset?.animationState?.current === 'waiting') {
                setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
            }
            setTimeout(() => {
                requestAnimationFrame(unloadLoop);
            }, 150)
        };

        const human = getHumanById(humanId);
        const humanAction = human?.point.actions.find((action) => action.actionUuid === humanActionId);
        if (human && human.currentAction?.actionUuid !== humanActionId && human.currentLoad < (humanAction?.loadCapacity || 0)) {
            addCurrentAction(humanId, humanActionId);
            unloadLoop();
        }
    }

    function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
        if (model) {
            if (model.point.action.actionType === 'store') {
                loopMaterialDropToStorage(
                    agvDetail.modelUuid,
                    agvDetail.currentLoad,
                    agvDetail.point.action.unLoadDuration,
                    model.modelUuid,
                    model.point.action.storageCapacity,
                    agvDetail.point.action
                );
            }
        }
    }

    function loopMaterialDropToStorage(
        vehicleId: string,
        vehicleCurrentLoad: number,
        unLoadDuration: number,
        storageUnitId: string,
        storageMaxCapacity: number,
        action: VehicleAction
    ) {
        startTime = performance.now();
        const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / isSpeedRef.current));

        const unloadLoop = () => {
            if (isPausedRef.current) {
                pauseTimeRef.current ??= performance.now();
                requestAnimationFrame(unloadLoop);
                return;
            }

            if (pauseTimeRef.current) {
                const pauseDuration = performance.now() - pauseTimeRef.current;
                startTime += pauseDuration;
                pauseTimeRef.current = null;
            }

            const elapsedTime = performance.now() - startTime;
            const storageUnit = getStorageUnitById(storageUnitId);

            if (elapsedTime >= fixedInterval) {
                if (storageUnit && agvDetail &&
                    storageUnit.currentLoad < storageMaxCapacity &&
                    vehicleCurrentLoad > 0) {

                    decrementVehicleLoad(vehicleId, 1);
                    vehicleCurrentLoad -= 1;

                    const material = removeLastMaterial(vehicleId);
                    if (material) {

                        triggerPointActions(action, material.materialId);

                    }

                    if (vehicleCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
                        startTime = performance.now();
                        requestAnimationFrame(unloadLoop);
                    }
                }
            } else {
                requestAnimationFrame(unloadLoop);
            }
        };

        const storageUnit = getStorageUnitById(storageUnitId);
        if (storageUnit && vehicleCurrentLoad > 0 && storageUnit?.currentLoad < storageMaxCapacity) {
            unloadLoop();
        }
    }

    function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
        const conveyor = getConveyorById(model.modelUuid);
        if (conveyor) {
            loopMaterialDropToConveyor(
                agvDetail.modelUuid,
                agvDetail.currentLoad,
                conveyor.modelUuid,
                agvDetail.point.action.unLoadDuration,
                agvDetail.point.action
            );
        }
    }

    function loopMaterialDropToConveyor(
        vehicleId: string,
        vehicleCurrentLoad: number,
        conveyorId: string,
        unLoadDuration: number,
        action: VehicleAction
    ) {
        let lastIncrementTime = performance.now();
        let pauseStartTime: number | null = null;
        let totalPausedDuration = 0;
        const fixedInterval = (unLoadDuration * 1000) / speed;

        const dropLoop = (currentTime: number) => {
            const conveyor = getConveyorById(conveyorId);

            if (isPausedRef.current || (conveyor && conveyor.isPaused)) {
                if (pauseStartTime === null) {
                    pauseStartTime = currentTime;
                }
                requestAnimationFrame(dropLoop);
                return;
            }

            // If we were paused but now resumed
            if (pauseStartTime !== null) {
                totalPausedDuration += currentTime - pauseStartTime;
                pauseStartTime = null;
            }

            // Adjust for paused time
            const adjustedCurrentTime = currentTime - totalPausedDuration;
            const elapsedSinceLastIncrement = adjustedCurrentTime - lastIncrementTime;

            if (elapsedSinceLastIncrement >= fixedInterval) {
                if (conveyor && vehicleCurrentLoad > 0) {
                    decrementVehicleLoad(vehicleId, 1);
                    vehicleCurrentLoad -= 1;

                    const material = removeLastMaterial(vehicleId);
                    if (material) {
                        triggerPointActions(action, material.materialId);
                    }

                    // Update the last increment time (using adjusted time)
                    lastIncrementTime = adjustedCurrentTime;
                }
            }

            // Continue the loop if there's more load to drop
            if (vehicleCurrentLoad > 0) {
                requestAnimationFrame(dropLoop);
            }
        };

        requestAnimationFrame(dropLoop);
    }

    function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
        const armBot = getArmBotById(model.modelUuid);
        if (armBot && armBot.state === 'idle' && !armBot.isActive) {
            loopMaterialDropToArmBot(
                agvDetail.modelUuid,
                agvDetail.currentLoad,
                agvDetail.point.action.unLoadDuration,
                model.modelUuid,
                agvDetail.point.action
            );
        }
    }

    function loopMaterialDropToArmBot(
        vehicleId: string,
        vehicleCurrentLoad: number,
        unLoadDuration: number,
        armBotId: string,
        action: VehicleAction
    ) {
        startTime = performance.now();
        const armBot = getArmBotById(armBotId);

        if (!armBot || armBot.state !== 'idle' || armBot.isActive || vehicleCurrentLoad <= 0) {
            return;
        }

        const checkIdleDuration = () => {
            if (isPausedRef.current) {
                pauseTimeRef.current ??= performance.now();
                requestAnimationFrame(checkIdleDuration);
                return;
            }

            if (pauseTimeRef.current) {
                const pauseDuration = performance.now() - pauseTimeRef.current;
                startTime += pauseDuration;
                pauseTimeRef.current = null;
            }

            const elapsedTime = performance.now() - startTime;

            if (elapsedTime >= unLoadDuration * (1000 / speed)) {
                const material = getLastMaterial(vehicleId);
                if (material) {
                    vehicleCurrentLoad -= 1;

                    triggerPointActions(action, material.materialId);

                    if (vehicleCurrentLoad > 0) {
                        setTimeout(() => {
                            const waitForNextTransfer = () => {
                                const currentArmBot = getArmBotById(armBotId);
                                if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
                                    startTime = performance.now();
                                    loopMaterialDropToArmBot(vehicleId, vehicleCurrentLoad, unLoadDuration, armBotId, action);
                                } else {
                                    requestAnimationFrame(waitForNextTransfer);
                                }
                            };
                            waitForNextTransfer();
                        }, 0)
                    }
                }
            } else {
                requestAnimationFrame(checkIdleDuration);
            }
        };

        checkIdleDuration();
    }

    function handleMaterialDropByDefault(droppedMaterial: number) {
        if (isPausedRef.current) {
            pauseTimeRef.current ??= performance.now();
            requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
            return;
        }

        if (pauseTimeRef.current) {
            const pauseDuration = performance.now() - pauseTimeRef.current;
            startTime += pauseDuration;
            pauseTimeRef.current = null;
        }

        const elapsedTime = performance.now() - startTime;
        const unLoadDuration = agvDetail.point.action.unLoadDuration;
        fixedInterval = ((unLoadDuration / agvDetail.currentLoad) * (1000 / isSpeedRef.current));

        if (elapsedTime >= fixedInterval) {
            let droppedMat = droppedMaterial - 1;
            decrementVehicleLoad(agvDetail.modelUuid, 1);
            const material = removeLastMaterial(agvDetail.modelUuid);
            if (material) {
                setEndTime(material.materialId, performance.now());
                removeMaterial(material.materialId);
            }
            if (droppedMat > 0) {
                startTime = performance.now();
                requestAnimationFrame(() => handleMaterialDropByDefault(droppedMat));
            } else {
                return;
            }
        } else {
            requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
        }
    }

    return (
        <>
            <VehicleAnimator
                path={path}
                handleCallBack={handleCallBack}
                currentPhase={currentPhase}
                agvUuid={agvDetail?.modelUuid}
                agvDetail={agvDetail}
                reset={reset}
                startUnloadingProcess={startUnloadingProcess}
            />
            {selectedPath === "manual" && (<InteractivePoints agvUuid={agvDetail?.modelUuid} />)}
            <MaterialAnimator agvDetail={agvDetail} />
        </>
    );
}

export default VehicleInstance;