import { useEffect, useRef, useState } from "react";
import * as Types from "../../../../types/world/worldTypes";
import { Tube, useGLTF } from "@react-three/drei";
import * as THREE from "three";
import { useFrame, useThree } from "@react-three/fiber";
import { useSelectedEventSphere, useIsDragging, useIsRotating, } from "../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
import { DoubleSide, Group, Plane, Vector3 } from "three";

import startPoint from "../../../../assets/gltf-glb/ui/arrow_green.glb";
import startEnd from "../../../../assets/gltf-glb/ui/arrow_red.glb";
import { useSceneContext } from "../../../scene/sceneContext";
import { useProductContext } from "../../products/productContext";
import { useParams } from "react-router-dom";
import { useVersionContext } from "../../../builder/version/versionContext";

const VehicleUI = () => {
    const { scene: startScene } = useGLTF(startPoint) as any;
    const { scene: endScene } = useGLTF(startEnd) as any;
    const prevMousePos = useRef({ x: 0, y: 0 });
    const { selectedEventSphere } = useSelectedEventSphere();
    const { selectedProductStore } = useProductContext();
    const { vehicleStore, productStore } = useSceneContext();
    const { selectedProduct } = selectedProductStore();
    const { vehicles, getVehicleById } = vehicleStore();
    const { updateEvent } = productStore();
    const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0,]);

    const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0,]);
    const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0,]);

    const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0,]);
    const [steeringRotation, setSteeringRotation] = useState<[number, number, number]>([0, 0, 0]);

    const { isDragging, setIsDragging } = useIsDragging();
    const { isRotating, setIsRotating } = useIsRotating();
    const { raycaster } = useThree();
    const [point, setPoint] = useState<[number, number, number]>([0, 0, 0]);
    const plane = useRef(new Plane(new Vector3(0, 1, 0), 0));
    const [tubeRotation, setTubeRotation] = useState<boolean>(false);
    const tubeRef = useRef<Group>(null);
    const outerGroup = useRef<Group>(null);
    const state: Types.ThreeState = useThree();
    const controls: any = state.controls;
    const [selectedVehicleData, setSelectedVehicleData] = useState<{ position: [number, number, number]; rotation: [number, number, number]; }>({ position: [0, 0, 0], rotation: [0, 0, 0] });
    const CIRCLE_RADIUS = 0.8;
    const { selectedVersionStore } = useVersionContext();
    const { selectedVersion } = selectedVersionStore();
    const { projectId } = useParams();

    const updateBackend = (
        productName: string,
        productUuid: string,
        projectId: string,
        eventData: EventsSchema
    ) => {
        upsertProductOrEventApi({
            productName: productName,
            productUuid: productUuid,
            projectId: projectId,
            eventDatas: eventData,
            versionId: selectedVersion?.versionId || '',
        });
    };

    useEffect(() => {
        if (!selectedEventSphere) return;
        const selectedVehicle = getVehicleById(
            selectedEventSphere.userData.modelUuid
        );

        if (selectedVehicle) {
            setSelectedVehicleData({
                position: selectedVehicle.position,
                rotation: selectedVehicle.rotation,
            });
            setPoint(selectedVehicle.point.position);
        }

        setTimeout(() => {
            if (selectedVehicle?.point?.action) {
                const { pickUpPoint, unLoadPoint, steeringAngle } = selectedVehicle.point.action;

                if (pickUpPoint && outerGroup.current) {
                    const worldPos = new Vector3(
                        pickUpPoint.position.x,
                        pickUpPoint.position.y,
                        pickUpPoint.position.z
                    );
                    const localPosition = outerGroup.current.worldToLocal(worldPos.clone());

                    setStartPosition([
                        localPosition.x,
                        selectedVehicle.point.position[1],
                        localPosition.z,
                    ]);
                    setStartRotation([
                        pickUpPoint.rotation.x,
                        pickUpPoint.rotation.y,
                        pickUpPoint.rotation.z,
                    ]);
                } else {
                    setStartPosition([0, selectedVehicle.point.position[1] + 0.1, 1.5]);
                    setStartRotation([0, 0, 0]);
                }
                // end point
                if (unLoadPoint && outerGroup.current) {
                    const worldPos = new Vector3(
                        unLoadPoint.position.x,
                        unLoadPoint.position.y,
                        unLoadPoint.position.z
                    );
                    const localPosition = outerGroup.current.worldToLocal(worldPos);

                    setEndPosition([
                        localPosition.x,
                        selectedVehicle.point.position[1],
                        localPosition.z,
                    ]);
                    setEndRotation([
                        unLoadPoint.rotation.x,
                        unLoadPoint.rotation.y,
                        unLoadPoint.rotation.z,
                    ]);
                } else {
                    setEndPosition([0, selectedVehicle.point.position[1] + 0.1, -1.5]);
                    setEndRotation([0, 0, 0]);
                }
                setSteeringRotation([0, steeringAngle, 0]);
            }
        }, 10);
    }, [selectedEventSphere, outerGroup.current, vehicles]);

    const handlePointerDown = (
        e: any,
        state: "start" | "end",
        rotation: "start" | "end"
    ) => {
        if (e.object.name === "handle") {
            const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
            const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1;
            prevMousePos.current = { x: normalizedX, y: normalizedY };
            setIsRotating(rotation);
            if (controls) controls.enabled = false;
            setIsDragging(null);
        } else {
            setIsDragging(state);
            setIsRotating(null);
            if (controls) controls.enabled = false;
        }
    };

    const handlePointerUp = () => {
        controls.enabled = true;
        setIsDragging(null);
        setIsRotating(null);

        if (selectedEventSphere?.userData.modelUuid) {
            const updatedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid);

            let globalStartPosition = null;
            let globalEndPosition = null;

            if (outerGroup.current) {
                const worldPosStart = new Vector3(...startPosition);
                globalStartPosition = outerGroup.current.localToWorld(
                    worldPosStart.clone()
                );
                const worldPosEnd = new Vector3(...endPosition);
                globalEndPosition = outerGroup.current.localToWorld(
                    worldPosEnd.clone()
                );
            }
            if (updatedVehicle && globalEndPosition && globalStartPosition) {
                const event = updateEvent(
                    selectedProduct.productUuid,
                    selectedEventSphere.userData.modelUuid,
                    {
                        point: {
                            ...updatedVehicle.point,
                            action: {
                                ...updatedVehicle.point?.action,
                                pickUpPoint: {
                                    position: {
                                        x: globalStartPosition.x,
                                        y: 0,
                                        z: globalStartPosition.z,
                                    },
                                    rotation: { x: 0, y: startRotation[1], z: 0 },
                                },
                                unLoadPoint: {
                                    position: {
                                        x: globalEndPosition.x,
                                        y: 0,
                                        z: globalEndPosition.z,
                                    },
                                    rotation: { x: 0, y: endRotation[1], z: 0 },
                                },
                                steeringAngle: steeringRotation[1],
                            },
                        },
                    }
                );

                if (event) {
                    updateBackend(
                        selectedProduct.productName,
                        selectedProduct.productUuid,
                        projectId || '',
                        event
                    );
                }
            }
        }
    };

    useFrame(() => {
        if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return;
        const intersectPoint = new Vector3();
        const intersects = raycaster.ray.intersectPlane(
            plane.current,
            intersectPoint
        );
        if (!intersects) return;
        const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone());
        if (isDragging === "start") {
            setStartPosition([localPoint.x, point[1], localPoint.z]);
        } else if (isDragging === "end") {
            setEndPosition([localPoint.x, point[1], localPoint.z]);
        }
    });

    useEffect(() => {
        const handleGlobalPointerUp = () => {
            setIsDragging(null);
            setIsRotating(null);
            setTubeRotation(false);
            if (controls) controls.enabled = true;
            handlePointerUp();
        };

        if (isDragging || isRotating || tubeRotation) {
            window.addEventListener("pointerup", handleGlobalPointerUp);
        }

        return () => {
            window.removeEventListener("pointerup", handleGlobalPointerUp);
        };
    }, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, tubeRotation, steeringRotation, outerGroup.current, tubeRef.current,]);

    const prevSteeringY = useRef(0);

    useFrame((state) => {
        if (tubeRotation) {
            const currentPointerX = state.pointer.x;
            const deltaX = currentPointerX - prevMousePos.current.x;
            prevMousePos.current.x = currentPointerX;

            const marker = tubeRef.current;
            if (marker) {
                const rotationSpeed = 2;
                marker.rotation.y += deltaX * rotationSpeed;
                setSteeringRotation([
                    marker.rotation.x,
                    marker.rotation.y,
                    marker.rotation.z,
                ]);
            }
        } else {
            prevSteeringY.current = 0;
        }
    });

    useFrame((state) => {
        if (!isRotating) return;
        const currentPointerX = state.pointer.x;
        const deltaX = currentPointerX - prevMousePos.current.x;
        prevMousePos.current.x = currentPointerX;
        const rotationSpeed = 10;
        if (isRotating === "start") {
            setStartRotation([
                startRotation[0],
                startRotation[1] + deltaX * rotationSpeed,
                startRotation[2],
            ]);
        } else {
            setEndRotation([
                endRotation[0],
                endRotation[1] + deltaX * rotationSpeed,
                endRotation[2],
            ]);
        }
    });

    return selectedVehicleData ? (
        <group
            position={selectedVehicleData.position}
            ref={outerGroup}
        >
            <group
                position={[0, 0, 0]}
                ref={tubeRef}
                rotation={steeringRotation}
                onPointerDown={(e) => {
                    e.stopPropagation();
                    setTubeRotation(true);
                    prevMousePos.current.x = e.pointer.x;
                    controls.enabled = false;
                }}
                onPointerMissed={() => {
                    controls.enabled = true;
                    setTubeRotation(false);
                }}
                onPointerUp={() => {
                    controls.enabled = true;
                    setTubeRotation(false);
                }}
            >
                (
                <mesh
                    position={[0, point[1], 0]}
                    rotation={[-Math.PI / 2, 0, 0]}
                    name="steering"
                >
                    <ringGeometry args={[CIRCLE_RADIUS, CIRCLE_RADIUS + 0.2, 36]} />
                    <meshBasicMaterial color="yellow" side={DoubleSide} />
                </mesh>
                <mesh
                    position={[0, point[1], CIRCLE_RADIUS + 0.24]}
                    rotation={[Math.PI / 2, 0, 0]}
                >
                    <coneGeometry args={[0.1, 0.3, 12]} />
                    <meshBasicMaterial color="yellow" side={DoubleSide} />
                </mesh>
                )
            </group>

            {/* Start Marker */}
            <VehicleMarkerPrimitive
                name="startMarker"
                object={startScene}
                position={startPosition}
                rotation={startRotation}
                outerGroupRef={outerGroup}
                color="green"
                handlePointerDown={handlePointerDown}
                setIsDragging={setIsDragging}
                setIsRotating={setIsRotating}
            />

            {/* End Marker */}
            <VehicleMarkerPrimitive
                name="endMarker"
                object={endScene}
                position={endPosition}
                rotation={endRotation}
                outerGroupRef={outerGroup}
                color="orange"
                handlePointerDown={handlePointerDown}
                setIsDragging={setIsDragging}
                setIsRotating={setIsRotating}
            />
        </group>
    ) : null;
};

export default VehicleUI;

export const VehicleMarkerPrimitive = ({
    name,
    object,
    position,
    rotation,
    outerGroupRef,
    color,
    handlePointerDown,
    setIsDragging,
    setIsRotating,
}: {
    name: string;
    object: THREE.Object3D;
    position: [number, number, number];
    rotation: [number, number, number];
    outerGroupRef: React.RefObject<THREE.Group>;
    color: string;
    handlePointerDown: (e: any, type: "start" | "end", rotation: "start" | "end") => void;
    setIsDragging: (val: any) => void;
    setIsRotating: (val: any) => void;
}) => {
    const { scene, camera } = useThree();
    const markerRef = useRef<THREE.Group>(null);
    const [hitPoint, setHitPoint] = useState<THREE.Vector3 | null>(null);
    const [curve, setCurve] = useState<THREE.CatmullRomCurve3 | null>(null);

    useFrame(() => {
        if (!markerRef.current || !outerGroupRef.current) return;

        const worldPos = new THREE.Vector3();
        markerRef.current.getWorldPosition(worldPos);
        const localMarkerPos = outerGroupRef.current.worldToLocal(worldPos.clone());

        const rayOrigin = worldPos.clone();
        const direction = new THREE.Vector3(0, -1, 0);
        const raycaster = new THREE.Raycaster(rayOrigin, direction, 0.1, 1000);
        raycaster.camera = camera;
        const intersects = raycaster.intersectObjects(scene.children, true);

        const hit = intersects.find(i => i.object.name !== name);
        if (hit) {
            const localHit = outerGroupRef.current.worldToLocal(hit.point.clone());
            setHitPoint(localHit);

            const newCurve = new THREE.CatmullRomCurve3([
                localMarkerPos.clone(),
                localHit.clone(),
            ]);
            setCurve(newCurve);
        } else {
            setHitPoint(null);
            setCurve(null);
        }
    });

    return (
        <>
            <primitive
                name={name}
                ref={markerRef}
                object={object}
                position={position}
                rotation={rotation}
                onPointerDown={(e: any) =>
                    handlePointerDown(
                        e,
                        name === "startMarker" ? "start" : "end",
                        name === "startMarker" ? "start" : "end"
                    )
                }
                onPointerMissed={() => {
                    setIsDragging(null);
                    setIsRotating(null);
                }}
            />

            {hitPoint && (
                <>
                    <mesh
                        name={`${name}-torus`}
                        position={hitPoint}
                        rotation={[-Math.PI / 2, 0, 0]}
                    >
                        <torusGeometry args={[0.15, 0.02, 3, 32]} />
                        <meshBasicMaterial color={color} depthWrite={false} />
                    </mesh>

                    {curve && (
                        <Tube args={[curve, 20, 0.01, 8, true]}>
                            <meshBasicMaterial color={color} depthWrite={false} />
                        </Tube>
                    )}
                </>
            )}
        </>
    );
};