import { useFrame, useThree } from '@react-three/fiber';
import React, { useEffect, useState } from 'react';
import * as THREE from 'three';
import { QuadraticBezierLine } from '@react-three/drei';
import { useConnections, useIsConnecting, useSimulationPaths } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore';

function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
    const { activeModule } = useModuleStore();
    const { gl, raycaster, scene, pointer, camera } = useThree();
    const { connections, setConnections, addConnection } = useConnections();
    const { isConnecting, setIsConnecting } = useIsConnecting();
    const { simulationPaths, setSimulationPaths } = useSimulationPaths();

    const [firstSelected, setFirstSelected] = useState<{ pathUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null);
    const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3, end: THREE.Vector3, mid: THREE.Vector3 } | null>(null);
    const [hoveredSphere, setHoveredSphere] = useState<{ sphereUUID: string, position: THREE.Vector3 } | null>(null);
    const [helperlineColor, setHelperLineColor] = useState<string>('red');

    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 = (evt: MouseEvent) => {
            evt.preventDefault();
            if (drag || evt.button === 0) return;

            raycaster.setFromCamera(pointer, camera);
            const intersects = raycaster.intersectObjects(pathsGroupRef.current.children, true);

            if (intersects.length > 0) {
                const intersected = intersects[0].object;

                if (intersected.name.includes("action-sphere")) {
                    const pathUUID = intersected.userData.path.modeluuid;
                    const sphereUUID = intersected.uuid;
                    const worldPosition = new THREE.Vector3();
                    intersected.getWorldPosition(worldPosition);

                    const isStartOrEnd = intersected.userData.path.points.length > 0 && (
                        sphereUUID === intersected.userData.path.points[0].uuid ||
                        sphereUUID === intersected.userData.path.points[intersected.userData.path.points.length - 1].uuid
                    );

                    if (pathUUID) {
                        const isAlreadyConnected = connections.some((connection) =>
                            connection.fromUUID === sphereUUID ||
                            connection.toConnections.some(conn => conn.toUUID === sphereUUID)
                        );

                        if (isAlreadyConnected) {
                            console.log("Sphere is already connected. Ignoring.");
                            return;
                        }

                        if (!firstSelected) {
                            setFirstSelected({
                                pathUUID,
                                sphereUUID,
                                position: worldPosition,
                                isCorner: isStartOrEnd
                            });
                            setIsConnecting(true);
                        } else {
                            if (firstSelected.sphereUUID === sphereUUID) return;

                            if (firstSelected.pathUUID === pathUUID) {
                                console.log("Cannot connect spheres on the same path.");
                                return;
                            }
                            if (!firstSelected.isCorner && !isStartOrEnd) {
                                console.log("At least one of the selected spheres must be a start or end point.");
                                return;
                            }

                            addConnection({
                                fromPathUUID: firstSelected.pathUUID,
                                fromUUID: firstSelected.sphereUUID,
                                toConnections: [{ toPathUUID: pathUUID, toUUID: sphereUUID }]
                            });

                            setFirstSelected(null);
                            setCurrentLine(null);
                            setIsConnecting(false);
                            setHoveredSphere(null);
                        }
                    }
                }
            } else {
                setFirstSelected(null);
                setCurrentLine(null);
                setIsConnecting(false);
                setHoveredSphere(null);
            }
        };

        if (activeModule === 'simulation') {
            canvasElement.addEventListener("mousedown", onMouseDown);
            canvasElement.addEventListener("mouseup", onMouseUp);
            canvasElement.addEventListener("mousemove", onMouseMove);
            canvasElement.addEventListener("contextmenu", onContextMenu);
        } else {
            setFirstSelected(null);
            setCurrentLine(null);
            setIsConnecting(false);
            setHoveredSphere(null);
        }

        return () => {
            canvasElement.removeEventListener("mousedown", onMouseDown);
            canvasElement.removeEventListener("mouseup", onMouseUp);
            canvasElement.removeEventListener("mousemove", onMouseMove);
            canvasElement.removeEventListener("contextmenu", onContextMenu);
        };
    }, [camera, scene, raycaster, firstSelected, connections]);

    useFrame(() => {
        if (firstSelected) {
            raycaster.setFromCamera(pointer, camera);
            const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) =>
                !intersect.object.name.includes("Roof") &&
                !intersect.object.name.includes("MeasurementReference") &&
                !intersect.object.userData.isPathObject &&
                !(intersect.object.type === "GridHelper")
            );

            let point: THREE.Vector3 | null = null;
            let snappedSphere: { sphereUUID: string, position: THREE.Vector3, pathUUID: string, isCorner: boolean } | null = null;
            let isInvalidConnection = false;

            if (intersects.length > 0) {
                point = intersects[0].point;

                if (point.y < 0.05) {
                    point = new THREE.Vector3(point.x, 0.05, point.z);
                }
            }

            const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) =>
                obj.object.name.includes("action-sphere")
            );

            if (sphereIntersects.length > 0) {
                const sphere = sphereIntersects[0].object;
                const sphereUUID = sphere.uuid;
                const spherePosition = new THREE.Vector3();
                sphere.getWorldPosition(spherePosition);
                const pathUUID = sphere.userData.path.modeluuid;

                const isStartOrEnd = sphere.userData.path.points.length > 0 && (
                    sphereUUID === sphere.userData.path.points[0].uuid ||
                    sphereUUID === sphere.userData.path.points[sphere.userData.path.points.length - 1].uuid
                );

                const isAlreadyConnected = connections.some((connection) =>
                    connection.fromUUID === sphereUUID ||
                    connection.toConnections.some(conn => conn.toUUID === sphereUUID)
                );

                if (
                    !isAlreadyConnected &&
                    firstSelected.sphereUUID !== sphereUUID &&
                    firstSelected.pathUUID !== pathUUID &&
                    (firstSelected.isCorner || isStartOrEnd)
                ) {
                    snappedSphere = { sphereUUID, position: spherePosition, pathUUID, isCorner: isStartOrEnd };
                } else {
                    isInvalidConnection = true;
                }
            }

            if (snappedSphere) {
                setHoveredSphere(snappedSphere);
                point = snappedSphere.position;
            } else {
                setHoveredSphere(null);
            }

            if (point) {
                const distance = firstSelected.position.distanceTo(point);
                const heightFactor = Math.max(0.5, distance * 0.2);
                const midPoint = new THREE.Vector3(
                    (firstSelected.position.x + point.x) / 2,
                    Math.max(firstSelected.position.y, point.y) + heightFactor,
                    (firstSelected.position.z + point.z) / 2
                );

                setCurrentLine({
                    start: firstSelected.position,
                    end: point,
                    mid: midPoint,
                });

                setIsConnecting(true);

                if (sphereIntersects.length > 0) {
                    setHelperLineColor(isInvalidConnection ? 'red' : '#6cf542');
                } else {
                    setHelperLineColor('yellow');
                }
            } else {
                setCurrentLine(null);
                setIsConnecting(false);
            }
        } else {
            setCurrentLine(null);
            setIsConnecting(false);
        }
    });


    useEffect(() => {
        console.log('connections: ', connections);
    }, [connections]);

    return (
        <>
            {connections.map((connection, index) => {
                if (!pathsGroupRef.current) return;
                const fromSphere = pathsGroupRef.current.getObjectByProperty('uuid', connection.fromUUID);
                const toSphere = pathsGroupRef.current.getObjectByProperty('uuid', connection.toConnections[0].toUUID);

                if (fromSphere && toSphere) {
                    const fromWorldPosition = new THREE.Vector3();
                    const toWorldPosition = new THREE.Vector3();
                    fromSphere.getWorldPosition(fromWorldPosition);
                    toSphere.getWorldPosition(toWorldPosition);

                    const distance = fromWorldPosition.distanceTo(toWorldPosition);
                    const heightFactor = Math.max(0.5, distance * 0.2);

                    const midPoint = new THREE.Vector3(
                        (fromWorldPosition.x + toWorldPosition.x) / 2,
                        Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
                        (fromWorldPosition.z + toWorldPosition.z) / 2
                    );

                    return (
                        <QuadraticBezierLine
                            key={index}
                            start={fromWorldPosition.toArray()}
                            end={toWorldPosition.toArray()}
                            mid={midPoint.toArray()}
                            color="white"
                            lineWidth={4}
                            dashed
                            dashSize={1}
                            dashScale={20}
                            userData={connection}
                        />
                    );
                }
                return null;
            })}

            {currentLine && (
                <QuadraticBezierLine
                    start={currentLine.start.toArray()}
                    end={currentLine.end.toArray()}
                    mid={currentLine.mid.toArray()}
                    color={helperlineColor}
                    lineWidth={4}
                    dashed
                    dashSize={1}
                    dashScale={20}
                />
            )}
        </>
    );
}

export default PathConnector;