import { DragControls } from "@react-three/drei";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useActiveTool, useToolMode } from "../../../../store/builder/store";
import { Plane, Vector3 } from "three";
import { useThree } from "@react-three/fiber";
import { handleCanvasCursors } from "../../../../utils/mouseUtils/handleCanvasCursors";
import { getPathsByPointId, setPathPosition } from "./function/getPaths";
import { aStar } from "../structuredPath/functions/aStar";
type PointData = {
  pointId: string;
  position: [number, number, number];
  isCurved?: boolean;
  handleA?: [number, number, number] | null;
  handleB?: [number, number, number] | null;
};
interface PathDataInterface {
  pathId: string;
  isActive?: boolean;
  isCurved?: boolean;
  pathPoints: [PointData, PointData];
}

type PathData = PathDataInterface[];
type PointHandlerProps = {
  point: PointData;
  hoveredPoint: PointData | null;
  setPaths: React.Dispatch<React.SetStateAction<PathData>>;
  paths: PathDataInterface[];
  setHoveredPoint: React.Dispatch<React.SetStateAction<PointData | null>>;
  hoveredLine: PathDataInterface | null;
};
export default function PointHandler({
  point,
  setPaths,
  paths,
  setHoveredPoint,
  hoveredLine,
  hoveredPoint,
}: PointHandlerProps) {
  const { toolMode } = useToolMode();
  const { activeTool } = useActiveTool();
  const { scene, raycaster } = useThree();
  const [isHovered, setIsHovered] = useState(false);
  const [dragOffset, setDragOffset] = useState<Vector3 | null>(null);
  const plane = useMemo(() => new Plane(new Vector3(0, 1, 0), 0), []);
  const [initialPositions, setInitialPositions] = useState<{
    paths?: any;
  }>({});
  const [selectedPoints, setSelectedPoints] = useState<PointData[]>([]);

  const POINT_SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters

  const CAN_POINT_SNAP = true;
  const CAN_ANGLE_SNAP = true;
  const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5;

  const removePathByPoint = (pointId: string): PathDataInterface[] => {
    const removedPaths: PathDataInterface[] = [];

    setPaths((prevPaths) =>
      prevPaths.filter((path) => {
        const hasPoint = path.pathPoints.some((p) => p.pointId === pointId);
        if (hasPoint) {
          removedPaths.push(JSON.parse(JSON.stringify(path))); // keep a copy
          return false; // remove this path
        }
        return true; // keep this path
      })
    );

    return removedPaths;
  };
  const getConnectedPoints = (uuid: string): PointData[] => {
    const connected: PointData[] = [];

    for (const path of paths) {
      for (const point of path.pathPoints) {
        if (point.pointId === uuid) {
          connected.push(
            ...path.pathPoints.filter((p: PointData) => p.pointId !== uuid)
          );
        }
      }
    }

    return connected;
  };
  const snapPathAngle = useCallback(
    (
      newPosition: [number, number, number],
      pointId: string
    ): {
      position: [number, number, number];
      isSnapped: boolean;
      snapSources: Vector3[];
    } => {
      if (!pointId || !CAN_ANGLE_SNAP) {
        return { position: newPosition, isSnapped: false, snapSources: [] };
      }

      const connectedPoints: PointData[] = getConnectedPoints(pointId) || [];

      if (connectedPoints.length === 0) {
        return {
          position: newPosition,
          isSnapped: false,
          snapSources: [],
        };
      }

      const newPos = new Vector3(...newPosition);

      let closestX: { pos: Vector3; dist: number } | null = null;
      let closestZ: { pos: Vector3; dist: number } | null = null;

      for (const connectedPoint of connectedPoints) {
        const cPos = new Vector3(...connectedPoint.position);

        const xDist = Math.abs(newPos.x - cPos.x);
        const zDist = Math.abs(newPos.z - cPos.z);

        if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
          if (!closestX || xDist < closestX.dist) {
            closestX = { pos: cPos, dist: xDist };
          }
        }

        if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
          if (!closestZ || zDist < closestZ.dist) {
            closestZ = { pos: cPos, dist: zDist };
          }
        }
      }

      const snappedPos = newPos.clone();
      const snapSources: Vector3[] = [];

      if (closestX) {
        snappedPos.x = closestX.pos.x;
        snapSources.push(closestX.pos.clone());
      }

      if (closestZ) {
        snappedPos.z = closestZ.pos.z;
        snapSources.push(closestZ.pos.clone());
      }

      const isSnapped = snapSources.length > 0;

      return {
        position: [snappedPos.x, snappedPos.y, snappedPos.z],
        isSnapped,
        snapSources,
      };
    },
    []
  );
  const getAllOtherPathPoints = useCallback((): PointData[] => {
    return (
      paths?.flatMap((path) =>
        path.pathPoints.filter((pt) => pt.pointId !== point.pointId)
      ) ?? []
    );
  }, [paths]);

  const snapPathPoint = useCallback(
    (position: [number, number, number], pointId?: string) => {
      if (!CAN_POINT_SNAP)
        return { position: position, isSnapped: false, snappedPoint: null };

      const otherPoints = getAllOtherPathPoints();

      const currentVec = new Vector3(...position);
      for (const point of otherPoints) {
        const pointVec = new Vector3(...point.position);
        const distance = currentVec.distanceTo(pointVec);
        if (distance <= POINT_SNAP_THRESHOLD) {
          return {
            position: point.position,
            isSnapped: true,
            snappedPoint: point,
          };
        }
      }
      return { position: position, isSnapped: false, snappedPoint: null };
    },
    [getAllOtherPathPoints]
  );
  useEffect(() => {
    console.log("selectedPoints: ", selectedPoints);
  }, [selectedPoints]);
  //   const setPathPosition = useCallback(
  //     (
  //       pointUuid: string,
  //       position: [number, number, number],
  //       setPaths: React.Dispatch<React.SetStateAction<PathData>>
  //     ) => {
  //       setPaths((prevPaths) =>
  //         prevPaths.map((path) => {
  //           if (path.pathPoints.some((p) => p.pointId === pointUuid)) {
  //             return {
  //               ...path,
  //               pathPoints: path.pathPoints.map((p) =>
  //                 p.pointId === pointUuid ? { ...p, position } : p
  //               ) as [PointData, PointData], // 👈 force back to tuple
  //             };
  //           }
  //           return path;
  //         })
  //       );
  //     },
  //     [setPaths]
  //   );

  //   const getPathsByPointId = (pointId: any) => {
  //     return paths.filter((a) => a.pathPoints.some((p) => p.pointId === pointId));
  //   };

  const handlePointClick = (e: any, pointId: string) => {
    if (toolMode === "3D-Delete") {
      removePathByPoint(pointId);
    } else if (e.ctrlKey) {
    }
  };
  const handleDragStart = (point: PointData) => {
    if (activeTool !== "cursor") return;
    const intersectionPoint = new Vector3();
    const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
    if (hit) {
      const currentPosition = new Vector3(...point.position);
      const offset = new Vector3().subVectors(currentPosition, hit);
      setDragOffset(offset);
      const pathIntersection = getPathsByPointId(point.pointId, paths);
      setInitialPositions({ paths: pathIntersection });
    }
  };
  const handleDrag = (point: PointData) => {
    if (isHovered && dragOffset) {
      const intersectionPoint = new Vector3();
      const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
      if (hit) {
        // handleCanvasCursors("grabbing");
        const positionWithOffset = new Vector3().addVectors(hit, dragOffset);
        const newPosition: [number, number, number] = [
          positionWithOffset.x,
          positionWithOffset.y,
          positionWithOffset.z,
        ];

        // ✅ Pass newPosition and pointId
        const pathSnapped = snapPathAngle(newPosition, point.pointId);
        const finalSnapped = snapPathPoint(pathSnapped.position);
        setPathPosition(point.pointId, finalSnapped.position, setPaths);
      }
    }
  };

  const handleDragEnd = (point: PointData) => {
    const pathIntersection = getPathsByPointId(point.pointId, paths);
    if (pathIntersection && pathIntersection.length > 0) {
      pathIntersection.forEach((update) => {});
    }
  };
  return (
    <>
      <DragControls
        axisLock="y"
        autoTransform={false}
        onDragStart={() => handleDragStart(point)}
        onDrag={() => handleDrag(point)}
        onDragEnd={() => handleDragEnd(point)}
      >
        <mesh
          key={point.pointId}
          position={point.position}
          name="Path-Point"
          userData={point}
          onClick={(e) => {
            e.stopPropagation();
            handlePointClick(e, point.pointId);
          }}
          onPointerOver={(e) => {
            if (!hoveredPoint && e.buttons === 0 && !e.ctrlKey) {
              setHoveredPoint(point);
              setIsHovered(true);
              //   handleCanvasCursors("default");
            }
          }}
          onPointerOut={() => {
            if (hoveredPoint) {
              setHoveredPoint(null);
              if (!hoveredLine) {
                // handleCanvasCursors("default");
              }
            }
            setIsHovered(false);
          }}
        >
          <sphereGeometry args={[0.3, 16, 16]} />
          <meshBasicMaterial color="pink" />
        </mesh>
      </DragControls>
    </>
  );
}
