import * as THREE from "three";
import { useEffect, useRef, useState } from "react";
import { useThree, useFrame } from "@react-three/fiber";
import { useToolMode } from "../../../store/builder/store";
import { Html, Line } from "@react-three/drei";

const MeasurementTool = () => {
  const { gl, raycaster, pointer, camera, scene } = useThree();
  const { toolMode } = useToolMode();

  const [points, setPoints] = useState<THREE.Vector3[]>([]);
  const [linePoints, setLinePoints] = useState<THREE.Vector3[] | null>(null);
  const groupRef = useRef<THREE.Group>(null);

  useEffect(() => {
    const canvasElement = gl.domElement;
    let drag = false;
    let isLeftMouseDown = false;

    const onMouseDown = () => {
      isLeftMouseDown = true;
      drag = false;
    };

    const onMouseUp = (evt: any) => {
      isLeftMouseDown = false;
      if (evt.button === 0 && !drag) {
        raycaster.setFromCamera(pointer, camera);
        const intersects = raycaster
          .intersectObjects(scene.children, true)
          .filter(
            (intersect) =>
              !intersect.object.name.includes("Roof") &&
              !intersect.object.name.includes("MeasurementReference") &&
              !intersect.object.name.includes("agv-collider") &&
              !intersect.object.name.includes("zonePlane") &&
              !intersect.object.name.includes("SelectionGroup") &&
              !intersect.object.name.includes("selectionAssetGroup") &&
              !intersect.object.name.includes(
                "SelectionGroupBoundingBoxLine"
              ) &&
              !intersect.object.name.includes("SelectionGroupBoundingBox") &&
              !intersect.object.name.includes("SelectionGroupBoundingLine") &&
              intersect.object.type !== "GridHelper"
          );

        if (intersects.length > 0) {
          const intersectionPoint = intersects[0].point.clone();
          if (points.length < 2) {
            setPoints([...points, intersectionPoint]);
          } else {
            setPoints([intersectionPoint]);
          }
        }
      }
    };

    const onMouseMove = () => {
      if (isLeftMouseDown) drag = true;
    };

    const onContextMenu = (evt: any) => {
      evt.preventDefault();
      if (!drag) {
        setPoints([]);
        setLinePoints(null);
      }
    };

    if (toolMode === "MeasurementScale") {
      canvasElement.addEventListener("pointerdown", onMouseDown);
      canvasElement.addEventListener("pointermove", onMouseMove);
      canvasElement.addEventListener("pointerup", onMouseUp);
      canvasElement.addEventListener("contextmenu", onContextMenu);
    } else {
      setPoints([]);
      setLinePoints(null);
    }

    return () => {
      canvasElement.removeEventListener("pointerdown", onMouseDown);
      canvasElement.removeEventListener("pointermove", onMouseMove);
      canvasElement.removeEventListener("pointerup", onMouseUp);
      canvasElement.removeEventListener("contextmenu", onContextMenu);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [toolMode, camera, raycaster, pointer, scene, points]);

  useFrame(() => {
    if (points.length === 1) {
      // live preview for second point
      raycaster.setFromCamera(pointer, camera);
      const intersects = raycaster
        .intersectObjects(scene.children, true)
        .filter(
          (intersect) =>
            !intersect.object.name.includes("Roof") &&
            !intersect.object.name.includes("MeasurementReference") &&
            !intersect.object.name.includes("agv-collider") &&
            !intersect.object.name.includes("zonePlane") &&
            !intersect.object.name.includes("SelectionGroup") &&
            !intersect.object.name.includes("selectionAssetGroup") &&
            !intersect.object.name.includes("SelectionGroupBoundingBoxLine") &&
            !intersect.object.name.includes("SelectionGroupBoundingBox") &&
            !intersect.object.name.includes("SelectionGroupBoundingLine") &&
            intersect.object.type !== "GridHelper"
        );

      if (intersects.length > 0) {
        const tempEnd = intersects[0].point.clone();
        updateMeasurement(points[0], tempEnd);
      }
    } else if (points.length === 2) {
      // second point already fixed
      updateMeasurement(points[0], points[1]);
    } else {
      setLinePoints(null);
    }
  });

  const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => {
    setLinePoints([start.clone(), end.clone()]);
  };

  return (
    <group ref={groupRef} name="MeasurementGroup">
      {linePoints && (
        <>
          {/* Outline line */}
          <Line
            points={linePoints}
            color="black"
            lineWidth={6} // thicker than main line
            depthTest={false}
            depthWrite={false}
            renderOrder={998} // render behind main line
          />

          {/* Main line */}
          <Line
            points={linePoints}
            color="#b18ef1"
            lineWidth={2} // actual line width
            depthTest={false}
            depthWrite={false}
            transparent={false}
            opacity={1}
            renderOrder={999} // render on top
          />
        </>
      )}

      {points.map((point, index) => (
        <Html
          key={index}
          position={point}
          scale={0.5}
          wrapperClass="measurement-label-wrapper"
          className="measurement-label"
          zIndexRange={[1, 0]}
          prepend
          sprite
        >
          <div className="measurement-point"></div>
        </Html>
      ))}

      {linePoints && linePoints.length === 2 && (
        <Html
          position={[
            (linePoints[0].x + linePoints[1].x) / 2,
            (linePoints[0].y + linePoints[1].y) / 2,
            (linePoints[0].z + linePoints[1].z) / 2,
          ]}
          scale={0.5}
          wrapperClass="distance-text-wrapper"
          className="distance-text"
          zIndexRange={[2, 1]}
          prepend
          sprite
        >
          <div>{linePoints[0].distanceTo(linePoints[1]).toFixed(2)} m</div>
        </Html>
      )}
    </group>
  );
};

export default MeasurementTool;
