import { useCallback, useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { useThree } from '@react-three/fiber';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } 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 HumanAnimator from '../animator/humanAnimator';
import MaterialAnimator from '../animator/materialAnimator';

function HumanInstance({ human }: { human: HumanStatus }) {
    const { navMesh } = useNavMesh();
    const { isPlaying } = usePlayButtonStore();
    const { scene } = useThree();
    const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
    const { removeMaterial, setEndTime, setMaterial, setIsVisible } = materialStore();
    const { getStorageUnitById } = storageUnitStore();
    const { getArmBotById } = armBotStore();
    const { getConveyorById } = conveyorStore();
    const { getVehicleById } = vehicleStore();
    const { getMachineById } = machineStore();
    const { triggerPointActions } = useTriggerHandler();
    const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
    const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
    const { selectedProductStore } = useProductContext();
    const { selectedProduct } = selectedProductStore();
    const { setHumanActive, setHumanState, clearCurrentMaterials, setHumanLoad, setHumanScheduled, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = humanStore();

    const [currentPhase, setCurrentPhase] = useState<string>('init');
    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);
    const { speed } = useAnimationPlaySpeed();
    const { isPaused } = usePauseButtonStore();
    const previousTimeRef = useRef<number | null>(null);
    const animationFrameIdRef = useRef<number | null>(null);
    const humanAsset = getAssetById(human.modelUuid);
    const processStartTimeRef = useRef<number | null>(null);
    const processTimeRef = useRef<number>(0);
    const processAnimationIdRef = useRef<number | null>(null);
    const accumulatedPausedTimeRef = useRef<number>(0);
    const lastPauseTimeRef = useRef<number | null>(null);
    const hasLoggedHalfway = useRef(false);
    const hasLoggedCompleted = useRef(false);

    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 {
                    console.log("There is no path here...Choose valid path")
                    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 humanStatus(modelId: string, status: string) {
        // console.log(`${modelId} , ${status}`);
    }

    function reset() {
        setCurrentPhase('init');
        setHumanActive(human.modelUuid, false);
        setHumanState(human.modelUuid, 'idle');
        setHumanScheduled(human.modelUuid, false);
        setHumanLoad(human.modelUuid, 0);
        resetAnimation(human.modelUuid);
        setPath([]);
        isPausedRef.current = false;
        pauseTimeRef.current = 0;
        resetTime(human.modelUuid)
        activeTimeRef.current = 0
        idleTimeRef.current = 0
        previousTimeRef.current = null
        if (animationFrameIdRef.current !== null) {
            cancelAnimationFrame(animationFrameIdRef.current)
            animationFrameIdRef.current = null
        }
        if (processAnimationIdRef.current) {
            cancelAnimationFrame(processAnimationIdRef.current);
            processAnimationIdRef.current = null;
        }
        processStartTimeRef.current = null;
        processTimeRef.current = 0;
        accumulatedPausedTimeRef.current = 0;
        lastPauseTimeRef.current = null;
        hasLoggedHalfway.current = false;
        hasLoggedCompleted.current = false;
        const object = scene.getObjectByProperty('uuid', human.modelUuid);
        if (object && human) {
            object.position.set(human.position[0], human.position[1], human.position[2]);
            object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
        }
    }

    useEffect(() => {
        if (isPlaying) {
            const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');

            if (!action || !(action as HumanAction).assemblyPoint || (action as HumanAction).actionType === 'worker') return;

            if (!human.isActive && human.state === 'idle' && (currentPhase === 'init' || currentPhase === 'picking')) {

                const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
                if (!humanMesh) return;

                const toPickupPath = computePath(
                    new THREE.Vector3(...humanMesh.position.toArray()),
                    new THREE.Vector3(
                        (action as HumanAction)?.assemblyPoint?.position?.[0] ?? 0,
                        (action as HumanAction)?.assemblyPoint?.position?.[1] ?? 0,
                        (action as HumanAction)?.assemblyPoint?.position?.[2] ?? 0
                    )
                );
                setPath(toPickupPath);
                setHumanState(human.modelUuid, 'idle');
                setCurrentPhase('init_assembly');
                setHumanActive(human.modelUuid, false);
                setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
                humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
            } else if (!human.isActive && human.state === 'idle' && currentPhase === 'waiting') {

                if (human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current !== 'working_standing') {
                    setCurrentAnimation(human.modelUuid, 'working_standing', true, true, false);
                    setHumanState(human.modelUuid, 'running');
                    setCurrentPhase('assembling');
                    setHumanActive(human.modelUuid, true);

                    processStartTimeRef.current = performance.now();
                    processTimeRef.current = (action as HumanAction).processTime || 0;
                    accumulatedPausedTimeRef.current = 0;
                    lastPauseTimeRef.current = null;
                    hasLoggedHalfway.current = false;
                    hasLoggedCompleted.current = false;

                    if (!processAnimationIdRef.current) {
                        processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
                    }
                }
            } else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
                if ((action as HumanAction).assemblyPoint && currentPhase === 'assembling') {
                    setHumanState(human.modelUuid, 'idle');
                    setCurrentPhase('waiting');
                    setHumanActive(human.modelUuid, false);
                    setHumanScheduled(human.modelUuid, false);
                    setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
                    humanStatus(human.modelUuid, 'Human is waiting for material in assembly');

                    decrementHumanLoad(human.modelUuid, 1);
                    const material = removeLastMaterial(human.modelUuid);
                    if (material) {
                        triggerPointActions((action as HumanAction), material.materialId);
                    }
                }
            }

        } else {
            reset()
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);

    const trackAssemblyProcess = useCallback(() => {
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');

        const now = performance.now();

        if (!processStartTimeRef.current || !(action as HumanAction).processTime || !action) {
            return;
        }

        if (isPausedRef.current) {
            if (!lastPauseTimeRef.current) {
                lastPauseTimeRef.current = now;
            }
            processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
            return;
        } else if (lastPauseTimeRef.current) {
            accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
            lastPauseTimeRef.current = null;
        }

        const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
        const totalProcessTimeMs = ((action as HumanAction).processTime || 1) * 1000;

        if (elapsed >= totalProcessTimeMs / 2 && !hasLoggedHalfway.current) {
            hasLoggedHalfway.current = true;
            if (human.currentMaterials.length > 0) {
                setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
            }
            humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
        }

        if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
            hasLoggedCompleted.current = true;
            setCurrentAnimation(human.modelUuid, 'working_standing', true, true, true);
            if (processAnimationIdRef.current) {
                cancelAnimationFrame(processAnimationIdRef.current);
                processAnimationIdRef.current = null;
            }
            humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
            return;
        }

        processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
    }, [human.modelUuid, human.currentMaterials]);

    useEffect(() => {
        if (isPlaying) {
            const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
            if (!action || action.actionType !== 'worker' || !action.pickUpPoint || !action.dropPoint) return;

            if (!human.isActive && human.state === 'idle' && (currentPhase === 'init' || currentPhase === 'waiting')) {

                const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
                if (!humanMesh) return;

                const toPickupPath = computePath(
                    new THREE.Vector3(...humanMesh.position.toArray()),
                    new THREE.Vector3(
                        action?.pickUpPoint?.position?.[0] ?? 0,
                        action?.pickUpPoint?.position?.[1] ?? 0,
                        action?.pickUpPoint?.position?.[2] ?? 0
                    )
                );
                setPath(toPickupPath);
                setCurrentPhase('init-pickup');
                setHumanState(human.modelUuid, 'running');
                setHumanActive(human.modelUuid, true);
                setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
                humanStatus(human.modelUuid, 'Started from init, heading to pickup');
                return;
            } else if (!human.isActive && human.state === 'idle' && currentPhase === 'picking') {
                if (humanAsset && human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
                    if (action.pickUpPoint && action.dropPoint) {
                        const toDrop = computePath(
                            new THREE.Vector3(
                                action.pickUpPoint.position?.[0] ?? 0,
                                action.pickUpPoint.position?.[1] ?? 0,
                                action.pickUpPoint.position?.[2] ?? 0
                            ),
                            new THREE.Vector3(
                                action.dropPoint.position?.[0] ?? 0,
                                action.dropPoint.position?.[1] ?? 0,
                                action.dropPoint.position?.[2] ?? 0
                            )
                        );
                        setPath(toDrop);
                        setCurrentPhase('pickup-drop');
                        setHumanState(human.modelUuid, 'running');
                        setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
                        humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
                    }
                } else if (human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && humanAsset?.animationState?.current !== 'pickup') {
                    if (human.currentMaterials[0]?.materialId) {
                        setIsVisible(human.currentMaterials[0]?.materialId, false);
                    }
                    setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
                }
            } else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) {
                if (action.pickUpPoint && action.dropPoint) {
                    const dropToPickup = computePath(
                        new THREE.Vector3(
                            action.dropPoint.position?.[0] ?? 0,
                            action.dropPoint.position?.[1] ?? 0,
                            action.dropPoint.position?.[2] ?? 0
                        ),
                        new THREE.Vector3(
                            action.pickUpPoint.position?.[0] ?? 0,
                            action.pickUpPoint.position?.[1] ?? 0,
                            action.pickUpPoint.position?.[2] ?? 0
                        )
                    );
                    setPath(dropToPickup);
                    setCurrentPhase('drop-pickup');
                    setHumanState(human.modelUuid, 'running');
                    setHumanActive(human.modelUuid, true);
                    setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
                    humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
                }
            }

        } else {
            reset()
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);

    function handleCallBack() {
        if (currentPhase === 'init-pickup') {
            setCurrentPhase('picking');
            setHumanState(human.modelUuid, 'idle');
            setHumanActive(human.modelUuid, false);
            setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
            humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
            setPath([]);
        } if (currentPhase === 'init_assembly') {
            setCurrentPhase('waiting');
            setHumanState(human.modelUuid, 'idle');
            setHumanActive(human.modelUuid, false);
            setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
            humanStatus(human.modelUuid, 'Reached assembly point, waiting for material');
            setPath([]);
        } else if (currentPhase === 'pickup-drop') {
            setCurrentPhase('dropping');
            setHumanState(human.modelUuid, 'idle');
            setHumanActive(human.modelUuid, false);
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
            humanStatus(human.modelUuid, 'Reached drop point');
            setPath([]);
        } else if (currentPhase === 'drop-pickup') {
            setCurrentPhase('picking');
            setHumanState(human.modelUuid, 'idle');
            setHumanActive(human.modelUuid, false);
            setHumanScheduled(human.modelUuid, false);
            setPath([]);
            clearCurrentMaterials(human.modelUuid);
            setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
            humanStatus(human.modelUuid, 'Reached pickup point again, cycle complete');
        }
    }

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

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

        if (human.isActive) {
            if (!isPausedRef.current) {
                activeTimeRef.current += deltaTime * isSpeedRef.current;
            }
        } else {
            if (!isPausedRef.current) {
                idleTimeRef.current += deltaTime * isSpeedRef.current;
            }
        }
        animationFrameIdRef.current = requestAnimationFrame(animate);
    }

    useEffect(() => {
        if (!isPlaying) return
        if (!human.isActive) {
            const roundedActiveTime = Math.round(activeTimeRef.current);
            incrementActiveTime(human.modelUuid, roundedActiveTime);
            activeTimeRef.current = 0;
        } else {
            const roundedIdleTime = Math.round(idleTimeRef.current);
            incrementIdleTime(human.modelUuid, roundedIdleTime);
            idleTimeRef.current = 0;
        }

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

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

    function startUnloadingProcess() {
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');

        const humanAsset = getAssetById(human.modelUuid);
        if (humanAsset?.animationState?.current !== 'drop') {
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
        }
        if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
            if ((action as HumanAction).triggers.length > 0) {
                const trigger = getTriggerByUuid(selectedProduct.productUuid, (action as HumanAction).triggers[0]?.triggerUuid);
                const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');

                if (trigger && model) {
                    if (model.type === 'transfer') {
                        if (action) {
                            handleMaterialDropToConveyor(model);
                        }
                    } else if (model.type === 'machine') {
                        if (action) {
                            handleMaterialDropToMachine(model);
                        }
                    } else if (model.type === 'roboticArm') {
                        if (action) {
                            handleMaterialDropToArmBot(model);
                        }
                    } else if (model.type === 'storageUnit') {
                        if (action) {
                            handleMaterialDropToStorageUnit(model);
                        }
                    } else if (model.type === 'vehicle') {
                        if (action) {
                            handleMaterialDropToVehicle(model);
                        }
                    }
                } else {
                    const droppedMaterial = human.currentLoad;
                    handleMaterialDropByDefault(droppedMaterial);
                }
            } else {
                const droppedMaterial = human.currentLoad;
                handleMaterialDropByDefault(droppedMaterial);
            }
        } else {
            requestAnimationFrame(startUnloadingProcess);
        }
    }

    function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
        const humanAsset = getAssetById(human.modelUuid);
        if (model && humanAsset?.animationState?.current !== 'drop') {
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
        }

        const checkAnimation = () => {
            if (humanAsset?.animationState?.isCompleted) {
                if (model.point.action.actionType === 'store') {
                    loopMaterialDropToStorage(
                        human.modelUuid,
                        human.currentLoad,
                        model.modelUuid,
                        model.point.action.storageCapacity,
                        (action as HumanAction)
                    );
                }
            } else {
                requestAnimationFrame(checkAnimation);
            }
        };
        checkAnimation();
    }

    function loopMaterialDropToStorage(
        humanId: string,
        humanCurrentLoad: number,
        storageUnitId: string,
        storageMaxCapacity: number,
        action: HumanAction
    ) {
        const storageUnit = getStorageUnitById(storageUnitId);
        const humanAsset = getAssetById(human.modelUuid);

        if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) {
            return;
        }

        decrementHumanLoad(humanId, 1);
        humanCurrentLoad -= 1;

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

        if (humanCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
            resetAnimation(human.modelUuid);
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);

            const waitForNextDrop = () => {
                if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
                    loopMaterialDropToStorage(
                        humanId,
                        humanCurrentLoad,
                        storageUnitId,
                        storageMaxCapacity,
                        action
                    );
                } else {
                    requestAnimationFrame(waitForNextDrop);
                }
            };
            waitForNextDrop();
        }
    }

    function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
        const humanAsset = getAssetById(human.modelUuid);
        if (humanAsset?.animationState?.current !== 'drop') {
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
        }

        const checkAnimation = () => {
            if (humanAsset?.animationState?.isCompleted) {
                const conveyor = getConveyorById(model.modelUuid);
                if (conveyor) {
                    loopMaterialDropToConveyor(
                        human.modelUuid,
                        human.currentLoad,
                        conveyor.modelUuid,
                        (action as HumanAction)
                    );
                }
            } else {
                requestAnimationFrame(checkAnimation);
            }
        };
        checkAnimation();
    }

    function loopMaterialDropToConveyor(
        humanId: string,
        humanCurrentLoad: number,
        conveyorId: string,
        action: HumanAction
    ) {
        const conveyor = getConveyorById(conveyorId);
        const humanAsset = getAssetById(human.modelUuid);

        if (!conveyor || humanCurrentLoad <= 0) {
            return;
        }

        decrementHumanLoad(humanId, 1);
        humanCurrentLoad -= 1;

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

        if (humanCurrentLoad > 0) {
            resetAnimation(human.modelUuid);
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);

            const waitForNextDrop = () => {
                if (humanAsset?.animationState?.isCompleted) {
                    loopMaterialDropToConveyor(
                        humanId,
                        humanCurrentLoad,
                        conveyorId,
                        action
                    );
                } else {
                    requestAnimationFrame(waitForNextDrop);
                }
            };
            waitForNextDrop();
        }
    }

    function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
        const humanAsset = getAssetById(human.modelUuid);
        if (humanAsset?.animationState?.current !== 'drop') {
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
        }

        const checkAnimation = () => {
            if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
                const armBot = getArmBotById(model.modelUuid);
                if (armBot && armBot.state === 'idle' && !armBot.isActive) {
                    loopMaterialDropToArmBot(
                        human.modelUuid,
                        human.currentLoad,
                        model.modelUuid,
                        (action as HumanAction)
                    );
                }
            } else {
                requestAnimationFrame(checkAnimation);
            }
        };
        checkAnimation();
    }

    function loopMaterialDropToArmBot(
        humanId: string,
        humanCurrentLoad: number,
        armBotId: string,
        action: HumanAction
    ) {
        const armBot = getArmBotById(armBotId);
        const humanAsset = getAssetById(human.modelUuid);

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

        decrementHumanLoad(humanId, 1);
        humanCurrentLoad -= 1;

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

        if (humanCurrentLoad > 0) {
            resetAnimation(human.modelUuid);
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);

            const waitForNextTransfer = () => {
                const currentArmBot = getArmBotById(armBotId);
                if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
                    if (humanAsset?.animationState?.isCompleted) {
                        loopMaterialDropToArmBot(
                            humanId,
                            humanCurrentLoad,
                            armBotId,
                            action
                        );
                    } else {
                        requestAnimationFrame(waitForNextTransfer);
                    }
                } else {
                    requestAnimationFrame(waitForNextTransfer);
                }
            };
            waitForNextTransfer();
        }
    }

    function handleMaterialDropToVehicle(model: VehicleEventSchema) {
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
        const humanAsset = getAssetById(human.modelUuid);
        if (humanAsset?.animationState?.current !== 'drop') {
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
        }

        const checkAnimation = () => {
            if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
                const vehicle = getVehicleById(model.modelUuid);
                if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) {
                    loopMaterialDropToVehicle(
                        human.modelUuid,
                        human.currentLoad,
                        model.modelUuid,
                        (action as HumanAction)
                    );
                }
            } else {
                requestAnimationFrame(checkAnimation);
            }
        };
        checkAnimation();
    }

    function loopMaterialDropToVehicle(
        humanId: string,
        humanCurrentLoad: number,
        vehicleId: string,
        action: HumanAction
    ) {
        const vehicle = getVehicleById(vehicleId);
        const humanAsset = getAssetById(human.modelUuid);

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

        decrementHumanLoad(humanId, 1);
        humanCurrentLoad -= 1;

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

        if (humanCurrentLoad > 0) {
            resetAnimation(human.modelUuid);
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);

            const waitForNextTransfer = () => {
                const currentVehicle = getVehicleById(vehicleId);
                if (currentVehicle && currentVehicle.state === 'idle' && !currentVehicle.isActive) {
                    if (humanAsset?.animationState?.isCompleted) {
                        loopMaterialDropToVehicle(
                            humanId,
                            humanCurrentLoad,
                            vehicleId,
                            action
                        );
                    } else {
                        requestAnimationFrame(waitForNextTransfer);
                    }
                } else {
                    requestAnimationFrame(waitForNextTransfer);
                }
            };
            waitForNextTransfer();
        }
    }

    function handleMaterialDropToMachine(model: MachineEventSchema) {
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
        const humanAsset = getAssetById(human.modelUuid);
        if (humanAsset?.animationState?.current !== 'drop') {
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
        }

        const checkAnimation = () => {
            if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
                const machine = getMachineById(model.modelUuid);
                if (machine && machine.state === 'idle' && !machine.isActive) {
                    loopMaterialDropToMachine(
                        human.modelUuid,
                        human.currentLoad,
                        model.modelUuid,
                        (action as HumanAction)
                    );
                }
            } else {
                requestAnimationFrame(checkAnimation);
            }
        };
        checkAnimation();
    }

    function loopMaterialDropToMachine(
        humanId: string,
        humanCurrentLoad: number,
        machineId: string,
        action: HumanAction
    ) {
        const machine = getMachineById(machineId);
        const humanAsset = getAssetById(human.modelUuid);

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

        decrementHumanLoad(humanId, 1);
        humanCurrentLoad -= 1;

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

        if (humanCurrentLoad > 0) {
            resetAnimation(human.modelUuid);
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);

            const waitForNextTransfer = () => {
                const currentMachine = getMachineById(machineId);
                if (currentMachine && currentMachine.state === 'idle' && !currentMachine.isActive) {
                    if (humanAsset?.animationState?.isCompleted) {
                        loopMaterialDropToMachine(
                            humanId,
                            humanCurrentLoad,
                            machineId,
                            action
                        );
                    } else {
                        requestAnimationFrame(waitForNextTransfer);
                    }
                } else {
                    requestAnimationFrame(waitForNextTransfer);
                }
            };
            waitForNextTransfer();
        }
    }

    function handleMaterialDropByDefault(droppedMaterial: number) {
        const humanAsset = getAssetById(human.modelUuid);
        if (humanAsset?.animationState?.current !== 'drop') {
            setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
        }

        if (humanAsset?.animationState?.isCompleted) {
            const remainingMaterials = droppedMaterial - 1;
            decrementHumanLoad(human.modelUuid, 1);
            const material = removeLastMaterial(human.modelUuid);

            if (material) {
                setEndTime(material.materialId, performance.now());
                removeMaterial(material.materialId);
            }

            if (remainingMaterials > 0) {
                resetAnimation(human.modelUuid);
                setCurrentAnimation(human.modelUuid, 'drop', true, false, false);

                requestAnimationFrame(() => handleMaterialDropByDefault(remainingMaterials));
            }
            return;
        }

        requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
    }

    return (
        <>

            <HumanAnimator
                path={path}
                handleCallBack={handleCallBack}
                currentPhase={currentPhase}
                human={human}
                reset={reset}
                startUnloadingProcess={startUnloadingProcess}
            />

            <MaterialAnimator currentPhase={currentPhase} human={human} />
        </>
    )
}

export default HumanInstance