import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import { useRef, useState, useEffect, useMemo } from "react";
import { Sphere, TransformControls } from "@react-three/drei";
import {
  useEditingPoint,
  useEyeDropMode,
  useIsConnecting,
  usePreviewPosition,
  useRenderDistance,
  useSelectedActionSphere,
  useSelectedPath,
  useSimulationPaths,
} from "../../../store/store";
import { useFrame, useThree } from "@react-three/fiber";
import { useSubModuleStore } from "../../../store/useModuleStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";

function PathCreation({
  pathsGroupRef,
}: {
  pathsGroupRef: React.MutableRefObject<THREE.Group>;
}) {
  const { isPlaying } = usePlayButtonStore();
  const { renderDistance } = useRenderDistance();
  const { setSubModule } = useSubModuleStore();
  const { setSelectedActionSphere, selectedActionSphere } =
    useSelectedActionSphere();
  const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
  const { editingPoint, setEditingPoint } = useEditingPoint();
  const { previewPosition, setPreviewPosition } = usePreviewPosition();
  const { raycaster, camera, pointer, gl } = useThree();
  const plane = useMemo(
    () => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
    []
  );
  const { setSelectedPath } = useSelectedPath();
  const { simulationPaths, setSimulationPaths } = useSimulationPaths();
  const { isConnecting } = useIsConnecting();

  const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
  const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
  const isMovingRef = useRef(false);
  const transformRef = useRef<any>(null);
  const [transformMode, setTransformMode] = useState<
    "translate" | "rotate" | null
  >(null);

  useEffect(() => {
    setTransformMode(null);
    const handleKeyDown = (e: KeyboardEvent) => {
      if (!selectedActionSphere) return;
      if (e.key === "g") {
        setTransformMode((prev) => (prev === "translate" ? null : "translate"));
      }
      if (e.key === "r") {
        setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [selectedActionSphere]);

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

  const updateSimulationPaths = () => {
    if (!selectedActionSphere) return;

    const updatedPaths = simulationPaths.map((path) => {
      if (path.type === "Conveyor") {
        return {
          ...path,
          points: path.points.map((point) =>
            point.uuid === selectedActionSphere.point.uuid
              ? {
                  ...point,
                  position: [
                    selectedActionSphere.point.position.x,
                    selectedActionSphere.point.position.y,
                    selectedActionSphere.point.position.z,
                  ],
                  rotation: [
                    selectedActionSphere.point.rotation.x,
                    selectedActionSphere.point.rotation.y,
                    selectedActionSphere.point.rotation.z,
                  ],
                }
              : point
          ),
        };
      }
      return path;
    }) as Types.ConveyorEventsSchema[];

    setSimulationPaths(updatedPaths);
  };

  useFrame(() => {
    if (eyeDropMode) {
      raycaster.setFromCamera(pointer, camera);
      const intersectionPoint = new THREE.Vector3();
      const point = raycaster.ray.intersectPlane(plane, intersectionPoint);

      if (point) {
        setPreviewPosition({ x: point.x, y: point.z });
      }
    } else {
      setPreviewPosition(null);
    }
  });

  useEffect(() => {
    if (!camera) return;
    const canvasElement = gl.domElement;
    canvasElement.tabIndex = 0;

    const onPointerDown = () => {
      isMovingRef.current = false;
    };

    const onPointerMove = () => {
      isMovingRef.current = true;
    };

    const onPointerUp = (event: PointerEvent) => {
      if (
        !isMovingRef.current &&
        eyeDropMode &&
        event.button === 0 &&
        previewPosition
      ) {
        event.preventDefault();
        if (editingPoint) {
          handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y);
          setEditingPoint(null);
          setEyeDropMode(false);
        }
      }
    };

    if (eyeDropMode) {
      canvasElement.addEventListener("pointerdown", onPointerDown);
      canvasElement.addEventListener("pointermove", onPointerMove);
      canvasElement.addEventListener("pointerup", onPointerUp);
    }

    return () => {
      canvasElement.removeEventListener("pointerdown", onPointerDown);
      canvasElement.removeEventListener("pointermove", onPointerMove);
      canvasElement.removeEventListener("pointerup", onPointerUp);
    };
  }, [eyeDropMode, editingPoint, previewPosition]);

  const handlePointUpdate = (
    pointType: "start" | "end",
    x: number,
    z: number
  ) => {
    if (!selectedActionSphere?.point?.uuid) return;

    const updatedPaths = simulationPaths.map((path) => {
      if (
        path.type === "Vehicle" &&
        path.point.uuid === selectedActionSphere.point.uuid
      ) {
        return {
          ...path,
          point: {
            ...path.point,
            actions: {
              ...path.point.actions,
              [pointType]: {
                ...path.point.actions[pointType],
                x: x,
                y: z,
              },
            },
          },
        };
      }
      return path;
    });

    setSimulationPaths(updatedPaths);
  };

  return (
    <group visible={!isPlaying} name="simulation-simulationPaths-group" ref={pathsGroupRef}>
      {simulationPaths.map((path) => {
        if (path.type === "Conveyor") {
          const points = path.points.map(
            (point) => new THREE.Vector3(...point.position)
          );

          return (
            <group
              name={`${path.modeluuid}-event-path`}
              key={path.modeluuid}
              ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
              position={path.position}
              rotation={path.rotation}
              onClick={(e) => {
                if (isConnecting || eyeDropMode) return;
                e.stopPropagation();
                setSelectedPath({
                  path,
                  group: groupRefs.current[path.modeluuid],
                });
                setSelectedActionSphere(null);
                setTransformMode(null);
                setSubModule("mechanics");
              }}
              onPointerMissed={() => {
                if (eyeDropMode) return;
                setSelectedPath(null);
                setSubModule("properties");
              }}
            >
              {path.points.map((point, index) => (
                <Sphere
                  key={point.uuid}
                  uuid={point.uuid}
                  position={point.position}
                  args={[0.15, 32, 32]}
                  name="events-sphere"
                  ref={(el) => (sphereRefs.current[point.uuid] = el!)}
                  onClick={(e) => {
                    if (isConnecting || eyeDropMode) return;
                    e.stopPropagation();
                    setSelectedActionSphere({
                      path,
                      point: sphereRefs.current[point.uuid],
                    });
                    setSubModule("mechanics");
                    setSelectedPath(null);
                  }}
                  userData={{ point, path }}
                  onPointerMissed={() => {
                    if (eyeDropMode) return;
                    setSubModule("properties");
                    setSelectedActionSphere(null);
                  }}
                >
                  <meshStandardMaterial
                    color={
                      index === 0
                        ? "orange"
                        : index === path.points.length - 1
                        ? "blue"
                        : "green"
                    }
                  />
                </Sphere>
              ))}

              {points.slice(0, -1).map((point, index) => {
                const nextPoint = points[index + 1];
                const segmentCurve = new THREE.CatmullRomCurve3([
                  point,
                  nextPoint,
                ]);
                const tubeGeometry = new THREE.TubeGeometry(
                  segmentCurve,
                  20,
                  0.1,
                  16,
                  false
                );

                return (
                  <mesh
                    name="event-connection-tube"
                    key={`tube-${index}`}
                    geometry={tubeGeometry}
                  >
                    <meshStandardMaterial
                      transparent
                      opacity={0.9}
                      color="red"
                    />
                  </mesh>
                );
              })}
            </group>
          );
        } else if (path.type === "Vehicle") {
          return (
            <group
              name={`${path.modeluuid}-vehicle-path`}
              key={path.modeluuid}
              ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
              position={path.position}
              onClick={(e) => {
                if (isConnecting || eyeDropMode) return;
                e.stopPropagation();
                setSelectedPath({
                  path,
                  group: groupRefs.current[path.modeluuid],
                });
                setSelectedActionSphere(null);
                setTransformMode(null);
                setSubModule("mechanics");
              }}
              onPointerMissed={() => {
                if (eyeDropMode) return;
                setSelectedPath(null);
                setSubModule("properties");
              }}
            >
              <Sphere
                key={path.point.uuid}
                uuid={path.point.uuid}
                position={path.point.position}
                args={[0.15, 32, 32]}
                name="events-sphere"
                ref={(el) => (sphereRefs.current[path.point.uuid] = el!)}
                onClick={(e) => {
                  if (isConnecting || eyeDropMode) return;
                  e.stopPropagation();
                  setSelectedActionSphere({
                    path,
                    point: sphereRefs.current[path.point.uuid],
                  });
                  setSubModule("mechanics");
                  setSelectedPath(null);
                }}
                userData={{ point: path.point, path }}
                onPointerMissed={() => {
                  if (eyeDropMode) return;
                  setSubModule("properties");
                  setSelectedActionSphere(null);
                }}
              >
                <meshStandardMaterial color="purple" />
              </Sphere>
            </group>
          );
        }
        return null;
      })}

      {selectedActionSphere && transformMode && (
        <TransformControls
          ref={transformRef}
          object={selectedActionSphere.point}
          mode={transformMode}
          onMouseUp={updateSimulationPaths}
        />
      )}
    </group>
  );
}

export default PathCreation;
