import * as THREE from 'three';
import { useState, useEffect, useRef, useMemo } from "react";
import { useLoader, useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three-stdlib";
import crate from "../../../../assets/gltf-glb/crate_box.glb";
import { useOrganization } from '../../../../store/store';
import { useControls } from 'leva';

type PathPoint = {
    position: THREE.Vector3;
    rotation: THREE.Quaternion;
    uuid: string;
};

type PathFlowProps = {
    path: PathPoint[];
    connections: { start: PathPoint; end: PathPoint }[];
};

export default function PathFlow({ path, connections }: PathFlowProps) {
    const { organization } = useOrganization();
    const [isPaused, setIsPaused] = useState(false);
    const [isStopped, setIsStopped] = useState(false);

    const { spawnInterval, speed, pauseResume, startStop } = useControls({
        spawnInterval: { value: 1000, min: 500, max: 5000, step: 100 },
        speed: { value: 2, min: 1, max: 20, step: 0.5 },
        pauseResume: { value: false, label: "Pause/Resume" },
        startStop: { value: false, label: "Start/Stop" },
    });

    const [meshes, setMeshes] = useState<{ id: number }[]>([]);
    const gltf = useLoader(GLTFLoader, crate);

    const meshIdRef = useRef(0);
    const lastSpawnTime = useRef(performance.now());
    const totalPausedTime = useRef(0);
    const pauseStartTime = useRef<number | null>(null);

    useEffect(() => {
        setIsPaused(pauseResume);
        setIsStopped(startStop);
    }, [pauseResume, startStop]);

    const removeMesh = (id: number) => {
        setMeshes((prev) => prev.filter((m) => m.id !== id));
    };

    useFrame(() => {
        if (isStopped || !path) return;

        const now = performance.now();

        if (isPaused) {
            if (pauseStartTime.current === null) {
                pauseStartTime.current = now;
            }
            return;
        }

        if (pauseStartTime.current !== null) {
            totalPausedTime.current += now - pauseStartTime.current;
            pauseStartTime.current = null;
        }

        const adjustedTime = now - totalPausedTime.current;

        if (adjustedTime - lastSpawnTime.current >= spawnInterval) {
            setMeshes((prev) => [...prev, { id: meshIdRef.current++ }]);
            lastSpawnTime.current = adjustedTime;
        }
    });

    return (
        <>
            {meshes.map((mesh) => (
                <MovingMesh
                    key={mesh.id}
                    meshId={mesh.id}
                    points={path}
                    speed={speed}
                    gltf={gltf}
                    removeMesh={removeMesh}
                    isPaused={isPaused}
                />
            ))}
        </>
    );
}

function MovingMesh({ meshId, points, speed, gltf, removeMesh, isPaused }: any) {
    const meshRef = useRef<any>();
    const startTime = useRef<number | null>(null); // Initialize as null
    const pausedTime = useRef(0);
    const pauseStartTime = useRef<number | null>(null);

    const distances = useMemo(() => {
        if (!points || points.length < 2) return [];
        return points.slice(1).map((point: any, i: number) => points[i].position.distanceTo(point.position));
    }, [points]);

    useFrame(() => {
        if (!points || points.length < 2) return;

        if (startTime.current === null && points.length > 0) {
            startTime.current = performance.now();
        }

        if (!meshRef.current) return;

        if (isPaused) {
            if (pauseStartTime.current === null) {
                pauseStartTime.current = performance.now();
            }
            return;
        }

        if (pauseStartTime.current !== null) {
            pausedTime.current += performance.now() - pauseStartTime.current;
            pauseStartTime.current = null;
        }

        if (startTime.current === null) return;

        const elapsed = performance.now() - startTime.current - pausedTime.current;

        const distanceTraveled = elapsed / 1000 * speed;

        let remainingDistance = distanceTraveled;
        let currentSegmentIndex = 0;

        while (currentSegmentIndex < distances.length && remainingDistance > distances[currentSegmentIndex]) {
            remainingDistance -= distances[currentSegmentIndex];
            currentSegmentIndex++;
        }

        if (currentSegmentIndex >= distances.length) {
            removeMesh(meshId);
            return;
        }

        const progress = remainingDistance / distances[currentSegmentIndex];
        const start = points[currentSegmentIndex].position;
        const end = points[currentSegmentIndex + 1].position;

        meshRef.current.position.lerpVectors(start, end, Math.min(progress, 1));

        const startRotation = points[currentSegmentIndex].rotation;
        const endRotation = points[currentSegmentIndex + 1].rotation;
        const interpolatedRotation = new THREE.Quaternion().slerpQuaternions(startRotation, endRotation, Math.min(progress, 1));

        meshRef.current.quaternion.copy(interpolatedRotation);
    });

    return (
        <>
            {points && points.length > 0 &&
                <mesh ref={meshRef}>
                    <primitive object={gltf.scene.clone()} />
                </mesh>
            }
        </>
    );
}