import { Add, Close } from "@mui/icons-material";
import { Box, IconButton } from "@mui/material";
import {
  cleardragQuestionContentId,
  setModalQuestionParentContentId,
  setOpenedModalQuestions,
  updateDeleteId,
  updateOpenedModalSettingsId,
} from "@root/uiTools/actions";
import { Core, EventObject, NodeSingular } from "cytoscape";
import {
  MutableRefObject,
  forwardRef,
  useEffect,
  useMemo,
  useRef,
} from "react";
import { createPortal } from "react-dom";
import {
  addNode,
  isElementANode,
  isNodeInViewport,
  isQuestionProhibited,
  storeToNodes,
} from "./helper";

const csButtonTypes = ["delete", "add", "settings", "select"] as const;

type CsButtonType = (typeof csButtonTypes)[number];

type CsNodeButtonsByType = Partial<
  Record<CsButtonType, HTMLButtonElement | null>
>;

type CsButtonsById = Record<string, CsNodeButtonsByType | undefined>;

interface Props {
  csElements: ReturnType<typeof storeToNodes>;
  cyRef: MutableRefObject<Core | null>;
}

export default function CsNodeButtons({ csElements, cyRef }: Props) {
  const buttonRefsById = useRef<CsButtonsById>({});

  const buttons = useMemo(() => {
    const nodeElements = csElements.filter(isElementANode);
    buttonRefsById.current = nodeElements.reduce<CsButtonsById>(
      (acc, node) => ((acc[node.data.id] = {}), acc),
      {},
    );
    return (
      <Box
        sx={{
          position: "absolute",
          top: 0,
          left: 0,
          width: "100%",
          height: "100%",
        }}
      >
        {nodeElements.flatMap((csElement) => [
          <CsDeleteButton
            key={`delete-${csElement.data.id}`}
            ref={(r) => {
              const buttonData = buttonRefsById.current[csElement.data.id];
              if (buttonData) buttonData.delete = r;
            }}
            onClick={() => {
              updateDeleteId(csElement.data.id);
            }}
          />,
          <CsAddButton
            key={`add-${csElement.data.id}`}
            ref={(r) => {
              const buttonData = buttonRefsById.current[csElement.data.id];
              if (buttonData) buttonData.add = r;
            }}
            onPointerUp={() => {
              addNode({ parentNodeContentId: csElement.data.id });
              cleardragQuestionContentId();
            }}
          />,
          !csElement.data.isRoot &&
            !isQuestionProhibited(csElement.data.qtype) && (
              <CsSettingsButton
                key={`settings-${csElement.data.id}`}
                ref={(r) => {
                  const buttonData = buttonRefsById.current[csElement.data.id];
                  if (buttonData) buttonData.settings = r;
                }}
                onClick={() => {
                  updateOpenedModalSettingsId(csElement.data.id);
                }}
              />
            ),
          //оболочка узла
          <CsSelectButton
            key={`select-${csElement.data.id}`}
            ref={(r) => {
              const buttonData = buttonRefsById.current[csElement.data.id];
              if (buttonData) buttonData.select = r;
            }}
            onClick={() => {
              setModalQuestionParentContentId(csElement.data.id);
              console.log("csElement ", csElement);
              setOpenedModalQuestions(
                !(
                  isQuestionProhibited(csElement.data.type) &&
                  csElement.data.children > 0
                ),
              );
            }}
          />,
        ])}
      </Box>
    );
  }, [csElements]);

  useEffect(function attachViewportHandler() {
    const cy = cyRef.current;
    if (!cy) return;

    let rafId: number = 0;
    function handleViewportChange(event: EventObject) {
      cancelAnimationFrame(rafId);
      rafId = requestAnimationFrame(() => {
        for (const nodeId in buttonRefsById.current) {
          const buttonsByType = buttonRefsById.current[nodeId];
          if (!buttonsByType) continue;

          const node = event.cy.$id(nodeId);
          if (!node) {
            console.warn("Could not find node for id:" + nodeId);
            continue;
          }

          for (const buttonType of csButtonTypes) {
            const button = buttonsByType[buttonType];
            if (!button) continue;

            applyButtonStyleByType[buttonType](button, node, event.cy.zoom());
          }
        }
      });
    }

    cy.on("viewport", handleViewportChange);

    return () => {
      cy.off("viewport", handleViewportChange);
    };
  }, []);

  const container = cyRef.current?.container();
  const buttonsPortal = container ? createPortal(buttons, container) : null;

  return buttonsPortal;
}

const applyButtonStyleByType: Record<
  CsButtonType,
  (button: HTMLButtonElement, node: NodeSingular, zoom: number) => void
> = {
  delete(button, node, zoom) {
    const nodePosition = node.renderedPosition();
    const shiftX =
      node.renderedWidth() / 2 - (CLOSE_BUTTON_WIDTH / 2 + 6) * zoom;
    const shiftY =
      node.renderedHeight() / 2 - (CLOSE_BUTTON_HEIGHT / 2 + 6) * zoom;

    if (!isNodeInViewport(node, 100)) {
      return button.style.setProperty("display", "none");
    }

    button.style.setProperty("display", "flex");
    button.style.setProperty("left", `${nodePosition.x}px`);
    button.style.setProperty("top", `${nodePosition.y}px`);
    button.style.setProperty(
      "transform",
      `translate3d(calc(-50% + ${shiftX}px), calc(-50% - ${shiftY}px), 0) scale(${zoom})`,
    );
  },
  add(button, node, zoom) {
    const nodePosition = node.renderedPosition();
    const shiftX = node.renderedWidth() / 2 + (ADD_BUTTON_WIDTH / 2) * zoom;

    if (!isNodeInViewport(node, 100)) {
      return button.style.setProperty("display", "none");
    }

    button.style.setProperty("display", "flex");
    button.style.setProperty("left", `${nodePosition.x}px`);
    button.style.setProperty("top", `${nodePosition.y}px`);
    button.style.setProperty(
      "transform",
      `translate3d(calc(-50% + ${shiftX}px), -50%, 0) scale(${zoom})`,
    );
  },
  settings(button, node, zoom) {
    const nodePosition = node.renderedPosition();
    const shiftX =
      -node.renderedWidth() / 2 - (SETTINGS_BUTTON_WIDTH / 2) * zoom;

    if (!isNodeInViewport(node, 100)) {
      return button.style.setProperty("display", "none");
    }

    button.style.setProperty("display", "flex");
    button.style.setProperty("left", `${nodePosition.x}px`);
    button.style.setProperty("top", `${nodePosition.y}px`);
    button.style.setProperty(
      "transform",
      `translate3d(calc(-50% + ${shiftX}px), -50%, 0) scale(${zoom})`,
    );
  },
  select(button, node, zoom) {
    const nodePosition = node.renderedPosition();

    if (!isNodeInViewport(node, 100)) {
      return button.style.setProperty("display", "none");
    }

    button.style.setProperty("display", "flex");
    button.style.setProperty("left", `${nodePosition.x}px`);
    button.style.setProperty("top", `${nodePosition.y}px`);
    button.style.setProperty(
      "transform",
      `translate3d(-50%, -50%, 0) scale(${zoom})`,
    );
  },
};

const CLOSE_BUTTON_WIDTH = 24;
const CLOSE_BUTTON_HEIGHT = CLOSE_BUTTON_WIDTH;

const CsDeleteButton = forwardRef<
  HTMLButtonElement,
  {
    onClick: () => void;
  }
>(({ onClick }, ref) => (
  <IconButton
    ref={ref}
    sx={{
      position: "absolute",
      width: CLOSE_BUTTON_WIDTH,
      height: CLOSE_BUTTON_HEIGHT,
      color: "white",
      backgroundColor: "rgba(154, 154, 175, 0.7)",
      borderRadius: "6px",
      borderTopRightRadius: "8px",
      p: 0,
      zIndex: 1,
      ":hover": {
        backgroundColor: "rgba(154, 154, 175, 0.9)",
      },
    }}
    onClick={onClick}
    onMouseDownCapture={(event) => event.stopPropagation()}
    onTouchStartCapture={(event) => event.stopPropagation()}
  >
    <Close sx={{ width: "16px", height: "16px" }} />
  </IconButton>
));

const ADD_BUTTON_WIDTH = 40;
const ADD_BUTTON_HEIGHT = ADD_BUTTON_WIDTH;

const CsAddButton = forwardRef<
  HTMLButtonElement,
  {
    onPointerUp: () => void;
  }
>(({ onPointerUp }, ref) => (
  <IconButton
    ref={ref}
    sx={{
      position: "absolute",
      width: ADD_BUTTON_WIDTH,
      height: ADD_BUTTON_HEIGHT,
      color: "rgba(154, 154, 175, 0.5)",
      backgroundColor: "#eeeff4",
      borderRadius: "6px",
      border: "1.5px dashed rgba(154, 154, 175, 0.5)",
      p: 0,
      zIndex: 1,
      ":hover": {
        backgroundColor: "#f9f9fc",
      },
    }}
    onPointerUp={onPointerUp}
    onMouseDownCapture={(event) => event.stopPropagation()}
    onTouchStartCapture={(event) => event.stopPropagation()}
  >
    <Add />
  </IconButton>
));

const SETTINGS_BUTTON_WIDTH = 70;
const SETTINGS_BUTTON_HEIGHT = 60;

const CsSettingsButton = forwardRef<
  HTMLButtonElement,
  {
    onClick: () => void;
  }
>(({ onClick }, ref) => (
  <IconButton
    ref={ref}
    sx={{
      position: "absolute",
      width: SETTINGS_BUTTON_WIDTH,
      height: SETTINGS_BUTTON_HEIGHT,
      borderRadius: "6px",
      zIndex: 1,
    }}
    onClick={onClick}
    onMouseDownCapture={(event) => event.stopPropagation()}
    onTouchStartCapture={(event) => event.stopPropagation()}
  >
    <svg viewBox="0 0 33 14" fill="none">
      <path
        d="M32 7L18 7M32 7L28.2879 11M32 7L28.2879 3"
        stroke="#7E2AEA"
        strokeWidth="1.5"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      <path
        fillRule="evenodd"
        clipRule="evenodd"
        d="M7.94882 1.13431C8.27877 1.20499 8.48562 1.51017 8.60097 1.82727C8.74184 2.21454 9.01459 2.55625 9.3994 2.77842C9.7475 2.9794 10.1345 3.05205 10.5056 3.01104C10.8406 2.97403 11.2052 3.02416 11.415 3.28791C11.7809 3.74777 12.0812 4.26207 12.302 4.81671C12.4425 5.16982 12.2499 5.54856 11.9934 5.82905C11.6982 6.15204 11.518 6.58208 11.518 7.05419C11.518 7.52631 11.6982 7.95634 11.9934 8.27933C12.2499 8.55983 12.4425 8.93857 12.302 9.29168C12.0813 9.84611 11.7811 10.3602 11.4155 10.82C11.2056 11.0838 10.8408 11.1339 10.5058 11.0969C10.1346 11.0558 9.74757 11.1284 9.3994 11.3295C9.01448 11.5517 8.74169 11.8935 8.60085 12.2809C8.48553 12.5981 8.27866 12.9034 7.94863 12.9741C7.53788 13.0621 7.11169 13.1084 6.67467 13.1084C6.23796 13.1084 5.81204 13.0621 5.40155 12.9743C5.07144 12.9036 4.86452 12.5983 4.74919 12.281C4.60836 11.8936 4.33556 11.5517 3.95062 11.3295C3.60233 11.1284 3.21513 11.0558 2.84388 11.0969C2.50874 11.134 2.14388 11.084 1.93398 10.8201C1.56827 10.3603 1.26803 9.84615 1.04738 9.29168C0.906851 8.93856 1.09947 8.55983 1.3559 8.27933C1.65119 7.95634 1.83132 7.52631 1.83132 7.05419C1.83132 6.58208 1.65119 6.15204 1.3559 5.82905C1.09947 5.54856 0.906851 5.16982 1.04738 4.81671C1.26812 4.26203 1.56849 3.74768 1.93439 3.28779C2.14427 3.02399 2.50902 2.97391 2.84408 3.011C3.21527 3.0521 3.6024 2.97947 3.95062 2.77842C4.33545 2.55624 4.60821 2.2145 4.74908 1.8272C4.86444 1.51003 5.07134 1.20477 5.40136 1.13413C5.81191 1.04625 6.23789 1 6.67467 1C7.11175 1 7.53802 1.04632 7.94882 1.13431Z"
        fill="white"
      />
      <path
        d="M9.3994 2.77842L9.0244 3.42794L9.3994 2.77842ZM9.3994 11.3295L9.0244 10.6799L9.3994 11.3295ZM3.95062 11.3295L3.57562 11.979L3.57562 11.979L3.95062 11.3295ZM3.95062 2.77842L4.32562 3.42794L3.95062 2.77842ZM1.3559 5.82905L0.802362 6.33511L1.3559 5.82905ZM4.74919 12.281L4.04432 12.5372L4.74919 12.281ZM8.60085 12.2809L9.30572 12.5372L8.60085 12.2809ZM7.94863 12.9741L7.79157 12.2407L7.94863 12.9741ZM11.4155 10.82L10.8285 10.3531L11.4155 10.82ZM10.5058 11.0969L10.4233 11.8423L10.5058 11.0969ZM1.3559 8.27933L0.802363 7.77328L1.3559 8.27933ZM11.415 3.28791L12.002 2.82097L11.415 3.28791ZM1.93439 3.28779L1.34748 2.82084L1.93439 3.28779ZM11.9934 8.27933L11.4399 8.78539L11.9934 8.27933ZM12.302 9.29168L12.9988 9.569L12.302 9.29168ZM8.60097 1.82727L7.89615 2.08365L8.60097 1.82727ZM7.89615 2.08365C8.09506 2.63048 8.48123 3.11434 9.0244 3.42794L9.7744 2.1289C9.54795 1.99816 9.38861 1.79859 9.30578 1.57089L7.89615 2.08365ZM9.0244 3.42794C9.51547 3.71146 10.0635 3.81446 10.5879 3.75651L10.4232 2.26558C10.2055 2.28963 9.97954 2.24734 9.7744 2.1289L9.0244 3.42794ZM12.9988 4.53939C12.7506 3.91555 12.4129 3.3375 12.002 2.82097L10.8281 3.75484C11.1489 4.15803 11.4119 4.60859 11.6051 5.09402L12.9988 4.53939ZM12.268 7.05419C12.268 6.77661 12.3731 6.52529 12.547 6.33511L11.4399 5.323C11.0232 5.7788 10.768 6.38756 10.768 7.05419H12.268ZM12.547 7.77328C12.3731 7.5831 12.268 7.33178 12.268 7.05419H10.768C10.768 7.72083 11.0232 8.32959 11.4399 8.78539L12.547 7.77328ZM12.0024 11.2868C12.4132 10.7704 12.7506 10.1926 12.9988 9.569L11.6051 9.01436C11.412 9.49961 11.1491 9.95002 10.8285 10.3531L12.0024 11.2868ZM9.7744 11.979C9.97958 11.8605 10.2056 11.8182 10.4233 11.8423L10.5882 10.3514C10.0637 10.2934 9.51556 10.3964 9.0244 10.6799L9.7744 11.979ZM9.30572 12.5372C9.38853 12.3094 9.54788 12.1098 9.7744 11.979L9.0244 10.6799C8.48108 10.9936 8.09485 11.4777 7.89598 12.0247L9.30572 12.5372ZM6.67467 13.8584C7.1648 13.8584 7.64364 13.8064 8.10569 13.7075L7.79157 12.2407C7.43213 12.3177 7.05857 12.3584 6.67467 12.3584V13.8584ZM5.24459 13.7077C5.70635 13.8065 6.18487 13.8584 6.67467 13.8584V12.3584C6.29104 12.3584 5.91773 12.3178 5.55851 12.2409L5.24459 13.7077ZM3.57562 11.979C3.80215 12.1098 3.96151 12.3094 4.04432 12.5372L5.45407 12.0248C5.25522 11.4777 4.86897 10.9936 4.32562 10.6799L3.57562 11.979ZM2.92649 11.8423C3.14427 11.8182 3.37038 11.8605 3.57562 11.979L4.32562 10.6799C3.83429 10.3963 3.28599 10.2933 2.76126 10.3515L2.92649 11.8423ZM0.350529 9.569C0.598719 10.1927 0.936243 10.7705 1.34702 11.287L2.52093 10.3532C2.20029 9.9501 1.93735 9.49965 1.74422 9.01436L0.350529 9.569ZM1.08132 7.05419C1.08132 7.33178 0.976228 7.5831 0.802363 7.77328L1.90944 8.78539C2.32614 8.32959 2.58132 7.72083 2.58132 7.05419H1.08132ZM0.802362 6.33511C0.976229 6.52529 1.08132 6.77661 1.08132 7.05419H2.58132C2.58132 6.38756 2.32614 5.7788 1.90944 5.323L0.802362 6.33511ZM1.34748 2.82084C0.936496 3.33741 0.598811 3.9155 0.350529 4.53939L1.74422 5.09402C1.93742 4.60855 2.20049 4.15796 2.52129 3.75474L1.34748 2.82084ZM3.57562 2.1289C3.37041 2.24738 3.14435 2.28966 2.9266 2.26556L2.76155 3.75645C3.28619 3.81453 3.83438 3.71155 4.32562 3.42794L3.57562 2.1289ZM4.04425 1.57085C3.96142 1.79857 3.80208 1.99815 3.57562 2.1289L4.32562 3.42794C4.86882 3.11433 5.255 2.63042 5.4539 2.08356L4.04425 1.57085ZM6.67467 0.25C6.1848 0.25 5.70621 0.301891 5.24438 0.400745L5.55835 1.86752C5.91761 1.79062 6.29098 1.75 6.67467 1.75V0.25ZM8.10591 0.400947C7.64379 0.301962 7.16488 0.25 6.67467 0.25V1.75C7.05863 1.75 7.43224 1.79067 7.79173 1.86768L8.10591 0.400947ZM5.4539 2.08356C5.49103 1.98147 5.53206 1.91361 5.56318 1.87745C5.59007 1.84621 5.58888 1.86098 5.55835 1.86752L5.24438 0.400745C4.5225 0.555264 4.18465 1.18482 4.04425 1.57085L5.4539 2.08356ZM1.90944 5.323C1.82598 5.23171 1.77792 5.15014 1.75839 5.09591C1.74167 5.04952 1.75743 5.06084 1.74422 5.09402L0.350529 4.53939C0.0415841 5.31572 0.499907 6.00428 0.802362 6.33511L1.90944 5.323ZM2.76126 10.3515C2.65345 10.3634 2.57451 10.3567 2.52826 10.3449C2.48826 10.3346 2.50172 10.329 2.52093 10.3532L1.34702 11.287C1.80572 11.8636 2.51822 11.8876 2.92649 11.8423L2.76126 10.3515ZM5.55851 12.2409C5.58905 12.2474 5.59024 12.2622 5.56335 12.231C5.53222 12.1948 5.49119 12.1269 5.45407 12.0248L4.04432 12.5372C4.18467 12.9233 4.52252 13.5532 5.24459 13.7077L5.55851 12.2409ZM7.89598 12.0247C7.85887 12.1268 7.81785 12.1946 7.78673 12.2308C7.75984 12.2621 7.76103 12.2473 7.79157 12.2407L8.10569 13.7075C8.82759 13.5529 9.16537 12.9232 9.30572 12.5372L7.89598 12.0247ZM10.8285 10.3531C10.8477 10.3289 10.8612 10.3345 10.8212 10.3448C10.7749 10.3566 10.696 10.3633 10.5882 10.3514L10.4233 11.8423C10.8315 11.8875 11.5438 11.8634 12.0024 11.2868L10.8285 10.3531ZM11.6051 5.09402C11.5919 5.06084 11.6077 5.04952 11.591 5.09591C11.5714 5.15014 11.5234 5.23171 11.4399 5.323L12.547 6.33511C12.8494 6.00428 13.3078 5.31572 12.9988 4.53939L11.6051 5.09402ZM1.74422 9.01436C1.75743 9.04755 1.74167 9.05887 1.75839 9.01247C1.77792 8.95825 1.82598 8.87668 1.90944 8.78539L0.802363 7.77328C0.499907 8.10411 0.0415843 8.79267 0.350529 9.569L1.74422 9.01436ZM10.5879 3.75651C10.6957 3.7446 10.7746 3.75129 10.8208 3.76315C10.8608 3.77341 10.8473 3.77898 10.8281 3.75484L12.002 2.82097C11.5434 2.24462 10.8313 2.22048 10.4232 2.26558L10.5879 3.75651ZM2.52129 3.75474C2.50208 3.77889 2.48863 3.7733 2.52862 3.76305C2.57485 3.75119 2.65376 3.74452 2.76155 3.75645L2.9266 2.26556C2.51841 2.22037 1.80612 2.24437 1.34748 2.82084L2.52129 3.75474ZM11.4399 8.78539C11.5234 8.87668 11.5714 8.95825 11.591 9.01247C11.6077 9.05887 11.5919 9.04755 11.6051 9.01436L12.9988 9.569C13.3078 8.79267 12.8494 8.10411 12.547 7.77328L11.4399 8.78539ZM9.30578 1.57089C9.16539 1.18493 8.82761 0.555536 8.10591 0.400947L7.79173 1.86768C7.76121 1.86114 7.76002 1.84637 7.7869 1.87761C7.81801 1.91376 7.85902 1.98159 7.89615 2.08365L9.30578 1.57089Z"
        fill="#7E2AEA"
      />
      <circle
        cx="1.81626"
        cy="1.81626"
        r="1.81626"
        transform="matrix(-1 0 0 1 8.49219 5.23438)"
        fill="#DDDCE7"
        stroke="#7E2AEA"
        strokeWidth="1.5"
      />
    </svg>
  </IconButton>
));

const SELECT_BUTTON_WIDTH = 130;
const SELECT_BUTTON_HEIGHT = SELECT_BUTTON_WIDTH;

const CsSelectButton = forwardRef<
  HTMLButtonElement,
  {
    onClick: () => void;
  }
>(({ onClick }, ref) => (
  <IconButton
    ref={ref}
    sx={{
      position: "absolute",
      width: SELECT_BUTTON_WIDTH,
      height: SELECT_BUTTON_HEIGHT,
      backgroundColor: "rgb(0 0 0 / 0)",
      borderRadius: "8px",
      p: 0,
      zIndex: 0,
    }}
    onClick={onClick}
    onMouseDownCapture={(event) => event.stopPropagation()}
    onTouchStartCapture={(event) => event.stopPropagation()}
  />
));
