import React, { FC, useCallback, useEffect, useRef, useState } from "react";
import { IElementType } from "../../definitions/element";
import {
  getLabelCordX,
  getLabelCordY,
  getLineArc,
  getLineLength,
} from "../../helpers/mathHelpers";
import { validatePosition } from "../../helpers/pdfHelpers";
import { ItemPoint, PointLabel, PointLine } from "./Point.styles";
import { useStoreState } from "../../store";

interface PointProps {
  element: IElementType;
  scale: number;
  onClick?: any;
  active: boolean;
  showDetails: (
    id: string,
    isMapeElement: boolean,
    isReference: boolean
  ) => void;
  saveChanges: (element: IElementType, isSource: boolean) => void;
  pdfHeight?: number;
  editMode: boolean;
  pdfWidth?: number;
  handleShortcut: (key: string, elementID: string, elementType: string) => void;
  focused: boolean;
}

export const getCordinates = (dimension: number) => {
  return dimension - 2;
};

const Point: FC<PointProps> = ({
  element,
  onClick,
  active,
  scale,
  showDetails,
  saveChanges,
  editMode,
  pdfHeight = 0,
  pdfWidth = 0,
  handleShortcut,
  focused,
}: PointProps) => {
  interface IPointsData {
    move: boolean;
    alt: boolean;
    x: number;
    y: number;
    editMode: boolean;
    label_x: number;
    label_y: number;
    pageHeight: number;
    pageWidth: number;
    lock: boolean;
    scale: number;
  }
  const { orgRole } = useStoreState((state) => state.user.userDetails);
  const pointRef = useRef<HTMLDivElement>(null);

  const [startPos, setStartPos] = useState({ x: 0, y: 0 });
  const [pointPos, setPointPos] = useState({
    left: 0,
    top: 0,
    labelTop: 0,
    labelLeft: 0,
  });

  const [pointsData, setPointsData] = useState<IPointsData>({
    move: false,
    alt: false,
    editMode: editMode,
    x: element.x,
    y: element.y,
    label_x: element.label_x,
    label_y: element.label_y,
    pageHeight: pdfHeight,
    pageWidth: pdfWidth,
    lock: element.lock,
    scale: scale,
  });

  const updateElements = useCallback(() => {
    saveChanges(
      {
        ...element,
        x: pointsData.x,
        y: pointsData.y,
        label_x: pointsData.label_x,
        label_y: pointsData.label_y,
      },
      false
    );
  }, [element, pointsData, saveChanges]);

  const setAtcive = (e: any) => {
    e.stopPropagation();
    showDetails(element.id, false, element.sourceElementID !== undefined);
  };

  const startMove = useCallback(
    (event: MouseEvent) => {
      event.stopPropagation();
      showDetails(element.id, false, element.sourceElementID !== undefined);

      if (pointsData.lock) return;
      if (orgRole !== "readOnly") {
        setStartPos({ x: event.clientX, y: event.clientY });
        setPointPos({
          left: pointsData.x * pointsData.scale,
          top: pointsData.y * pointsData.scale,
          labelLeft: pointsData.label_x * pointsData.scale,
          labelTop: pointsData.label_y * pointsData.scale,
        });
        setPointsData((prev) => ({
          ...prev,
          alt: event.altKey,
          move: true,
        }));
      }
    },
    [active, showDetails, element, scale]
  );

  const move = useCallback(
    (event: MouseEvent) => {
      if (!pointsData.move || element.lock) return;
      event.stopPropagation();
      if (orgRole !== "readOnly") {
        const newDelta = {
          left: event.clientX - startPos.x,
          top: event.clientY - startPos.y,
        };

        if (
          newDelta.left !== 0 &&
          newDelta.top !== 0 &&
          event.clientX !== 0 &&
          event.clientY !== 0
        ) {
          if (pointsData.alt) {
            setPointsData((prev) => ({
              ...prev,
              label_x: (pointPos.labelLeft + newDelta.left) / pointsData.scale,
              label_y: (pointPos.labelTop + newDelta.top) / pointsData.scale,
            }));
          } else {
            setPointsData((prev) => ({
              ...prev,
              x: (pointPos.left + newDelta.left) / pointsData.scale,
              y: (pointPos.top + newDelta.top) / pointsData.scale,
            }));
          }
        }
      }
    },
    [pointsData.move, pointsData.alt, showDetails, active, scale, pointRef]
  );

  const endMove = useCallback(
    (event: MouseEvent) => {
      if (!pointsData.move || pointsData.lock) return;
      event.stopPropagation();
      if (orgRole !== "readOnly") {
        setPointsData((prev) => ({
          ...prev,
          move: false,
          alt: false,
        }));

        updateElements();
      }
    },
    [pointsData.move, updateElements]
  );
  const shortScope = [
    "e",
    "E",
    "x",
    "X",
    "r",
    "R",
    "d",
    "D",
    "r",
    "R",
    "Delete",
    "Backspace",
  ];

  useEffect(() => {
    const point = pointRef.current;
    const handleKeyDown = (e: KeyboardEvent) => {
      if (orgRole !== "readOnly") {
        if (active && !editMode) {
          if (shortScope.includes(e.key)) {
            let action = "";
            if (e.key === "e" || e.key === "E") {
              action = "edit";
            } else if (e.key === "d" || e.key === "D") {
              action = "duplicate";
            } else if (e.key === "r" || e.key === "R") {
              if (!e.shiftKey) {
                action = "setReference";
              }
            } else if (
              e.key === "x" ||
              e.key === "X" ||
              e.key === "Backspace" ||
              e.key === "Delete"
            ) {
              action = "delete";
            }
            if (action !== "") {
              handleShortcut(
                action,
                element.id,
                element.sourceElementID !== undefined ? "ref" : "joint"
              );
            }
          }
        }
      }
    };
    if (!point) return;
    // if (point && active) {
    point.addEventListener("mousedown", startMove);
    window.addEventListener("mousemove", move);
    window.addEventListener("mouseup", endMove);
    window.addEventListener("mouseleave", endMove);
    document.addEventListener("keydown", handleKeyDown);
    // }
    return () => {
      point.removeEventListener("mousedown", startMove);
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseup", endMove);
      window.removeEventListener("mouseleave", endMove);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [startMove, move, endMove]);

  useEffect(() => {
    setPointsData((prev) => ({
      ...prev,
      x: element.x,
      y: element.y,
      label_x: element.label_x,
      label_y: element.label_y,
      pageHeight: pdfHeight,
      pageWidth: pdfWidth,
      scale: scale,
      editMode: editMode,
    }));
  }, [element, pdfHeight, pdfWidth, scale, editMode]);

  return (
    <ItemPoint
      onClick={onClick}
      active={active || focused}
      isEditMode={pointsData.editMode}
      style={{
        left: getCordinates(pointsData.x * pointsData.scale) + "px",
        top: getCordinates(pointsData.y * pointsData.scale) + "px",
      }}
    >
      <PointLine
        active={active || focused}
        isEditMode={pointsData.editMode}
        style={{
          height:
            getLineLength(
              pointsData.label_x * pointsData.scale,
              pointsData.label_y * pointsData.scale
            ) + "px",
          transform: `rotate(${getLineArc(
            pointsData.label_x * pointsData.scale,
            pointsData.label_y * pointsData.scale
          )}deg)`,
        }}
      />
      <PointLabel
        ref={pointRef}
        isEditMode={pointsData.editMode}
        active={active || focused}
        label={element.joint_name}
        move={pointsData.move}
        scale={pointsData.scale}
        isSquere={false}
        // onClick={(e) => setAtcive(e)}
        style={{
          left:
            getLabelCordX(pointsData.label_x, pointsData.scale, false) + "px",
          top:
            getLabelCordY(pointsData.label_y, pointsData.scale, false) + "px",
        }}
      />
    </ItemPoint>
  );
};

export default Point;
