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 OperatorAnimator from '../../animator/operatorAnimator';

function OperatorInstance({ human }: { human: HumanStatus }) {
    const { navMesh } = useNavMesh();
    const { isPlaying } = usePlayButtonStore();
    const { scene } = useThree();
    const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, craneStore, productStore } = useSceneContext();
    const { removeMaterial, setEndTime, setIsVisible } = materialStore();
    const { getStorageUnitById } = storageUnitStore();
    const { getArmBotById } = armBotStore();
    const { getConveyorById } = conveyorStore();
    const { getVehicleById } = vehicleStore();
    const { getMachineById } = machineStore();
    const { getCraneById } = craneStore();
    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, setCurrentPhase } = humanStore();

    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);

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

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

    const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
        try {
            const navMeshQuery = new NavMeshQuery(navMesh);
            let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
            let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
            const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
            if (
                segmentPath.length > 0 &&
                Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
                Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.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(startPoint, startPoint);
                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(human.modelUuid, '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
        }
        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.actionType !== 'operator' || !action.pickUpPoint || !action.dropPoint) return;

            if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
                const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
                if (!humanMesh) return;

                const toPickupPath = computePath(humanMesh.position.toArray(), action?.pickUpPoint?.position || [0, 0, 0]);

                setPath(toPickupPath);
                setCurrentPhase(human.modelUuid, 'init-loadPoint');
                setHumanState(human.modelUuid, 'running');
                setHumanActive(human.modelUuid, true);
                setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
                humanStatus(human.modelUuid, 'Started from init, heading to loadPoint');
            } else if (human.isActive && human.currentPhase === 'loadPoint-unloadPoint') {
                if (action.pickUpPoint && action.dropPoint && humanAsset?.animationState?.current === 'idle') {
                    const toDrop = computePath(action.pickUpPoint.position || [0, 0, 0], action.dropPoint.position || [0, 0, 0]);
                    setPath(toDrop);
                    setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
                    humanStatus(human.modelUuid, 'Started from loadPoint, heading to unloadPoint');
                }
            } else if (human.state === 'idle' && human.currentPhase === 'unhooking') {
                setTimeout(() => {
                    setHumanState(human.modelUuid, 'running');
                    setHumanActive(human.modelUuid, true);
                    setCurrentAnimation(human.modelUuid, 'working_standing', true, false, false);
                }, 1)
            }
        } else {
            reset()
        }
    }, [human, human.currentAction, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);

    function handleCallBack() {
        if (human.currentPhase === 'init-loadPoint') {
            setCurrentPhase(human.modelUuid, 'waiting');
            setHumanState(human.modelUuid, 'idle');
            setHumanActive(human.modelUuid, false);
            setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
            humanStatus(human.modelUuid, 'Reached loadPoint point, waiting for material');
            setPath([]);
        } else if (human.currentPhase === 'loadPoint-unloadPoint') {
            setCurrentPhase(human.modelUuid, 'unhooking');
            setHumanState(human.modelUuid, 'idle');
            setHumanActive(human.modelUuid, false);
            setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
            humanStatus(human.modelUuid, 'Reached loadPoint point, waiting for material');
            setPath([]);
        }
    }

    return (
        <>
            <OperatorAnimator
                path={path}
                handleCallBack={handleCallBack}
                human={human}
                reset={reset}
            />
        </>
    )
}

export default OperatorInstance;