import React, { useEffect, useRef, useState } from 'react'
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import IKInstance from '../ikInstance/ikInstance';
import RoboticArmAnimator from '../animator/roboticArmAnimator';
import MaterialAnimator from '../animator/materialAnimator';
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
import { useProductStore } from '../../../../../store/simulation/useProductStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore';
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';

function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {

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

    const { setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
    const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
    const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
    const { setIsVisible, getMaterialById } = useMaterialStore();
    const { selectedProduct } = useSelectedProduct();
    const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = useProductStore();
    const { triggerPointActions } = useTriggerHandler();
    const { isPlaying } = usePlayButtonStore();
    const { isReset } = useResetButtonStore();
    const { isPaused } = usePauseButtonStore();

    function firstFrame() {
        startTime = performance.now();
        step();
    }

    const action = getActionByUuid(selectedProduct.productId, 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.productId, 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)
                    }
                } else {
                    setIsVisible(armBot.currentAction.materialId, false);
                }
            } else {
                setIsVisible(armBot.currentAction.materialId, false);
            }
        }
    }

    const handleDropTrigger = () => {

        if (armBot.currentAction) {
            const action = getActionByUuid(selectedProduct.productId, armBot.currentAction.actionUuid);
            if (action && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) {
                const model = getEventByModelUuid(selectedProduct.productId, 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)
            }
        }
    }

    function step() {
        if (isPausedRef.current) {
            if (!pauseTimeRef.current) {
                pauseTimeRef.current = performance.now();
            }
            requestAnimationFrame(() => step());
            return;
        }
        if (pauseTimeRef.current) {
            const pauseDuration = performance.now() - pauseTimeRef.current;
            startTime += pauseDuration;
            pauseTimeRef.current = null;
        }
        const elapsedTime = performance.now() - startTime;
        if (elapsedTime < 1000) {
            // Wait until 1500ms has passed
            requestAnimationFrame(step);
            return;
        }
        if (currentPhase === "picking") {
            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.")
        } else if (currentPhase === "dropping") {
            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.")
        }
    }

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

    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)
            isPausedRef.current = false
            pauseTimeRef.current = null
            startTime = 0
            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])

    useEffect(() => {
        const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
        if (targetMesh) {
            targetMesh.visible = (!isPlaying)
        }
        const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
        if (!isReset && isPlaying) {
            //Moving armBot from initial point to rest position.
            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.")
            }
            //Waiting for trigger.
            else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
                logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
            }
            //Moving to pickup point
            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.")
            }
            // Moving to Pick to Drop position
            else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) {
                requestAnimationFrame(firstFrame);
            }
            //Moving to drop point to restPosition
            else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) {
                requestAnimationFrame(firstFrame);
            }
        } 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
            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.isActive && 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 modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} armBot={armBot} groupRef={groupRef} />
                    <RoboticArmAnimator
                        HandleCallback={HandleCallback}
                        restPosition={restPosition}
                        ikSolver={ikSolver}
                        targetBone={targetBone}
                        armBot={armBot}
                        logStatus={logStatus}
                        path={path}
                        currentPhase={currentPhase}
                    />
                </>
            )}
            <MaterialAnimator ikSolver={ikSolver} armBot={armBot} currentPhase={currentPhase} />
        </>
    )
}

export default RoboticArmInstance;
