import { useEffect, useRef, useState } from 'react'
import * as THREE from "three";
import IKInstance from '../ikInstance/ikInstance';
import RoboticArmAnimator from '../animator/roboticArmAnimator';
import MaterialAnimator from '../animator/materialAnimator';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';

function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
    const [currentPhase, setCurrentPhase] = useState<(string)>("init");
    const [path, setPath] = useState<[number, number, number][]>([]);
    const [ikSolver, setIkSolver] = useState<any>(null);
    const restPosition = new THREE.Vector3(0, 1.75, -1.6);
    const targetBone = "Target";
    const pauseTimeRef = useRef<number | null>(null);
    const isPausedRef = useRef<boolean>(false);
    const isSpeedRef = useRef<any>(null);
    let startTime: number;

    const { selectedProductStore } = useProductContext();
    const { materialStore, armBotStore, vehicleStore, storageUnitStore, productStore, assetStore } = useSceneContext();
    const { resetAsset } = assetStore();
    const { setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = armBotStore();
    const { decrementVehicleLoad, removeLastMaterial } = vehicleStore();
    const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = storageUnitStore();
    const { getMaterialById, setIsVisible } = materialStore();
    const { selectedProduct } = selectedProductStore();
    const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = productStore();
    const { triggerPointActions } = useTriggerHandler();
    const { isPlaying } = usePlayButtonStore();
    const { isReset } = useResetButtonStore();
    const { isPaused } = usePauseButtonStore();
    const { speed } = useAnimationPlaySpeed();

    const activeSecondsElapsed = useRef(0);
    const idleSecondsElapsed = useRef(0);

    const animationFrameIdRef = useRef<number | null>(null);
    const previousTimeRef = useRef<number | null>(null);

    const lastRemoved = useRef<{ type: string, materialId: string, modelId: string } | null>(null);

    const action = getActionByUuid(selectedProduct.productUuid, armBot.currentAction?.actionUuid || '');

    const handlePickUpTrigger = () => {
        if (armBot.currentAction && armBot.currentAction.materialId) {
            const material = getMaterialById(armBot.currentAction.materialId);
            if (material && material.previous && material.previous.modelUuid) {
                const previousModel = getEventByActionUuid(selectedProduct.productUuid, material.previous.actionUuid);
                if (previousModel) {
                    if (previousModel.type === 'transfer') {
                        setIsVisible(armBot.currentAction.materialId, false);
                    } else if (previousModel.type === 'machine') {
                        // machine specific logic
                    } else if (previousModel.type === 'vehicle') {
                        decrementVehicleLoad(previousModel.modelUuid, 1);
                        removeLastMaterial(previousModel.modelUuid);
                    } else if (previousModel.type === 'storageUnit') {
                        // storage unit logic
                        removeLastStorageMaterial(previousModel.modelUuid);
                        updateCurrentLoad(previousModel.modelUuid, -1)
                    }
                    lastRemoved.current = { type: previousModel.type, materialId: armBot.currentAction.materialId, modelId: previousModel.modelUuid };
                } else {
                    setIsVisible(armBot.currentAction.materialId, false);
                }
            } else {
                setIsVisible(armBot.currentAction.materialId, false);
            }
        }
    }

    const handleDropTrigger = () => {

        if (armBot.currentAction) {
            const action = getActionByUuid(selectedProduct.productUuid, armBot.currentAction.actionUuid);
            const model = getEventByModelUuid(selectedProduct.productUuid, action?.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
            if (action && action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid) {
                if (!model) return;
                if (model.type === 'transfer') {
                    setIsVisible(armBot.currentAction.materialId || '', true);
                } else if (model.type === 'machine') {
                    //
                } else if (model.type === 'vehicle') {
                    //
                } else if (model.type === 'storageUnit') {
                    //
                }
            }
            if (action && armBot.currentAction.materialId) {
                triggerPointActions(action, armBot.currentAction.materialId)
                removeCurrentAction(armBot.modelUuid)
            }

            // if (lastRemoved.current) {
            //     if (lastRemoved.current.type === 'transfer') {
            //         setIsPaused(lastRemoved.current.materialId, true)
            //     } else {
            //         setIsPaused(lastRemoved.current.materialId, false)
            //     }
            // }
        }
    }

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

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

    useEffect(() => {
        if (isReset || !isPlaying) {
            logStatus(armBot.modelUuid, "Simulation Play Reset Successfully")
            setArmBotActive(armBot.modelUuid, false)
            setArmBotState(armBot.modelUuid, "idle")
            setCurrentPhase("init");
            setPath([])
            setIkSolver(null);
            removeCurrentAction(armBot.modelUuid);
            resetAsset(armBot.modelUuid);
            isPausedRef.current = false
            pauseTimeRef.current = null
            startTime = 0
            activeSecondsElapsed.current = 0;
            idleSecondsElapsed.current = 0;
            previousTimeRef.current = null;

            if (animationFrameIdRef.current !== null) {
                cancelAnimationFrame(animationFrameIdRef.current);
                animationFrameIdRef.current = null;
            }
            const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone
            );
            if (targetBones && isPlaying) {
                let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
                if (curve) {
                    setPath(curve.points.map(point => [point.x, point.y, point.z]));
                    logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.")
                }
            }
        }
    }, [isReset, isPlaying])

    function animate(currentTime: number) {
        if (previousTimeRef.current === null) {
            previousTimeRef.current = currentTime;
        }
        const deltaTime = (currentTime - previousTimeRef.current) / 1000;
        previousTimeRef.current = currentTime;
        if (armBot.isActive) {
            if (!isPausedRef.current) {
                activeSecondsElapsed.current += deltaTime * isSpeedRef.current;
                // console.log(' activeSecondsElapsed.current: ', activeSecondsElapsed.current);
            }
        } else {
            if (!isPausedRef.current) {
                idleSecondsElapsed.current += deltaTime * isSpeedRef.current;
                // console.log('idleSecondsElapsed.current: ', idleSecondsElapsed.current);
            }
        }
        animationFrameIdRef.current = requestAnimationFrame(animate);
    }

    useEffect(() => {
        if (!isPlaying) return

        if (!armBot.isActive && armBot.state === "idle" && (currentPhase === "rest" || currentPhase === "init")) {
            cancelAnimationFrame(animationFrameIdRef.current!);
            animationFrameIdRef.current = null;
            const roundedActiveTime = Math.round(activeSecondsElapsed.current); // Get the final rounded active time
            // console.log('🚨Final Active Time:',armBot.modelUuid, roundedActiveTime, 'seconds');
            incrementActiveTime(armBot.modelUuid, roundedActiveTime);
            activeSecondsElapsed.current = 0;

        } else if (armBot.isActive && armBot.state !== "idle" && currentPhase !== "rest" && armBot.currentAction) {
            cancelAnimationFrame(animationFrameIdRef.current!);
            animationFrameIdRef.current = null;
            const roundedIdleTime = Math.round(idleSecondsElapsed.current); // Get the final rounded idle time
            // console.log('🕒 Final Idle Time:', armBot.modelUuid,roundedIdleTime, 'seconds');
            incrementIdleTime(armBot.modelUuid, roundedIdleTime);
            idleSecondsElapsed.current = 0;

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

        return () => {
            if (animationFrameIdRef.current !== null) {
                cancelAnimationFrame(animationFrameIdRef.current);
                animationFrameIdRef.current = null; // Reset the animation frame ID
            }
        };

    }, [armBot, currentPhase, isPlaying])

    useEffect(() => {
        const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
        if (!isReset && isPlaying) {
            if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") {
                if (targetBones) {
                    setArmBotActive(armBot.modelUuid, true)
                    setArmBotState(armBot.modelUuid, "running")
                    setCurrentPhase("init-to-rest");
                    let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
                    if (curve) {
                        setPath(curve.points.map(point => [point.x, point.y, point.z]));
                    }
                }
                logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.")
            } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
                logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
            } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) {
                if (armBot.currentAction) {

                    setArmBotActive(armBot.modelUuid, true);
                    setArmBotState(armBot.modelUuid, "running");
                    setCurrentPhase("rest-to-start");
                    if (!action) return;
                    const startPoint = (action as RoboticArmAction).process.startPoint;
                    if (startPoint) {
                        let curve = createCurveBetweenTwoPoints(targetBones.position, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
                        if (curve) {
                            setPath(curve.points.map(point => [point.x, point.y, point.z]));
                        }
                    }
                }
                logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.")
            } else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) {
                setTimeout(() => {
                    setArmBotActive(armBot.modelUuid, true);
                    setArmBotState(armBot.modelUuid, "running");
                    setCurrentPhase("start-to-end");
                    startTime = 0
                    if (!action) return;
                    const startPoint = (action as RoboticArmAction).process.startPoint;
                    const endPoint = (action as RoboticArmAction).process.endPoint;
                    if (startPoint && endPoint) {
                        let curve = createCurveBetweenTwoPoints(new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]));
                        if (curve) {
                            logStatus(armBot.modelUuid, "picking the object");
                            setPath(curve.points.map(point => [point.x, point.y, point.z]))
                            handlePickUpTrigger();
                        }
                    }
                    logStatus(armBot.modelUuid, "Moving armBot from start point to end position.")
                }, 100)
            } else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) {
                setTimeout(() => {
                    setArmBotActive(armBot.modelUuid, true);
                    setArmBotState(armBot.modelUuid, "running");
                    setCurrentPhase("end-to-rest");
                    startTime = 0;
                    if (!action) return;
                    const endPoint = (action as RoboticArmAction).process.endPoint;
                    if (endPoint) {
                        let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition);
                        if (curve) {
                            logStatus(armBot.modelUuid, "dropping the object");
                            setPath(curve.points.map(point => [point.x, point.y, point.z]));

                            handleDropTrigger();

                        }
                    }
                    logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.")
                }, 100)
            }
        } else {
            logStatus(armBot.modelUuid, "Simulation Play Exited")
            setArmBotActive(armBot.modelUuid, false)
            setArmBotState(armBot.modelUuid, "idle")
            setCurrentPhase("init");
            setIkSolver(null);
            setPath([])
            isPausedRef.current = false
            pauseTimeRef.current = null
            isPausedRef.current = false
            startTime = 0
            activeSecondsElapsed.current = 0;
            idleSecondsElapsed.current = 0;
            previousTimeRef.current = null;
            if (animationFrameIdRef.current !== null) {
                cancelAnimationFrame(animationFrameIdRef.current);
                animationFrameIdRef.current = null;
            }
            removeCurrentAction(armBot.modelUuid)
        }

    }, [currentPhase, armBot, isPlaying, isReset, ikSolver])

    function createCurveBetweenTwoPoints(p1: any, p2: any) {
        const mid = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5);
        const points = [p1, mid, p2];
        return new THREE.CatmullRomCurve3(points);
    }

    const HandleCallback = () => {
        if (armBot.isActive && armBot.state == "running" && currentPhase == "init-to-rest") {
            logStatus(armBot.modelUuid, "Callback triggered: rest");
            setArmBotActive(armBot.modelUuid, false)
            setArmBotState(armBot.modelUuid, "idle")
            setCurrentPhase("rest");
            setPath([])
        }
        else if (armBot.state == "running" && currentPhase == "rest-to-start") {
            logStatus(armBot.modelUuid, "Callback triggered: pick.");
            setArmBotActive(armBot.modelUuid, false)
            setArmBotState(armBot.modelUuid, "running")
            setCurrentPhase("picking");
            setPath([])
        }
        else if (armBot.isActive && armBot.state == "running" && currentPhase == "start-to-end") {
            logStatus(armBot.modelUuid, "Callback triggered: drop.");
            setArmBotActive(armBot.modelUuid, false)
            setArmBotState(armBot.modelUuid, "running")
            setCurrentPhase("dropping");
            setPath([])
        }
        else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") {
            logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed.");
            setArmBotActive(armBot.modelUuid, false)
            setArmBotState(armBot.modelUuid, "idle")
            setCurrentPhase("rest");
            setPath([])
        }
    }

    const logStatus = (id: string, status: string) => {
        // console.log('status: ', status);
    }

    return (
        <>
            {!isReset && isPlaying && (
                <>
                    <IKInstance setIkSolver={setIkSolver} armBot={armBot} />
                    <RoboticArmAnimator
                        HandleCallback={HandleCallback}
                        restPosition={restPosition}
                        ikSolver={ikSolver}
                        targetBone={targetBone}
                        armBot={armBot}
                        path={path}
                        currentPhase={currentPhase}
                    />
                </>
            )}
            <MaterialAnimator ikSolver={ikSolver} armBot={armBot} currentPhase={currentPhase} />
        </>
    )
}

export default RoboticArmInstance;
