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 { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';

interface WorkerAnimatorProps {
    path: [number, number, number][];
    handleCallBack: () => void;
    reset: () => void;
    human: HumanStatus;
}

function OperatorAnimator({ path, handleCallBack, human, reset }: Readonly<WorkerAnimatorProps>) {
    const { humanStore, assetStore, productStore } = useSceneContext();
    const { getActionByUuid } = productStore();
    const { selectedProductStore } = useProductContext();
    const { selectedProduct } = selectedProductStore();
    const { getHumanById } = humanStore();
    const { setCurrentAnimation } = assetStore();
    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 action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
    const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.pickUpPoint?.rotation || [0, 0, 0]);
    const [restRotation, setRestingRotation] = useState<boolean>(true);
    const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
    const { scene } = useThree();

    useEffect(() => {
        if (!human.currentAction?.actionUuid) return;
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
        if (human.currentPhase === 'init-loadPoint' && path.length > 0) {
            setCurrentPath(path);
            setObjectRotation((action as HumanAction).pickUpPoint?.rotation ?? null);
        } else if (human.currentPhase === 'loadPoint-unloadPoint' && path.length > 0) {
            setObjectRotation((action as HumanAction)?.dropPoint?.rotation ?? null);
            setCurrentPath(path);
        } else if (human.currentPhase === 'unloadPoint-loadPoint' && path.length > 0) {
            setObjectRotation((action as HumanAction)?.pickUpPoint?.rotation ?? null);
            setCurrentPath(path);
        }
    }, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);

    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', human.modelUuid);
            const humanData = getHumanById(human.modelUuid);
            if (object && humanData) {
                object.position.set(humanData.position[0], humanData.position[1], humanData.position[2]);
                object.rotation.set(humanData.rotation[0], humanData.rotation[1], humanData.rotation[2]);
            }
        }
    }, [isReset, isPlaying]);

    const lastTimeRef = useRef(performance.now());

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

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

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

        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 targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(
                new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0))
            );
            const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
            targetQuaternion.multiply(y180);

            const angle = object.quaternion.angleTo(targetQuaternion);
            if (angle < 0.01) {
                object.quaternion.copy(targetQuaternion);
            } else {
                const step = rotationSpeed * delta * speed * human.speed;
                object.quaternion.rotateTowards(targetQuaternion, step);
            }

            const isAligned = angle < 0.01;

            if (isAligned) {
                progressRef.current += delta * (speed * human.speed);
                const t = (progressRef.current - accumulatedDistance) / segmentDistance;
                const position = start.clone().lerp(end, t);
                object.position.copy(position);
                setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
            } else {
                setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
            }
        }

        if (progressRef.current >= totalDistance) {
            if (restRotation && objectRotation) {
                const targetEuler = new THREE.Euler(0, objectRotation[1], 0);
                const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
                const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
                const targetQuaternion = baseQuaternion.multiply(y180);

                const angle = object.quaternion.angleTo(targetQuaternion);
                if (angle < 0.01) {
                    object.quaternion.copy(targetQuaternion);
                    setRestingRotation(false);
                } else {
                    const step = rotationSpeed * delta * speed * human.speed;
                    object.quaternion.rotateTowards(targetQuaternion, step);
                }

                setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
                return;
            }
        }

        if (progressRef.current >= totalDistance) {
            setRestingRotation(true);
            progressRef.current = 0;
            movingForward.current = !movingForward.current;
            setCurrentPath([]);
            handleCallBack();
        }
    });

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