import { useEffect, useRef, useState } from 'react'
import { useFrame, useThree } from '@react-three/fiber';
import * as THREE from 'three';
import { Line } from '@react-three/drei';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';

interface VehicleAnimatorProps {
    path: [number, number, number][];
    handleCallBack: () => void;
    reset: () => void;
    startUnloadingProcess: () => void;
    currentPhase: string;
    agvUuid: string;
    agvDetail: VehicleStatus;
}

function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset, startUnloadingProcess }: VehicleAnimatorProps) {
    const { getVehicleById } = useVehicleStore();
    const { isPaused } = usePauseButtonStore();
    const { isPlaying } = usePlayButtonStore();
    const { speed } = useAnimationPlaySpeed();
    const { isReset, setReset } = useResetButtonStore();
    const progressRef = useRef<number>(0);
    const movingForward = useRef<boolean>(true);
    const completedRef = useRef<boolean>(false);
    const [objectRotation, setObjectRotation] = useState<{ x: number; y: number; z: number } | undefined>(agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 })
    const [restRotation, setRestingRotation] = useState<boolean>(true);
    const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
    const { scene } = useThree();

    useEffect(() => {
        if (currentPhase === 'stationed-pickup' && path.length > 0) {
            setCurrentPath(path);
            setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation)
        } else if (currentPhase === 'pickup-drop' && path.length > 0) {
            setObjectRotation(agvDetail.point.action?.unLoadPoint?.rotation)
            setCurrentPath(path);
        } else if (currentPhase === 'drop-pickup' && path.length > 0) {
            setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation)
            setCurrentPath(path);
        }
    }, [currentPhase, path, objectRotation]);

    useEffect(() => {
        completedRef.current = false;
    }, [currentPath]);

    useEffect(() => {
        if (isReset || !isPlaying) {
            reset();
            setCurrentPath([]);
            completedRef.current = false;
            movingForward.current = true;
            progressRef.current = 0;
            setReset(false);
            setRestingRotation(true);
            const object = scene.getObjectByProperty('uuid', agvUuid);
            const vehicle = getVehicleById(agvDetail.modelUuid);
            if (object && vehicle) {
                object.position.set(vehicle.position[0], vehicle.position[1], vehicle.position[2]);
                object.rotation.set(vehicle.rotation[0], vehicle.rotation[1], vehicle.rotation[2]);
            }
        }
    }, [isReset, isPlaying])

    // useFrame((_, delta) => {
    //     const object = scene.getObjectByProperty('uuid', agvUuid);
    //     if (!object || currentPath.length < 2) return;
    //     if (isPaused) return;

    //     let totalDistance = 0;
    //     const distances = [];
    //     let accumulatedDistance = 0;
    //     let index = 0;
    //     const rotationSpeed = 1;

    //     for (let i = 0; i < currentPath.length - 1; i++) {
    //         const start = new THREE.Vector3(...currentPath[i]);
    //         const end = new THREE.Vector3(...currentPath[i + 1]);
    //         const segmentDistance = start.distanceTo(end);
    //         distances.push(segmentDistance);
    //         totalDistance += segmentDistance;
    //     }

    //     while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
    //         accumulatedDistance += distances[index];
    //         index++;
    //     }

    //     if (index < distances.length) {
    //         const start = new THREE.Vector3(...currentPath[index]);
    //         const end = new THREE.Vector3(...currentPath[index + 1]);
    //         const segmentDistance = distances[index];

    //         const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
    //         const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);

    //         const currentAngle = object.rotation.y;

    //         let angleDifference = targetAngle - currentAngle;
    //         if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
    //         if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;

    //         const maxRotationStep = (rotationSpeed * speed * agvDetail.speed) * delta;
    //         object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
    //         const isAligned = Math.abs(angleDifference) < 0.01;

    //         if (isAligned) {
    //             progressRef.current += delta * (speed * agvDetail.speed);
    //             const t = (progressRef.current - accumulatedDistance) / segmentDistance;
    //             const position = start.clone().lerp(end, t);
    //             object.position.copy(position);
    //         }
    //     }

    //     if (progressRef.current >= totalDistance) {
    //         if (restRotation && objectRotation) {
    //             const targetEuler = new THREE.Euler(
    //                 objectRotation.x,
    //                 objectRotation.y - (agvDetail.point.action.steeringAngle),
    //                 objectRotation.z
    //             );
    //             const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
    //             object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * agvDetail.speed));
    //             if (object.quaternion.angleTo(targetQuaternion) < 0.01) {
    //                 object.quaternion.copy(targetQuaternion);
    //                 object.rotation.copy(targetEuler);
    //                 setRestingRotation(false);
    //             }
    //             return;
    //         }
    //     }
    //     if (progressRef.current >= totalDistance) {
    //         setRestingRotation(true);
    //         progressRef.current = 0;
    //         movingForward.current = !movingForward.current;
    //         setCurrentPath([]);
    //         handleCallBack();
    //         if (currentPhase === 'pickup-drop') {
    //             requestAnimationFrame(startUnloadingProcess);
    //         }
    //     }
    // });
    const lastTimeRef = useRef(performance.now());

    useFrame(() => {
        const now = performance.now();
        const delta = (now - lastTimeRef.current) / 1000;
        lastTimeRef.current = now;

        const object = scene.getObjectByProperty('uuid', agvUuid);
        if (!object || currentPath.length < 2) return;
        if (isPaused) return;

        let totalDistance = 0;
        const distances = [];
        let accumulatedDistance = 0;
        let index = 0;
        const rotationSpeed = 1;

        for (let i = 0; i < currentPath.length - 1; i++) {
            const start = new THREE.Vector3(...currentPath[i]);
            const end = new THREE.Vector3(...currentPath[i + 1]);
            const segmentDistance = start.distanceTo(end);
            distances.push(segmentDistance);
            totalDistance += segmentDistance;
        }

        while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
            accumulatedDistance += distances[index];
            index++;
        }

        if (index < distances.length) {
            const start = new THREE.Vector3(...currentPath[index]);
            const end = new THREE.Vector3(...currentPath[index + 1]);
            const segmentDistance = distances[index];

            const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
            const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
            const currentAngle = object.rotation.y;

            let angleDifference = targetAngle - currentAngle;
            if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
            if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;

            const maxRotationStep = (rotationSpeed * speed * agvDetail.speed) * delta;
            object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
            const isAligned = Math.abs(angleDifference) < 0.01;

            if (isAligned) {
                progressRef.current += delta * (speed * agvDetail.speed);
                const t = (progressRef.current - accumulatedDistance) / segmentDistance;
                const position = start.clone().lerp(end, t);
                object.position.copy(position);
            }
        }

        if (progressRef.current >= totalDistance) {
            if (restRotation && objectRotation) {
                const targetEuler = new THREE.Euler(
                    objectRotation.x,
                    objectRotation.y - agvDetail.point.action.steeringAngle,
                    objectRotation.z
                );
                const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
                object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * agvDetail.speed));
                if (object.quaternion.angleTo(targetQuaternion) < 0.01) {
                    object.quaternion.copy(targetQuaternion);
                    object.rotation.copy(targetEuler);
                    setRestingRotation(false);
                }
                return;
            }
        }

        if (progressRef.current >= totalDistance) {
            setRestingRotation(true);
            progressRef.current = 0;
            movingForward.current = !movingForward.current;
            setCurrentPath([]);
            handleCallBack();
            if (currentPhase === 'pickup-drop') {
                requestAnimationFrame(startUnloadingProcess);
            }
        }
    });

    return (
        <>
            {currentPath.length > 0 && (
                // helper
                <group visible={false}>
                    <Line points={currentPath} color="blue" lineWidth={3} />
                    {currentPath.map((point, index) => (
                        <mesh key={index} position={point}>
                            <sphereGeometry args={[0.1, 16, 16]} />
                            <meshStandardMaterial color="red" />
                        </mesh>
                    ))}
                </group>
            )}
        </>
    );
}

export default VehicleAnimator;