import { useEffect, useRef, useCallback, useState } from 'react';
import * as THREE from 'three';
import { useFrame } from '@react-three/fiber';
import { useSceneContext } from '../../../../../scene/sceneContext';
import useModuleStore from '../../../../../../store/useModuleStore';
import { usePauseButtonStore, useAnimationPlaySpeed } from '../../../../../../store/usePlayButtonStore';

interface ModelAnimatorProps {
    asset: Asset;
    gltfScene: THREE.Object3D;
}

export function ModelAnimator({
    asset,
    gltfScene,
}: ModelAnimatorProps) {
    const mixerRef = useRef<THREE.AnimationMixer>();
    const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
    const [previousAnimation, setPreviousAnimation] = useState<string | null>(null);

    const blendFactor = useRef(0);
    const blendDuration = 0.5;

    const { speed } = useAnimationPlaySpeed();
    const { isPaused } = usePauseButtonStore();
    const { assetStore } = useSceneContext();
    const { activeModule } = useModuleStore();
    const { setAnimations, setAnimationComplete } = assetStore();

    const handleAnimationComplete = useCallback(() => {
        if (asset.animationState) {
            setAnimationComplete(asset.modelUuid, true);
        }
    }, [asset.animationState]);

    useEffect(() => {
        if (!gltfScene || !gltfScene.animations || gltfScene.animations.length === 0) return;

        mixerRef.current = new THREE.AnimationMixer(gltfScene);

        gltfScene.animations.forEach((clip: THREE.AnimationClip) => {
            const action = mixerRef.current!.clipAction(clip);
            actions.current[clip.name] = action;
        });

        const animationNames = gltfScene.animations.map(clip => clip.name);
        setAnimations(asset.modelUuid, animationNames);

        return () => {
            mixerRef.current?.stopAllAction();
            mixerRef.current = undefined;
        };
    }, [gltfScene]);

    useEffect(() => {
        if (!asset.animationState || !mixerRef.current) return;

        const { current, loopAnimation, isPlaying } = asset.animationState;
        const currentAction = actions.current[current];
        const previousAction = previousAnimation ? actions.current[previousAnimation] : null;

        if (isPlaying && currentAction && !isPaused) {
            blendFactor.current = 0;

            currentAction.reset();
            currentAction.setLoop(loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce, loopAnimation ? Infinity : 1);
            currentAction.clampWhenFinished = true;

            if (previousAction && previousAction !== currentAction) {
                previousAction.crossFadeTo(currentAction, blendDuration, false);
            }

            currentAction.play();
            mixerRef.current.addEventListener('finished', handleAnimationComplete);
            setPreviousAnimation(current);
        } else {
            Object.values(actions.current).forEach((action) => action.stop());
        }

        return () => {
            if (mixerRef.current) {
                mixerRef.current.removeEventListener('finished', handleAnimationComplete);
            }
        };
    }, [asset.animationState?.current, asset.animationState?.isCompleted, asset.animationState?.isPlaying, isPaused, activeModule]);

    useFrame((_, delta) => {
        if (mixerRef.current) {
            mixerRef.current.update(delta * (activeModule === 'simulation' ? speed : 1));
        }
    });

    return null;
}
