import { useEffect, useState } from 'react';
import * as THREE from 'three';
import { useThree, useFrame } from '@react-three/fiber';
import { Line, TransformControls } from '@react-three/drei';
import { useDrawMaterialPath } from '../../../../store/store';

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

type PathCreatorProps = {
    simulationStates: PathPoint[][];
    setSimulationStates: React.Dispatch<React.SetStateAction<PathPoint[][]>>;
    connections: { start: PathPoint; end: PathPoint }[];
    setConnections: React.Dispatch<React.SetStateAction<{ start: PathPoint; end: PathPoint }[]>>
};

const PathCreator = ({ simulationStates, setSimulationStates, connections, setConnections }: PathCreatorProps) => {
    const { camera, scene, raycaster, pointer, gl } = useThree();
    const { drawMaterialPath } = useDrawMaterialPath();

    const [currentPath, setCurrentPath] = useState<{ position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string }[]>([]);
    const [temporaryPoint, setTemporaryPoint] = useState<THREE.Vector3 | null>(null);
    const [selectedPoint, setSelectedPoint] = useState<{ position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string } | null>(null);
    const [selectedConnectionPoint, setSelectedConnectionPoint] = useState<{ point: PathPoint; pathIndex: number } | null>(null);
    const [previewConnection, setPreviewConnection] = useState<{ start: PathPoint; end?: THREE.Vector3 } | null>(null);
    const [transformMode, setTransformMode] = useState<'translate' | 'rotate'>('translate');

    useEffect(() => {
        const handleKeyDown = (event: KeyboardEvent) => {
            if (selectedPoint) {
                if (event.key === 'g') {
                    setTransformMode('translate');
                } else if (event.key === 'r') {
                    setTransformMode('rotate');
                }
            }
        };

        document.addEventListener('keydown', handleKeyDown);

        return () => {
            document.removeEventListener('keydown', handleKeyDown);
        };
    }, [selectedPoint]);

    useEffect(() => {
        const canvasElement = gl.domElement;

        let drag = false;
        let MouseDown = false;

        const onMouseDown = () => {
            MouseDown = true;
            drag = false;
        };

        const onMouseUp = () => {
            MouseDown = false;
        };

        const onMouseMove = () => {
            if (MouseDown) {
                drag = true;
            }
        };

        const onContextMenu = (e: any) => {
            e.preventDefault();
            if (drag || e.button === 0) return;
            if (currentPath.length > 1) {
                setSimulationStates((prevPaths) => [...prevPaths, currentPath]);
            }
            setCurrentPath([]);
            setTemporaryPoint(null);
            setPreviewConnection(null);
            setSelectedConnectionPoint(null);
        };

        const onMouseClick = (evt: any) => {
            if (drag || evt.button !== 0) return;

            evt.preventDefault();
            raycaster.setFromCamera(pointer, camera);

            let intersects = raycaster.intersectObjects(scene.children, true);

            if (intersects.some((intersect) => intersect.object.name.includes("path-point"))) {
                intersects = [];
            } else {
                intersects = intersects.filter(
                    (intersect) =>
                        !intersect.object.name.includes("Roof") &&
                        !intersect.object.name.includes("agv-collider") &&
                        !intersect.object.name.includes("MeasurementReference") &&
                        !intersect.object.userData.isPathObject &&
                        !(intersect.object.type === "GridHelper")
                );
            }

            if (intersects.length > 0 && selectedPoint === null) {
                let point = intersects[0].point;
                if (point.y < 0.05) {
                    point = new THREE.Vector3(point.x, 0.05, point.z);
                }
                const newPoint = {
                    position: point,
                    rotation: new THREE.Quaternion(),
                    uuid: THREE.MathUtils.generateUUID(),
                };
                setCurrentPath((prevPath) => [...prevPath, newPoint]);
                setTemporaryPoint(null);
            } else {
                setSelectedPoint(null);
            }
        };

        if (drawMaterialPath) {
            canvasElement.addEventListener("mousedown", onMouseDown);
            canvasElement.addEventListener("mouseup", onMouseUp);
            canvasElement.addEventListener("mousemove", onMouseMove);
            canvasElement.addEventListener("click", onMouseClick);
            canvasElement.addEventListener("contextmenu", onContextMenu);
        } else {
            if (currentPath.length > 1) {
                setSimulationStates((prevPaths) => [...prevPaths, currentPath]);
            }
            setCurrentPath([]);
            setTemporaryPoint(null);
        }

        return () => {
            canvasElement.removeEventListener("mousedown", onMouseDown);
            canvasElement.removeEventListener("mouseup", onMouseUp);
            canvasElement.removeEventListener("mousemove", onMouseMove);
            canvasElement.removeEventListener("click", onMouseClick);
            canvasElement.removeEventListener("contextmenu", onContextMenu);
        };
    }, [camera, scene, raycaster, currentPath, drawMaterialPath, selectedPoint]);

    useFrame(() => {
        if (drawMaterialPath && currentPath.length > 0) {
            raycaster.setFromCamera(pointer, camera);

            const intersects = raycaster.intersectObjects(scene.children, true).filter(
                (intersect) =>
                    !intersect.object.name.includes("Roof") &&
                    !intersect.object.name.includes("agv-collider") &&
                    !intersect.object.name.includes("MeasurementReference") &&
                    !intersect.object.userData.isPathObject &&
                    !(intersect.object.type === "GridHelper")
            );

            if (intersects.length > 0) {
                let point = intersects[0].point;
                if (point.y < 0.05) {
                    point = new THREE.Vector3(point.x, 0.05, point.z);
                }
                setTemporaryPoint(point);
            } else {
                setTemporaryPoint(null);
            }
        } else {
            setTemporaryPoint(null);
        }
    });

    const handlePointClick = (point: { position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string }) => {
        if (currentPath.length === 0 && drawMaterialPath) {
            setSelectedPoint(point);
        } else {
            setSelectedPoint(null);
        }
    };

    const handleTransform = (e: any) => {
        if (selectedPoint) {
            const updatedPosition = e.target.object.position.clone();
            const updatedRotation = e.target.object.quaternion.clone();
            const updatedPaths = simulationStates.map((path) =>
                path.map((p) =>
                    p.uuid === selectedPoint.uuid ? { ...p, position: updatedPosition, rotation: updatedRotation } : p
                )
            );
            setSimulationStates(updatedPaths);
        }
    };


    const meshContext = (uuid: string) => {
        const pathIndex = simulationStates.findIndex(path => path.some(point => point.uuid === uuid));
        if (pathIndex === -1) return;

        const clickedPoint = simulationStates[pathIndex].find(point => point.uuid === uuid);
        if (!clickedPoint) return;

        const isStart = simulationStates[pathIndex][0].uuid === uuid;
        const isEnd = simulationStates[pathIndex][simulationStates[pathIndex].length - 1].uuid === uuid;

        if (pathIndex === 0 && isStart) {
            console.log("The first-ever point is not connectable.");
            setSelectedConnectionPoint(null);
            setPreviewConnection(null);
            return;
        }

        if (!isStart && !isEnd) {
            console.log("Selected point is not a valid connection point (not start or end)");
            setSelectedConnectionPoint(null);
            setPreviewConnection(null);
            return;
        }

        if (connections.some(conn => conn.start.uuid === uuid || conn.end.uuid === uuid)) {
            console.log("The selected point is already connected.");
            setSelectedConnectionPoint(null);
            setPreviewConnection(null);
            return;
        }

        if (!selectedConnectionPoint) {
            setSelectedConnectionPoint({ point: clickedPoint, pathIndex });
            setPreviewConnection({ start: clickedPoint });
            console.log("First point selected for connection:", clickedPoint);
            return;
        }

        if (selectedConnectionPoint.pathIndex === pathIndex) {
            console.log("Cannot connect points within the same path.");
            setSelectedConnectionPoint(null);
            setPreviewConnection(null);
            return;
        }

        if (connections.some(conn => conn.start.uuid === clickedPoint.uuid || conn.end.uuid === clickedPoint.uuid)) {
            console.log("The target point is already connected.");
            setSelectedConnectionPoint(null);
            setPreviewConnection(null);
            return;
        }

        setConnections(prevConnections => [
            ...prevConnections,
            { start: selectedConnectionPoint.point, end: clickedPoint },
        ]);


        setSelectedConnectionPoint(null);
        setPreviewConnection(null);
    };

    useEffect(() => {
        if (!selectedConnectionPoint) {
            setPreviewConnection(null);
        }
    }, [selectedConnectionPoint, connections]);

    useFrame(() => {
        if (selectedConnectionPoint) {
            raycaster.setFromCamera(pointer, camera);

            const intersects = raycaster.intersectObjects(scene.children, true).filter(
                (intersect) =>
                    !intersect.object.name.includes("Roof") &&
                    !intersect.object.name.includes("agv-collider") &&
                    !intersect.object.name.includes("MeasurementReference") &&
                    !intersect.object.userData.isPathObject &&
                    !(intersect.object.type === "GridHelper")
            );

            if (intersects.length > 0) {
                let point = intersects[0].point;
                if (point.y < 0.05) {
                    point = new THREE.Vector3(point.x, 0.05, point.z);
                }
                setPreviewConnection({ start: selectedConnectionPoint.point, end: point });
            } else {
                setPreviewConnection(null);
            }
        }
    });

    return (
        <>
            <group name='pathObjects'>
                {/* Render finalized simulationStates */}
                {simulationStates.map((path, pathIndex) => (
                    <group key={`path-line-${pathIndex}`}>
                        <Line
                            name={`path-line-${pathIndex}`}
                            points={path.map((point) => point.position)}
                            color="yellow"
                            lineWidth={5}
                            userData={{ isPathObject: true }}
                        />
                    </group>
                ))}

                {/* Render finalized points */}
                {simulationStates.map((path) =>
                    path.map((point) => (
                        <mesh
                            key={`path-point-${point.uuid}`}
                            name={`path-point-${point.uuid}`}
                            uuid={`${point.uuid}`}
                            position={point.position}
                            userData={{ isPathObject: true }}
                            onClick={() => handlePointClick(point)}
                            onPointerMissed={() => { setSelectedPoint(null) }}
                            onContextMenu={() => { meshContext(point.uuid); }}
                        >
                            <sphereGeometry args={[0.1, 16, 16]} />
                            <meshStandardMaterial color="blue" wireframe />
                        </mesh>
                    ))
                )}

                {connections.map((conn, index) => (
                    <Line
                        key={`connection-${index}`}
                        points={[conn.start.position, conn.end.position]}
                        color="white"
                        dashed
                        lineWidth={4}
                        dashSize={1}
                        dashScale={15}
                        userData={{ isPathObject: true }}
                    />
                ))}

            </group>

            {/* Render current path */}
            {currentPath.length > 1 && (
                <group>
                    <Line
                        points={currentPath.map((point) => point.position)}
                        color="red"
                        lineWidth={5}
                        userData={{ isPathObject: true }}
                    />
                </group>
            )}

            {/* Render current path points */}
            {currentPath.map((point) => (
                <mesh
                    key={`current-point-${point.uuid}`}
                    position={point.position}
                    userData={{ isPathObject: true }}
                >
                    <sphereGeometry args={[0.1, 16, 16]} />
                    <meshStandardMaterial color="red" />
                </mesh>
            ))}

            {/* Render temporary indicator line */}
            {temporaryPoint && currentPath.length > 0 && (
                <group>
                    <Line
                        points={[currentPath[currentPath.length - 1].position, temporaryPoint]}
                        color="white"
                        lineWidth={2}
                        userData={{ isPathObject: true }}
                    />
                </group>
            )}

            {/* Render dashed preview connection */}
            {previewConnection && previewConnection.end && (
                <Line
                    points={[previewConnection.start.position, previewConnection.end]}
                    color="white"
                    dashed
                    lineWidth={4}
                    dashSize={1}
                    dashScale={15}
                    userData={{ isPathObject: true }}
                />
            )}

            {/* Render temporary point */}
            {temporaryPoint && (
                <mesh
                    position={temporaryPoint}
                    userData={{ isPathObject: true }}
                >
                    <sphereGeometry args={[0.1, 16, 16]} />
                    <meshStandardMaterial color="white" />
                </mesh>
            )}

            {/* Attach TransformControls to the selected point */}
            {selectedPoint && (
                <TransformControls
                    object={scene.getObjectByProperty('uuid', selectedPoint.uuid)}
                    mode={transformMode}
                    onObjectChange={handleTransform}
                />
            )}
        </>
    );
};

export default PathCreator;