import React, { useCallback, useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { Canvas, useThree, useFrame, ThreeEvent } from '@react-three/fiber';
import { Line, OrbitControls } from '@react-three/drei';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useActiveTool, useSelectedPath } from '../../../../../store/builder/store';

interface InteractivePointsProps {
    agvUuid: string;
}


export default function InteractivePoints({ agvUuid }: InteractivePointsProps) {
    const { gl, scene, raycaster } = useThree();
    const [points, setPoints] = useState<[number, number, number][]>([]);
    const { isPaused } = usePauseButtonStore();
    const { isPlaying } = usePlayButtonStore();
    const { speed } = useAnimationPlaySpeed();
    const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // XZ plane
    const progressRef = useRef<number>(0);
    const { selectedPath } = useSelectedPath();
    const lastTimeRef = useRef(performance.now());
    const [isAnyDragging, setIsAnyDragging] = useState<string>("");
    const { activeTool } = useActiveTool();
    const hasClicked = useRef(false);

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

        const object = scene.getObjectByProperty('uuid', agvUuid);
        if (!object || points.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 < points.length - 1; i++) {
            const start = new THREE.Vector3(...points[i]);
            const end = new THREE.Vector3(...points[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(...points[index]);
            const end = new THREE.Vector3(...points[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 * 2) * 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 * 2);
                const t = (progressRef.current - accumulatedDistance) / segmentDistance;
                const position = start.clone().lerp(end, t);
                object.position.copy(position);
            }
        }
    });

    const downPosition = useRef<{ x: number; y: number } | null>(null);

    const handleMouseDown = useCallback((e: MouseEvent) => {
        hasClicked.current = false;
        downPosition.current = { x: e.clientX, y: e.clientY };
    }, []);

    const handleClick = useCallback((e: MouseEvent) => {
        if (hasClicked.current) return;
        hasClicked.current = true;

        if (
            !downPosition.current ||
            Math.abs(downPosition.current.x - e.clientX) > 2 ||
            Math.abs(downPosition.current.y - e.clientY) > 2
        ) {
            return;
        }

        const intersection = new THREE.Vector3();
        if (raycaster.ray.intersectPlane(plane.current, intersection) && activeTool !== "pen") {
            const pointArray = intersection.toArray() as [number, number, number];

            // ✅ Check if this point already exists
            const alreadyExists = points.some((p) =>
                Math.abs(p[0] - pointArray[0]) < 0.01 &&
                Math.abs(p[1] - pointArray[1]) < 0.01 &&
                Math.abs(p[2] - pointArray[2]) < 0.01
            );

            if (!alreadyExists) {
                console.log("pointArray: ", pointArray);
                setPoints((prev) => [...prev, pointArray]);
                console.log("points created");
            }
        }
    }, [activeTool, raycaster, points]);


    useEffect(() => {
        if (isPlaying) return;
        const domElement = gl.domElement;

        domElement.addEventListener('mousedown', handleMouseDown);
        domElement.addEventListener('mouseup', handleClick);


        return () => {
            domElement.removeEventListener('mousedown', handleMouseDown);
            domElement.removeEventListener('mouseup', handleClick);
            ;
        };
    }, [isPlaying, handleClick, handleMouseDown]);


    const updatePoint = (index: number, pos: THREE.Vector3) => {
        const updated = [...points];
        updated[index] = pos.toArray() as [number, number, number];
        setPoints(updated);
    };


    return (
        <>
            {selectedPath === "manual" &&
                <group>
                    {points.length > 0 && (
                        <group >
                            {points.map((pos, i) =>
                            (<React.Fragment key={i}>
                                <DraggableSphere
                                    key={i}
                                    index={i}
                                    position={new THREE.Vector3(...pos)}
                                    onMove={updatePoint}
                                    isAnyDragging={isAnyDragging}
                                    setIsAnyDragging={setIsAnyDragging}
                                />

                            </React.Fragment>)


                            )}
                        </group >
                    )
                    }
                    {points && (
                        points.map((pos, i) => {
                            if (i < points.length - 1) {
                                return (
                                    <DraggableLineSegment
                                        key={i}
                                        index={i}
                                        start={new THREE.Vector3(...points[i])}
                                        end={new THREE.Vector3(...points[i + 1])}
                                        updatePoints={(i0, p0, i1, p1) => {
                                            const updated = [...points];
                                            updated[i0] = p0.toArray() as [number, number, number];
                                            updated[i1] = p1.toArray() as [number, number, number];
                                            setPoints(updated);
                                        }}
                                        isAnyDragging={isAnyDragging}
                                        setIsAnyDragging={setIsAnyDragging}
                                    />
                                );
                            }
                            return null;
                        })
                    )}
                </group>
            }
        </>
    );
}



function DraggableSphere({
    index,
    position,
    onMove,
    isAnyDragging,
    setIsAnyDragging,
}: {
    index: number;
    position: THREE.Vector3;
    onMove: (index: number, pos: THREE.Vector3) => void;
    isAnyDragging: string;
    setIsAnyDragging: (val: string) => void;
}) {
    const meshRef = useRef<THREE.Mesh>(null);
    const { gl, controls, raycaster } = useThree();
    const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
    const { activeTool } = useActiveTool();

    const onPointerDown = (e: ThreeEvent<PointerEvent>) => {
        e.stopPropagation()
        if (activeTool !== 'pen') return;
        setIsAnyDragging("point");
        gl.domElement.style.cursor = 'grabbing';
        if (controls) (controls as any).enabled = false;
    };

    const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
        if (isAnyDragging !== "point" || activeTool !== 'pen') return;

        const intersect = new THREE.Vector3();
        if (raycaster.ray.intersectPlane(plane, intersect)) {
            meshRef.current!.position.copy(intersect);
            onMove(index, intersect);
        }
    };

    const onPointerUp = () => {
        if (activeTool !== 'pen') return;
        setIsAnyDragging("");
        gl.domElement.style.cursor = 'default';
        if (controls) (controls as any).enabled = true;
    };
    useEffect(() => {
        gl.domElement.addEventListener("pointerup", onPointerUp);
        return (() => {
            gl.domElement.removeEventListener("pointerup", onPointerUp);
        })
    }, [activeTool])

    return (
        <mesh
            ref={meshRef}
            position={position}
            onPointerDown={onPointerDown}
            onPointerMove={onPointerMove}
            onPointerUp={onPointerUp}
            onPointerMissed={onPointerUp}
        >
            <sphereGeometry args={[0.2, 16, 16]} />
            <meshStandardMaterial color="red" />
        </mesh>
    );
}

function DraggableLineSegment({
    index,
    start,
    end,
    updatePoints,
    isAnyDragging,
    setIsAnyDragging,
}: {
    index: number;
    start: THREE.Vector3;
    end: THREE.Vector3;
    updatePoints: (i0: number, p0: THREE.Vector3, i1: number, p1: THREE.Vector3) => void;
    isAnyDragging: string;
    setIsAnyDragging: (val: string) => void;
}) {
    const { gl, raycaster, controls } = useThree();
    const { activeTool } = useActiveTool();
    const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
    const dragStart = useRef<THREE.Vector3 | null>(null);

    const onPointerDown = () => {
        if (activeTool !== 'pen' || isAnyDragging) return;
        setIsAnyDragging("line");
        gl.domElement.style.cursor = 'grabbing';
        if (controls) (controls as any).enabled = false;
    };

    const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
        if (isAnyDragging !== "line" || activeTool !== 'pen') return;

        const intersect = new THREE.Vector3();
        if (raycaster.ray.intersectPlane(plane, intersect)) {
            if (!dragStart.current) dragStart.current = intersect.clone();
            const offset = new THREE.Vector3().subVectors(intersect, dragStart.current);
            const newStart = start.clone().add(offset);
            const newEnd = end.clone().add(offset);
            updatePoints(index, newStart, index + 1, newEnd);
        }
    };

    const onPointerUp = () => {
        if (activeTool !== 'pen') return;
        setIsAnyDragging("");
        dragStart.current = null;
        gl.domElement.style.cursor = 'default';
        if (controls) (controls as any).enabled = true;
    };
    useEffect(() => {
        gl.domElement.addEventListener("pointerup", onPointerUp);
        return (() => {
            gl.domElement.removeEventListener("pointerup", onPointerUp);
        })
    }, [activeTool])

    return (
        <Line
            points={[start, end]}
            color="blue"
            lineWidth={5}
            onPointerDown={onPointerDown}
            onPointerMove={onPointerMove}
            onPointerUp={onPointerUp}
            onPointerMissed={onPointerUp}
        />
    );
}