import { useFrame, useThree } from '@react-three/fiber';
import React, { useEffect, useState } from 'react';
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { QuadraticBezierLine } from '@react-three/drei';
import { useIsConnecting, useSimulationStates } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore';
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';

function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
    const { activeModule } = useModuleStore();
    const { gl, raycaster, scene, pointer, camera } = useThree();
    const { setIsConnecting } = useIsConnecting();
    const { simulationStates, setSimulationStates } = useSimulationStates();
    const { isPlaying } = usePlayButtonStore();

    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 [helperlineColor, setHelperLineColor] = useState<string>('red');

    const updatePathConnections = (
        fromPathUUID: string,
        fromPointUUID: string,
        toPathUUID: string,
        toPointUUID: string
    ) => {
        const updatedPaths = simulationStates.map(path => {
            if (path.type === 'Conveyor') {
                if (path.modeluuid === fromPathUUID) {
                    return {
                        ...path,
                        points: path.points.map(point => {
                            if (point.uuid === fromPointUUID) {
                                const newTarget = {
                                    pathUUID: toPathUUID,
                                    pointUUID: toPointUUID
                                };
                                const existingTargets = point.connections.targets || [];

                                if (!existingTargets.some(target =>
                                    target.pathUUID === newTarget.pathUUID &&
                                    target.pointUUID === newTarget.pointUUID
                                )) {
                                    return {
                                        ...point,
                                        connections: {
                                            ...point.connections,
                                            targets: [...existingTargets, newTarget]
                                        }
                                    };
                                }
                            }
                            return point;
                        })
                    };
                }
                else if (path.modeluuid === toPathUUID) {
                    return {
                        ...path,
                        points: path.points.map(point => {
                            if (point.uuid === toPointUUID) {
                                const reverseTarget = {
                                    pathUUID: fromPathUUID,
                                    pointUUID: fromPointUUID
                                };
                                const existingTargets = point.connections.targets || [];

                                if (!existingTargets.some(target =>
                                    target.pathUUID === reverseTarget.pathUUID &&
                                    target.pointUUID === reverseTarget.pointUUID
                                )) {
                                    return {
                                        ...point,
                                        connections: {
                                            ...point.connections,
                                            targets: [...existingTargets, reverseTarget]
                                        }
                                    };
                                }
                            }
                            return point;
                        })
                    };
                }
            }
            // In the updatePathConnections function, modify the Vehicle handling section:
            else if (path.type === 'Vehicle') {
                // Handle outgoing connections from Vehicle
                if (path.modeluuid === fromPathUUID && path.points.uuid === fromPointUUID) {
                    const newTarget = {
                        pathUUID: toPathUUID,
                        pointUUID: toPointUUID
                    };
                    const existingTargets = path.points.connections.targets || [];

                    // Check if target is a Conveyor
                    const toPath = simulationStates.find(p => p.modeluuid === toPathUUID);
                    if (toPath?.type !== 'Conveyor') {
                        console.log("Vehicle can only connect to Conveyors");
                        return path;
                    }

                    // Check if already has a connection
                    if (existingTargets.length >= 1) {
                        console.log("Vehicle can have only one connection");
                        return path;
                    }

                    if (!existingTargets.some(target =>
                        target.pathUUID === newTarget.pathUUID &&
                        target.pointUUID === newTarget.pointUUID
                    )) {
                        return {
                            ...path,
                            points: {
                                ...path.points,
                                connections: {
                                    ...path.points.connections,
                                    targets: [...existingTargets, newTarget]
                                }
                            }
                        };
                    }
                }
                // Handle incoming connections to Vehicle
                else if (path.modeluuid === toPathUUID && path.points.uuid === toPointUUID) {
                    const reverseTarget = {
                        pathUUID: fromPathUUID,
                        pointUUID: fromPointUUID
                    };
                    const existingTargets = path.points.connections.targets || [];

                    // Check if source is a Conveyor
                    const fromPath = simulationStates.find(p => p.modeluuid === fromPathUUID);
                    if (fromPath?.type !== 'Conveyor') {
                        console.log("Vehicle can only connect to Conveyors");
                        return path;
                    }

                    // Check if already has a connection
                    if (existingTargets.length >= 1) {
                        console.log("Vehicle can have only one connection");
                        return path;
                    }

                    if (!existingTargets.some(target =>
                        target.pathUUID === reverseTarget.pathUUID &&
                        target.pointUUID === reverseTarget.pointUUID
                    )) {
                        return {
                            ...path,
                            points: {
                                ...path.points,
                                connections: {
                                    ...path.points.connections,
                                    targets: [...existingTargets, reverseTarget]
                                }
                            }
                        };
                    }
                }
                return path;
            }
            return path;
        });

        setSimulationStates(updatedPaths);
    };

    const handleAddConnection = (fromPathUUID: string, fromUUID: string, toPathUUID: string, toUUID: string) => {
        updatePathConnections(fromPathUUID, fromUUID, toPathUUID, toUUID);
        setFirstSelected(null);
        setCurrentLine(null);
        setIsConnecting(false);
    };

    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("events-sphere")) {
                    const pathUUID = intersected.userData.path.modeluuid;
                    const sphereUUID = intersected.uuid;
                    const worldPosition = new THREE.Vector3();
                    intersected.getWorldPosition(worldPosition);

                    let isStartOrEnd = false;

                    if (intersected.userData.path.points && intersected.userData.path.points.length > 1) {
                        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
                        );
                    } else if (intersected.userData.path.points) {
                        isStartOrEnd = sphereUUID === intersected.userData.path.points.uuid;
                    }

                    if (pathUUID) {
                        const firstPath = simulationStates.find(p => p.modeluuid === firstSelected?.pathUUID);
                        const secondPath = simulationStates.find(p => p.modeluuid === pathUUID);

                        // Prevent vehicle-to-vehicle connections
                        if (firstPath && secondPath && firstPath.type === 'Vehicle' && secondPath.type === 'Vehicle') {
                            console.log("Cannot connect two vehicle paths together");
                            return;
                        }

                        // Prevent conveyor middle point to conveyor connections
                        if (firstPath && secondPath &&
                            firstPath.type === 'Conveyor' &&
                            secondPath.type === 'Conveyor' &&
                            !firstSelected?.isCorner) {
                            console.log("Conveyor middle points can only connect to non-conveyor paths");
                            return;
                        }

                        // Check if this specific connection already exists
                        const isDuplicateConnection = firstSelected
                            ? simulationStates.some(path => {
                                if (path.modeluuid === firstSelected.pathUUID) {
                                    if (path.type === 'Conveyor') {
                                        const point = path.points.find(p => p.uuid === firstSelected.sphereUUID);
                                        return point?.connections.targets.some(t =>
                                            t.pathUUID === pathUUID && t.pointUUID === sphereUUID
                                        );
                                    } else if (path.type === 'Vehicle') {
                                        return path.points.connections.targets.some(t =>
                                            t.pathUUID === pathUUID && t.pointUUID === sphereUUID
                                        );
                                    }
                                }
                                return false;
                            })
                            : false;

                        if (isDuplicateConnection) {
                            console.log("These points are already connected. Ignoring.");
                            return;
                        }

                        // For Vehicles, check if they're already connected to anything
                        if (intersected.userData.path.type === 'Vehicle') {
                            console.log('intersected: ', intersected);
                            const vehicleConnections = intersected.userData.path.points.connections.targets.length;
                            if (vehicleConnections >= 1) {
                                console.log("Vehicle can only have one connection");
                                return;
                            }
                        }

                        // For non-Vehicle paths, check if already connected
                        if (intersected.userData.path.type !== 'Vehicle') {
                            const isAlreadyConnected = simulationStates.some(path => {
                                if (path.type === 'Conveyor') {
                                    return path.points.some(point =>
                                        point.uuid === sphereUUID &&
                                        point.connections.targets.length > 0
                                    );
                                }
                                return false;
                            });

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

                        if (firstSelected) {
                            // Check if trying to connect Vehicle to non-Conveyor
                            if ((firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') ||
                                (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor')) {
                                console.log("Vehicle can only connect to Conveyors");
                                return;
                            }

                            // Prevent same-path connections
                            if (firstSelected.pathUUID === pathUUID) {
                                console.log("Cannot connect spheres on the same path.");
                                return;
                            }

                            // At least one must be start/end point
                            if (!firstSelected.isCorner && !isStartOrEnd) {
                                console.log("At least one of the selected spheres must be a start or end point.");
                                return;
                            }

                            // All checks passed - make the connection
                            handleAddConnection(
                                firstSelected.pathUUID,
                                firstSelected.sphereUUID,
                                pathUUID,
                                sphereUUID
                            );
                        } else {
                            // First selection - just store it
                            setFirstSelected({
                                pathUUID,
                                sphereUUID,
                                position: worldPosition,
                                isCorner: isStartOrEnd
                            });
                            setIsConnecting(true);
                        }
                    }
                }
            } else {
                // Clicked outside - cancel connection
                setFirstSelected(null);
                setCurrentLine(null);
                setIsConnecting(false);
            }
        };

        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);
        }

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

    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("agv-collider") &&
                !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("events-sphere")
            );

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

                const firstPath = simulationStates.find(p => p.modeluuid === firstSelected.pathUUID);
                const secondPath = simulationStates.find(p => p.modeluuid === pathUUID);
                const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle';

                // Inside the useFrame hook, where we check for snapped spheres:
                const isConnectable = (pathData.type === 'Vehicle' ||
                    (pathData.points.length > 0 && (
                        sphereUUID === pathData.points[0].uuid ||
                        sphereUUID === pathData.points[pathData.points.length - 1].uuid
                    ))) &&
                    !isVehicleToVehicle &&
                    !(firstPath?.type === 'Conveyor' &&
                        pathData.type === 'Conveyor' &&
                        !firstSelected.isCorner);

                // Check for duplicate connection (regardless of path type)
                const isDuplicateConnection = simulationStates.some(path => {
                    if (path.modeluuid === firstSelected.pathUUID) {
                        if (path.type === 'Conveyor') {
                            const point = path.points.find(p => p.uuid === firstSelected.sphereUUID);
                            return point?.connections.targets.some(t =>
                                t.pathUUID === pathUUID && t.pointUUID === sphereUUID
                            );
                        } else if (path.type === 'Vehicle') {
                            return path.points.connections.targets.some(t =>
                                t.pathUUID === pathUUID && t.pointUUID === sphereUUID
                            );
                        }
                    }
                    return false;
                });

                // For non-Vehicle paths, check if already connected
                const isNonVehicleAlreadyConnected = pathData.type !== 'Vehicle' &&
                    simulationStates.some(path => {
                        if (path.type === 'Conveyor') {
                            return path.points.some(point =>
                                point.uuid === sphereUUID &&
                                point.connections.targets.length > 0
                            );
                        }
                        return false;
                    });

                // Check vehicle connection rules
                const isVehicleAtMaxConnections = pathData.type === 'Vehicle' &&
                    pathData.points.connections.targets.length >= 1;
                const isVehicleConnectingToNonConveyor =
                    (firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') ||
                    (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor');

                if (
                    !isDuplicateConnection &&
                    !isVehicleToVehicle &&
                    !isNonVehicleAlreadyConnected &&
                    !isVehicleAtMaxConnections &&
                    !isVehicleConnectingToNonConveyor &&
                    firstSelected.sphereUUID !== sphereUUID &&
                    firstSelected.pathUUID !== pathUUID &&
                    (firstSelected.isCorner || isConnectable)
                ) {
                    snappedSphere = {
                        sphereUUID,
                        position: spherePosition,
                        pathUUID,
                        isCorner: isConnectable
                    };
                } else {
                    isInvalidConnection = true;
                }
            }

            if (snappedSphere) {
                point = snappedSphere.position;
            }

            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,
                });

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

    return (
        <group name='simulationConnectionGroup' visible={!isPlaying} >
            {simulationStates.flatMap(path => {
                if (path.type === 'Conveyor') {
                    return path.points.flatMap(point =>
                        point.connections.targets.map((target, index) => {
                            const targetPath = simulationStates.find(p => p.modeluuid === target.pathUUID);
                            if (targetPath?.type === 'Vehicle') return null;

                            const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid);
                            const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);

                            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={`${point.uuid}-${target.pointUUID}-${index}`}
                                        start={fromWorldPosition.toArray()}
                                        end={toWorldPosition.toArray()}
                                        mid={midPoint.toArray()}
                                        color="white"
                                        lineWidth={4}
                                        dashed
                                        dashSize={0.75}
                                        dashScale={20}
                                    />
                                );
                            }
                            return null;
                        })
                    );
                } else if (path.type === 'Vehicle') {
                    return path.points.connections.targets.map((target, index) => {
                        const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.uuid);
                        const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);

                        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={`${path.points.uuid}-${target.pointUUID}-${index}`}
                                    start={fromWorldPosition.toArray()}
                                    end={toWorldPosition.toArray()}
                                    mid={midPoint.toArray()}
                                    color="orange"
                                    lineWidth={4}
                                    dashed
                                    dashSize={0.75}
                                    dashScale={20}
                                />
                            );
                        }
                        return null;
                    });
                }
                return [];
            })}

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

export default PathConnector;