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;
    currentPhase: string;
    agvUuid: number;
    agvDetail: VehicleStatus;
}

function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset }: VehicleAnimatorProps) {
    const { decrementVehicleLoad, 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 isPausedRef = useRef<boolean>(false);
    const pauseTimeRef = useRef<number | null>(null);
    const [progress, setProgress] = useState<number>(0);
    const [restRotation, setRestingRotation] = useState<boolean>(true);
    const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
    const { scene } = useThree();
    let startTime: number;
    let fixedInterval: number;
    let coveredDistance = progressRef.current;
    let objectRotation = (agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) as { x: number; y: number; z: number } | undefined;

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

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

    useEffect(() => {
        if (isReset || !isPlaying) {
            reset();
            setCurrentPath([]);
            setProgress(0);
            completedRef.current = false;
            movingForward.current = true;
            progressRef.current = 0;
            startTime = 0;
            coveredDistance = 0;
            setReset(false);
            setRestingRotation(true);
            decrementVehicleLoad(agvDetail.modelUuid, 0);
            isPausedRef.current = false;
            pauseTimeRef.current = 0;
            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])

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

    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;

        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 && coveredDistance > 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 rotationSpeed = speed;
            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 * 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);
                coveredDistance = progressRef.current;

                const t = (coveredDistance - accumulatedDistance) / segmentDistance;
                const position = start.clone().lerp(end, t);
                object.position.copy(position);
            }
        }

        if (progressRef.current >= totalDistance) {
            if (restRotation && objectRotation) {
                const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(objectRotation.x, objectRotation.y, objectRotation.z));
                object.quaternion.slerp(targetQuaternion, delta * 2);
                const angleDiff = object.quaternion.angleTo(targetQuaternion);
                if (angleDiff < 0.01) {
                    object.rotation.set(objectRotation.x, objectRotation.y, objectRotation.z);
                    setRestingRotation(false);
                }
                return;
            }
        }

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

    function firstFrame() {
        const droppedMaterial = agvDetail.currentLoad;
        startTime = performance.now();
        step(droppedMaterial);
    }

    function step(droppedMaterial: number) {
        if (isPausedRef.current) {
            if (!pauseTimeRef.current) {
                pauseTimeRef.current = performance.now();
            }
            requestAnimationFrame(() => step(droppedMaterial));
            return;
        }

        if (pauseTimeRef.current) {
            const pauseDuration = performance.now() - pauseTimeRef.current;
            startTime += pauseDuration;
            pauseTimeRef.current = null;
        }

        const elapsedTime = performance.now() - startTime;
        const unLoadDuration = agvDetail.point.action.unLoadDuration;
        fixedInterval = ((unLoadDuration / agvDetail.currentLoad) * (1000 / speed));

        if (elapsedTime >= fixedInterval) {
            let droppedMat = droppedMaterial - 1;
            decrementVehicleLoad(agvDetail.modelUuid, 1);
            if (droppedMat > 0) {
                startTime = performance.now();
                requestAnimationFrame(() => step(droppedMat));
            } else {
                return;
            }
        } else {
            requestAnimationFrame(() => step(droppedMaterial));
        }
    }

    return (
        <>
            {currentPath.length > 0 && (
                <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;