import { useFrame, useThree } from "@react-three/fiber";
import React, { useEffect, useRef, useState } from "react";
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import * as SimulationTypes from "../../../types/simulation";
import { QuadraticBezierLine } from "@react-three/drei";
import {
  useDeleteTool,
  useIsConnecting,
  useRenderDistance,
  useSimulationStates,
  useSocketStore,
} from "../../../store/store";
import useModuleStore from "../../../store/useModuleStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt";

function PathConnector({
  pathsGroupRef,
}: {
  pathsGroupRef: React.MutableRefObject<THREE.Group>;
}) {
  const { activeModule } = useModuleStore();
  const { gl, raycaster, scene, pointer, camera } = useThree();
  const { deleteTool } = useDeleteTool();
  const { renderDistance } = useRenderDistance();
  const { setIsConnecting } = useIsConnecting();
  const { simulationStates, setSimulationStates } = useSimulationStates();
  const { isPlaying } = usePlayButtonStore();
  const { socket } = useSocketStore();
  const groupRefs = useRef<{ [key: string]: any }>({});

  const [firstSelected, setFirstSelected] = useState<{
    modelUUID: 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 [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);

  const updatePathConnections = (
    fromModelUUID: string,
    fromPointUUID: string,
    toModelUUID: string,
    toPointUUID: string
  ) => {
    const updatedPaths = simulationStates.map((path) => {
      if (path.type === "Conveyor") {
        // Handle outgoing connections from Conveyor
        if (path.modeluuid === fromModelUUID) {
          return {
            ...path,
            points: path.points.map((point) => {
              if (point.uuid === fromPointUUID) {
                const newTarget = {
                  modelUUID: toModelUUID,
                  pointUUID: toPointUUID,
                };
                const existingTargets = point.connections.targets || [];

                if (
                  !existingTargets.some(
                    (target) =>
                      target.modelUUID === newTarget.modelUUID &&
                      target.pointUUID === newTarget.pointUUID
                  )
                ) {
                  return {
                    ...point,
                    connections: {
                      ...point.connections,
                      targets: [...existingTargets, newTarget],
                    },
                  };
                }
              }
              return point;
            }),
          };
        }
        // Handle incoming connections to Conveyor
        else if (path.modeluuid === toModelUUID) {
          return {
            ...path,
            points: path.points.map((point) => {
              if (point.uuid === toPointUUID) {
                const reverseTarget = {
                  modelUUID: fromModelUUID,
                  pointUUID: fromPointUUID,
                };
                const existingTargets = point.connections.targets || [];

                if (
                  !existingTargets.some(
                    (target) =>
                      target.modelUUID === reverseTarget.modelUUID &&
                      target.pointUUID === reverseTarget.pointUUID
                  )
                ) {
                  return {
                    ...point,
                    connections: {
                      ...point.connections,
                      targets: [...existingTargets, reverseTarget],
                    },
                  };
                }
              }
              return point;
            }),
          };
        }
      } else if (path.type === "Vehicle") {
        // Handle outgoing connections from Vehicle
        if (
          path.modeluuid === fromModelUUID &&
          path.points.uuid === fromPointUUID
        ) {
          const newTarget = {
            modelUUID: toModelUUID,
            pointUUID: toPointUUID,
          };
          const existingTargets = path.points.connections.targets || [];

          // Check if target is a Conveyor
          const toPath = simulationStates.find(
            (p) => p.modeluuid === toModelUUID
          );
          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.modelUUID === newTarget.modelUUID &&
                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 === toModelUUID &&
          path.points.uuid === toPointUUID
        ) {
          const reverseTarget = {
            modelUUID: fromModelUUID,
            pointUUID: fromPointUUID,
          };
          const existingTargets = path.points.connections.targets || [];

          // Check if source is a Conveyor
          const fromPath = simulationStates.find(
            (p) => p.modeluuid === fromModelUUID
          );
          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.modelUUID === reverseTarget.modelUUID &&
                target.pointUUID === reverseTarget.pointUUID
            )
          ) {
            return {
              ...path,
              points: {
                ...path.points,
                connections: {
                  ...path.points.connections,
                  targets: [...existingTargets, reverseTarget],
                },
              },
            };
          }
        }
        return path;
      } else if (path.type === "StaticMachine") {
        // Handle outgoing connections from StaticMachine
        if (
          path.modeluuid === fromModelUUID &&
          path.points.uuid === fromPointUUID
        ) {
          const newTarget = {
            modelUUID: toModelUUID,
            pointUUID: toPointUUID,
          };

          // Ensure target is an ArmBot
          const toPath = simulationStates.find(
            (p) => p.modeluuid === toModelUUID
          );
          if (toPath?.type !== "ArmBot") {
            console.log("StaticMachine can only connect to ArmBot");
            return path;
          }

          const existingTargets = path.points.connections.targets || [];

          // Allow only one connection
          if (existingTargets.length >= 1) {
            console.log("StaticMachine can only have one connection");
            return path;
          }

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

        // Handle incoming connections to StaticMachine
        else if (
          path.modeluuid === toModelUUID &&
          path.points.uuid === toPointUUID
        ) {
          const reverseTarget = {
            modelUUID: fromModelUUID,
            pointUUID: fromPointUUID,
          };

          const fromPath = simulationStates.find(
            (p) => p.modeluuid === fromModelUUID
          );
          if (fromPath?.type !== "ArmBot") {
            console.log("StaticMachine can only be connected from ArmBot");
            return path;
          }

          const existingTargets = path.points.connections.targets || [];

          if (existingTargets.length >= 1) {
            console.log("StaticMachine can only have one connection");
            return path;
          }

          if (
            !existingTargets.some(
              (target) =>
                target.modelUUID === reverseTarget.modelUUID &&
                target.pointUUID === reverseTarget.pointUUID
            )
          ) {
            return {
              ...path,
              points: {
                ...path.points,
                connections: {
                  ...path.points.connections,
                  targets: [...existingTargets, reverseTarget],
                },
              },
            };
          }
        }
        return path;
      } else if (path.type === "ArmBot") {
        // Handle outgoing connections from ArmBot
        if (
          path.modeluuid === fromModelUUID &&
          path.points.uuid === fromPointUUID
        ) {
          const newTarget = {
            modelUUID: toModelUUID,
            pointUUID: toPointUUID,
          };

          const toPath = simulationStates.find(
            (p) => p.modeluuid === toModelUUID
          );
          if (!toPath) return path;

          const existingTargets = path.points.connections.targets || [];

          // Check if connecting to a StaticMachine and already connected to one
          const alreadyConnectedToStatic = existingTargets.some((target) => {
            const targetPath = simulationStates.find(
              (p) => p.modeluuid === target.modelUUID
            );
            return targetPath?.type === "StaticMachine";
          });

          if (toPath.type === "StaticMachine") {
            if (alreadyConnectedToStatic) {
              console.log("ArmBot can only connect to one StaticMachine");
              return path;
            }
          }

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

        // Handle incoming connections to ArmBot
        else if (
          path.modeluuid === toModelUUID &&
          path.points.uuid === toPointUUID
        ) {
          const reverseTarget = {
            modelUUID: fromModelUUID,
            pointUUID: fromPointUUID,
          };

          const fromPath = simulationStates.find(
            (p) => p.modeluuid === fromModelUUID
          );
          if (!fromPath) return path;

          const existingTargets = path.points.connections.targets || [];

          const alreadyConnectedFromStatic = existingTargets.some((target) => {
            const targetPath = simulationStates.find(
              (p) => p.modeluuid === target.modelUUID
            );
            return targetPath?.type === "StaticMachine";
          });

          if (fromPath.type === "StaticMachine") {
            if (alreadyConnectedFromStatic) {
              console.log(
                "ArmBot can only be connected from one StaticMachine"
              );
              return path;
            }
          }

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

      return path;
    });

    setSimulationStates(updatedPaths);

    const updatedPathDetails = updatedPaths.filter(
      (path) =>
        path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID
    );

    updateBackend(updatedPathDetails);
  };

  const updateBackend = async (
    updatedPaths: (
      | SimulationTypes.ConveyorEventsSchema
      | SimulationTypes.VehicleEventsSchema
      | SimulationTypes.StaticMachineEventsSchema
      | SimulationTypes.ArmBotEventsSchema
    )[]
  ) => {
    if (updatedPaths.length === 0) return;
    const email = localStorage.getItem("email");
    const organization = email ? email.split("@")[1].split(".")[0] : "";

    updatedPaths.forEach(async (updatedPath) => {
      if (updatedPath.type === "Conveyor") {
        // await setEventApi(
        //   organization,
        //   updatedPath.modeluuid,
        //   { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed }
        // );

        const data = {
          organization: organization,
          modeluuid: updatedPath.modeluuid,
          eventData: {
            type: "Conveyor",
            points: updatedPath.points,
            speed: updatedPath.speed,
          },
        };

        socket.emit("v2:model-asset:updateEventData", data);
      } else if (updatedPath.type === "Vehicle") {
        // await setEventApi(
        //     organization,
        //     updatedPath.modeluuid,
        //     { type: "Vehicle", points: updatedPath.points }
        // );

        const data = {
          organization: organization,
          modeluuid: updatedPath.modeluuid,
          eventData: { type: "Vehicle", points: updatedPath.points },
        };

        socket.emit("v2:model-asset:updateEventData", data);
      } else if (updatedPath.type === "StaticMachine") {
        // await setEventApi(
        //     organization,
        //     updatedPath.modeluuid,
        //     { type: "StaticMachine", points: updatedPath.points }
        // );

        const data = {
          organization: organization,
          modeluuid: updatedPath.modeluuid,
          eventData: { type: "StaticMachine", points: updatedPath.points },
        };

        socket.emit("v2:model-asset:updateEventData", data);
      } else if (updatedPath.type === "ArmBot") {
        // await setEventApi(
        //     organization,
        //     updatedPath.modeluuid,
        //     { type: "ArmBot", points: updatedPath.points }
        // );

        const data = {
          organization: organization,
          modeluuid: updatedPath.modeluuid,
          eventData: { type: "ArmBot", points: updatedPath.points },
        };

        socket.emit("v2:model-asset:updateEventData", data);
      }
    });
  };

  const handleAddConnection = (
    fromModelUUID: string,
    fromUUID: string,
    toModelUUID: string,
    toUUID: string
  ) => {
    updatePathConnections(fromModelUUID, fromUUID, toModelUUID, 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 modelUUID = 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 (modelUUID) {
            const firstPath = simulationStates.find(
              (p) => p.modeluuid === firstSelected?.modelUUID
            );
            const secondPath = simulationStates.find(
              (p) => p.modeluuid === modelUUID
            );

            // 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 || !isStartOrEnd)
            ) {
              console.log(
                "Conveyor connections must be between start/end points"
              );
              return;
            }

            // Check if this specific connection already exists
            const isDuplicateConnection = firstSelected
              ? simulationStates.some((path) => {
                  if (path.modeluuid === firstSelected.modelUUID) {
                    if (path.type === "Conveyor") {
                      const point = path.points.find(
                        (p) => p.uuid === firstSelected.sphereUUID
                      );
                      return point?.connections.targets.some(
                        (t) =>
                          t.modelUUID === modelUUID &&
                          t.pointUUID === sphereUUID
                      );
                    } else if (path.type === "Vehicle") {
                      return path.points.connections.targets.some(
                        (t) =>
                          t.modelUUID === modelUUID &&
                          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") {
              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.modelUUID === modelUUID) {
                console.log("Cannot connect spheres on the same path.");
                return;
              }

              // Check if StaticMachine is involved in the connection
              if (
                (firstPath?.type === "StaticMachine" &&
                  secondPath?.type !== "ArmBot") ||
                (secondPath?.type === "StaticMachine" &&
                  firstPath?.type !== "ArmBot")
              ) {
                console.log("StaticMachine can only connect to ArmBot");
                return;
              }

              // Check if StaticMachine already has a connection
              if (firstPath?.type === "StaticMachine") {
                const staticConnections =
                  firstPath.points.connections.targets.length;
                if (staticConnections >= 1) {
                  console.log("StaticMachine can only have one connection");
                  return;
                }
              }
              if (secondPath?.type === "StaticMachine") {
                const staticConnections =
                  secondPath.points.connections.targets.length;
                if (staticConnections >= 1) {
                  console.log("StaticMachine can only have one connection");
                  return;
                }
              }

              // Check if ArmBot is involved
              if (
                (firstPath?.type === "ArmBot" &&
                  secondPath?.type === "StaticMachine") ||
                (secondPath?.type === "ArmBot" &&
                  firstPath?.type === "StaticMachine")
              ) {
                const armBotPath =
                  firstPath?.type === "ArmBot" ? firstPath : secondPath;
                const staticPath =
                  firstPath?.type === "StaticMachine" ? firstPath : secondPath;

                const armBotConnections =
                  armBotPath.points.connections.targets || [];
                const alreadyConnectedToStatic = armBotConnections.some(
                  (target) => {
                    const targetPath = simulationStates.find(
                      (p) => p.modeluuid === target.modelUUID
                    );
                    return targetPath?.type === "StaticMachine";
                  }
                );

                if (alreadyConnectedToStatic) {
                  console.log("ArmBot can only connect to one StaticMachine");
                  return;
                }

                const staticConnections =
                  staticPath.points.connections.targets.length;
                if (staticConnections >= 1) {
                  console.log("StaticMachine can only have one connection");
                  return;
                }
              }

              // Prevent ArmBot ↔ ArmBot
              if (
                firstPath?.type === "ArmBot" &&
                secondPath?.type === "ArmBot"
              ) {
                console.log("Cannot connect two ArmBots together");
                return;
              }

              // If one is ArmBot, ensure the other is StaticMachine or Conveyor
              if (
                firstPath?.type === "ArmBot" ||
                secondPath?.type === "ArmBot"
              ) {
                const otherType =
                  firstPath?.type === "ArmBot"
                    ? secondPath?.type
                    : firstPath?.type;
                if (otherType !== "StaticMachine" && otherType !== "Conveyor") {
                  console.log(
                    "ArmBot can only connect to Conveyors or one StaticMachine"
                  );
                  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.modelUUID,
                firstSelected.sphereUUID,
                modelUUID,
                sphereUUID
              );
            } else {
              // First selection - just store it
              setFirstSelected({
                modelUUID,
                sphereUUID,
                position: worldPosition,
                isCorner: isStartOrEnd,
              });
              setIsConnecting(true);
            }
          }
        }
      } else {
        // Clicked outside - cancel connection
        setFirstSelected(null);
        setCurrentLine(null);
        setIsConnecting(false);
      }
    };

    if (activeModule === "simulation" && !deleteTool) {
      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, deleteTool]);

  useFrame(() => {
    Object.values(groupRefs.current).forEach((group) => {
      if (group) {
        const distance = new THREE.Vector3(
          ...group.position.toArray()
        ).distanceTo(camera.position);
        group.visible = distance <= renderDistance && !isPlaying;
      }
    });
  });

  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;
        modelUUID: 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 modelUUID = pathData.modeluuid;

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

        // Inside the useFrame hook, where we check for snapped spheres:
        const isConnectable =
          (pathData.type === "Vehicle" ||
            pathData.type === "ArmBot" ||
            (pathData.points.length > 0 &&
              (sphereUUID === pathData.points[0].uuid ||
                sphereUUID ===
                  pathData.points[pathData.points.length - 1].uuid ||
                (pathData.type === "Conveyor" &&
                  firstPath?.type === "ArmBot")))) && // Allow ArmBot to connect to middle points
          !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.modelUUID) {
            if (path.type === "Conveyor") {
              const point = path.points.find(
                (p) => p.uuid === firstSelected.sphereUUID
              );
              return point?.connections.targets.some(
                (t) => t.modelUUID === modelUUID && t.pointUUID === sphereUUID
              );
            } else if (path.type === "Vehicle") {
              return path.points.connections.targets.some(
                (t) => t.modelUUID === modelUUID && 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");

        // Check if StaticMachine is connecting to non-ArmBot
        const isStaticMachineToNonArmBot =
          (firstPath?.type === "StaticMachine" &&
            secondPath?.type !== "ArmBot") ||
          (secondPath?.type === "StaticMachine" &&
            firstPath?.type !== "ArmBot");

        // Check if StaticMachine already has a connection
        const isStaticMachineAtMaxConnections =
          (firstPath?.type === "StaticMachine" &&
            firstPath.points.connections.targets.length >= 1) ||
          (secondPath?.type === "StaticMachine" &&
            secondPath.points.connections.targets.length >= 1);

        // Check if ArmBot is connecting to StaticMachine
        const isArmBotToStaticMachine =
          (firstPath?.type === "ArmBot" &&
            secondPath?.type === "StaticMachine") ||
          (secondPath?.type === "ArmBot" &&
            firstPath?.type === "StaticMachine");

        // Prevent multiple StaticMachine connections to ArmBot
        let isArmBotAlreadyConnectedToStatic = false;
        if (isArmBotToStaticMachine) {
          const armBotPath =
            firstPath?.type === "ArmBot" ? firstPath : secondPath;
          isArmBotAlreadyConnectedToStatic =
            armBotPath.points.connections.targets.some((target) => {
              const targetPath = simulationStates.find(
                (p) => p.modeluuid === target.modelUUID
              );
              return targetPath?.type === "StaticMachine";
            });
        }

        // Prevent ArmBot to ArmBot
        const isArmBotToArmBot =
          firstPath?.type === "ArmBot" && secondPath?.type === "ArmBot";

        // If ArmBot is involved, other must be Conveyor or StaticMachine
        const isArmBotToInvalidType =
          (firstPath?.type === "ArmBot" || secondPath?.type === "ArmBot") &&
          !(
            firstPath?.type === "Conveyor" ||
            firstPath?.type === "StaticMachine" ||
            secondPath?.type === "Conveyor" ||
            secondPath?.type === "StaticMachine"
          );

        if (
          !isDuplicateConnection &&
          !isVehicleToVehicle &&
          !isNonVehicleAlreadyConnected &&
          !isVehicleAtMaxConnections &&
          !isVehicleConnectingToNonConveyor &&
          !isStaticMachineToNonArmBot &&
          !isStaticMachineAtMaxConnections &&
          !isArmBotToArmBot &&
          !isArmBotToInvalidType &&
          !isArmBotAlreadyConnectedToStatic &&
          firstSelected.sphereUUID !== sphereUUID &&
          firstSelected.modelUUID !== modelUUID &&
          (firstSelected.isCorner || isConnectable) &&
          !(
            firstPath?.type === "Conveyor" &&
            pathData.type === "Conveyor" &&
            !(firstSelected.isCorner && isConnectable)
          )
        ) {
          snappedSphere = {
            sphereUUID,
            position: spherePosition,
            modelUUID,
            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);
    }
  });

  const removeConnections = (
    connection1: { model: string; point: string },
    connection2: { model: string; point: string }
  ) => {
    const updatedStates = simulationStates.map((state) => {
      // Handle Conveyor (which has multiple points)
      if (state.type === "Conveyor") {
        const updatedConveyor: SimulationTypes.ConveyorEventsSchema = {
          ...state,
          points: state.points.map((point) => {
            // Check if this point is either connection1 or connection2
            if (
              (state.modeluuid === connection1.model &&
                point.uuid === connection1.point) ||
              (state.modeluuid === connection2.model &&
                point.uuid === connection2.point)
            ) {
              return {
                ...point,
                connections: {
                  ...point.connections,
                  targets: point.connections.targets.filter((target) => {
                    // Remove the target that matches the other connection
                    return !(
                      (target.modelUUID === connection1.model &&
                        target.pointUUID === connection1.point) ||
                      (target.modelUUID === connection2.model &&
                        target.pointUUID === connection2.point)
                    );
                  }),
                },
              };
            }
            return point;
          }),
        };
        return updatedConveyor;
      }
      // Handle Vehicle
      else if (state.type === "Vehicle") {
        if (
          (state.modeluuid === connection1.model &&
            state.points.uuid === connection1.point) ||
          (state.modeluuid === connection2.model &&
            state.points.uuid === connection2.point)
        ) {
          const updatedVehicle: SimulationTypes.VehicleEventsSchema = {
            ...state,
            points: {
              ...state.points,
              connections: {
                ...state.points.connections,
                targets: state.points.connections.targets.filter((target) => {
                  return !(
                    (target.modelUUID === connection1.model &&
                      target.pointUUID === connection1.point) ||
                    (target.modelUUID === connection2.model &&
                      target.pointUUID === connection2.point)
                  );
                }),
              },
              // Ensure all required Vehicle point properties are included
              speed: state.points.speed,
              actions: state.points.actions,
            },
          };
          return updatedVehicle;
        }
      }
      // Handle StaticMachine
      else if (state.type === "StaticMachine") {
        if (
          (state.modeluuid === connection1.model &&
            state.points.uuid === connection1.point) ||
          (state.modeluuid === connection2.model &&
            state.points.uuid === connection2.point)
        ) {
          const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = {
            ...state,
            points: {
              ...state.points,
              connections: {
                ...state.points.connections,
                targets: state.points.connections.targets.filter((target) => {
                  return !(
                    (target.modelUUID === connection1.model &&
                      target.pointUUID === connection1.point) ||
                    (target.modelUUID === connection2.model &&
                      target.pointUUID === connection2.point)
                  );
                }),
              },
              // Ensure all required StaticMachine point properties are included
              actions: state.points.actions,
              triggers: state.points.triggers,
            },
          };
          return updatedStaticMachine;
        }
      }
      // Handle ArmBot
      else if (state.type === "ArmBot") {
        if (
          (state.modeluuid === connection1.model &&
            state.points.uuid === connection1.point) ||
          (state.modeluuid === connection2.model &&
            state.points.uuid === connection2.point)
        ) {
          const updatedArmBot: SimulationTypes.ArmBotEventsSchema = {
            ...state,
            points: {
              ...state.points,
              connections: {
                ...state.points.connections,
                targets: state.points.connections.targets.filter((target) => {
                  return !(
                    (target.modelUUID === connection1.model &&
                      target.pointUUID === connection1.point) ||
                    (target.modelUUID === connection2.model &&
                      target.pointUUID === connection2.point)
                  );
                }),
              },
              actions: {
                ...state.points.actions,
                processes:
                  state.points.actions.processes?.filter((process) => {
                    return !(
                      process.startPoint === connection1.point ||
                      process.endPoint === connection1.point ||
                      process.startPoint === connection2.point ||
                      process.endPoint === connection2.point
                    );
                  }) || [],
              },
              triggers: state.points.triggers,
            },
          };
          return updatedArmBot;
        }
      }
      return state;
    });

    const updatedPaths = updatedStates.filter(
      (state) =>
        state.modeluuid === connection1.model ||
        state.modeluuid === connection2.model
    );

    console.log("updatedPaths: ", updatedPaths);
    updateBackend(updatedPaths);

    setSimulationStates(updatedStates);
  };

  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.modelUUID
              );
              if (
                targetPath?.type !== "Conveyor" &&
                targetPath?.type !== "ArmBot"
              )
                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}`}
                    ref={(el) =>
                      (groupRefs.current[
                        `${point.uuid}-${target.pointUUID}-${index}`
                      ] = el!)
                    }
                    start={fromWorldPosition.toArray()}
                    end={toWorldPosition.toArray()}
                    mid={midPoint.toArray()}
                    color={
                      deleteTool &&
                      hoveredLineKey ===
                        `${point.uuid}-${target.pointUUID}-${index}`
                        ? "red"
                        : targetPath?.type === "ArmBot"
                        ? "#42a5f5"
                        : "white"
                    }
                    lineWidth={4}
                    dashed={
                      deleteTool &&
                      hoveredLineKey ===
                        `${point.uuid}-${target.pointUUID}-${index}`
                        ? false
                        : true
                    }
                    dashSize={0.75}
                    dashScale={20}
                    onPointerOver={() =>
                      setHoveredLineKey(
                        `${point.uuid}-${target.pointUUID}-${index}`
                      )
                    }
                    onPointerOut={() => setHoveredLineKey(null)}
                    onClick={() => {
                      if (deleteTool) {
                        const connection1 = {
                          model: path.modeluuid,
                          point: point.uuid,
                        };
                        const connection2 = {
                          model: target.modelUUID,
                          point: target.pointUUID,
                        };

                        removeConnections(connection1, connection2);
                      }
                    }}
                    userData={target}
                  />
                );
              }
              return null;
            })
          );
        }

        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}`}
                  ref={(el) =>
                    (groupRefs.current[
                      `${path.points.uuid}-${target.pointUUID}-${index}`
                    ] = el!)
                  }
                  start={fromWorldPosition.toArray()}
                  end={toWorldPosition.toArray()}
                  mid={midPoint.toArray()}
                  color={
                    deleteTool &&
                    hoveredLineKey ===
                      `${path.points.uuid}-${target.pointUUID}-${index}`
                      ? "red"
                      : "orange"
                  }
                  lineWidth={4}
                  dashed={
                    deleteTool &&
                    hoveredLineKey ===
                      `${path.points.uuid}-${target.pointUUID}-${index}`
                      ? false
                      : true
                  }
                  dashSize={0.75}
                  dashScale={20}
                  onPointerOver={() =>
                    setHoveredLineKey(
                      `${path.points.uuid}-${target.pointUUID}-${index}`
                    )
                  }
                  onPointerOut={() => setHoveredLineKey(null)}
                  onClick={() => {
                    if (deleteTool) {
                      const connection1 = {
                        model: path.modeluuid,
                        point: path.points.uuid,
                      };
                      const connection2 = {
                        model: target.modelUUID,
                        point: target.pointUUID,
                      };

                      removeConnections(connection1, connection2);
                    }
                  }}
                  userData={target}
                />
              );
            }
            return null;
          });
        }

        if (path.type === "StaticMachine") {
          return path.points.connections.targets.map((target, index) => {
            const targetPath = simulationStates.find(
              (p) => p.modeluuid === target.modelUUID
            );
            if (targetPath?.type !== "ArmBot") return null;

            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}`}
                  ref={(el) =>
                    (groupRefs.current[
                      `${path.points.uuid}-${target.pointUUID}-${index}`
                    ] = el!)
                  }
                  start={fromWorldPosition.toArray()}
                  end={toWorldPosition.toArray()}
                  mid={midPoint.toArray()}
                  color={
                    deleteTool &&
                    hoveredLineKey ===
                      `${path.points.uuid}-${target.pointUUID}-${index}`
                      ? "red"
                      : "#42a5f5"
                  }
                  lineWidth={4}
                  dashed={
                    deleteTool &&
                    hoveredLineKey ===
                      `${path.points.uuid}-${target.pointUUID}-${index}`
                      ? false
                      : true
                  }
                  dashSize={0.75}
                  dashScale={20}
                  onPointerOver={() =>
                    setHoveredLineKey(
                      `${path.points.uuid}-${target.pointUUID}-${index}`
                    )
                  }
                  onPointerOut={() => setHoveredLineKey(null)}
                  onClick={() => {
                    if (deleteTool) {
                      const connection1 = {
                        model: path.modeluuid,
                        point: path.points.uuid,
                      };
                      const connection2 = {
                        model: target.modelUUID,
                        point: target.pointUUID,
                      };

                      removeConnections(connection1, connection2);
                    }
                  }}
                  userData={target}
                />
              );
            }
            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;
