import { useRef, useMemo, useState, useCallback } from "react";
import { useThree, useFrame } from "@react-three/fiber";
import * as THREE from "three";
import { useSceneContext } from "../../../sceneContext";

type ColliderArrowProps = {
    arrowId: string;
    colliderId: string;
    startPosition: [number, number, number];
    endPosition: [number, number, number];
    colliderRotation: [number, number, number];
    thickness?: number;
    depth?: number;
    color?: string;
};

export function ColliderArrow({
    arrowId,
    colliderId,
    startPosition,
    endPosition,
    colliderRotation,
    thickness = 0.05,
    depth = 0.01,
    color = "green",
}: ColliderArrowProps) {
    const groupRef = useRef<THREE.Group>(null);
    const { raycaster, pointer, controls } = useThree();
    const { colliderStore } = useSceneContext();
    const { updateArrow, selectedArrowId, clearSelectedArrow } = colliderStore();
    const [dragging, setDragging] = useState(false);
    const { dir, length } = useMemo(() => {
        const start = new THREE.Vector3(...startPosition);
        const end = new THREE.Vector3(...endPosition);
        const d = new THREE.Vector3().subVectors(end, start);
        return { dir: d.clone().normalize(), length: d.length() };
    }, [startPosition, endPosition]);

    const arrowShape = useMemo(() => {
        const shaftWidth = thickness;
        const headLength = Math.min(length * 0.3, 0.4);
        const headWidth = thickness * 3;

        const shape = new THREE.Shape();
        shape.moveTo(0, -shaftWidth / 2);
        shape.lineTo(length - headLength, -shaftWidth / 2);
        shape.lineTo(length - headLength, -headWidth / 2);
        shape.lineTo(length, 0);
        shape.lineTo(length - headLength, headWidth / 2);
        shape.lineTo(length - headLength, shaftWidth / 2);
        shape.lineTo(0, shaftWidth / 2);
        shape.closePath();

        return shape;
    }, [length, thickness]);

    const extrudeSettings = useMemo(() => ({ depth, bevelEnabled: false, }), [depth]);
    const geometry = useMemo(() => new THREE.ExtrudeGeometry(arrowShape, extrudeSettings), [arrowShape, extrudeSettings]);

    const quaternion = useMemo(() => {
        const q = new THREE.Quaternion();
        q.setFromUnitVectors(new THREE.Vector3(1, 0, 0), dir);
        const colliderQuat = new THREE.Quaternion().setFromEuler(
            new THREE.Euler().fromArray(colliderRotation)
        );

        return colliderQuat.multiply(q);
    }, [dir, colliderRotation]);

    const handlePointerDown = useCallback((e: any) => {
        e.stopPropagation();
        setDragging(true);
        (controls as any).enabled = false; // Disable controls while dragging
    }, []);

    const handlePointerUp = useCallback((e: any) => {
        e.stopPropagation();
        clearSelectedArrow()
        setDragging(false);
        (controls as any).enabled = true;
    }, []);

    useFrame(({ camera }) => {
        if (dragging) {
            if (selectedArrowId) {
                const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), -startPosition[1]);
                raycaster.setFromCamera(pointer, camera);
                const hit = new THREE.Vector3();
                if (raycaster.ray.intersectPlane(plane, hit)) {
                    updateArrow(colliderId, arrowId, {
                        position: [hit.x, startPosition[1], hit.z],
                    });
                }
            }
        }
    });

    return (
        <group ref={groupRef} quaternion={quaternion}>

            <mesh geometry={geometry} rotation={[Math.PI / 2, 0, 0]}>
                <meshStandardMaterial color={color} />
            </mesh>

            <mesh
                position={[length, 0, 0]}
                onPointerDown={handlePointerDown}
                onPointerUp={handlePointerUp}
            >
                <sphereGeometry args={[0.15, 16, 16]} />
                <meshBasicMaterial transparent opacity={0.2} color="yellow" />
            </mesh>
        </group>
    );
}
