import * as React from "react";
import { get, has } from "lodash";
import { DndProvider, useDragLayer } from "react-dnd";
import Backend from "react-dnd-html5-backend";
import { useDrag } from "react-dnd";
import { useKeyPress } from "@sc/plugins/utilities";
import { useState, useEffect } from "react";

import {
  generateStyles,
  getObjectFromStyle,
  containerStyle,
  innerContainerStyle,
  Trapezoid,
} from "./style";

import {
  SpacingProps,
  commandType,
  SquareBlockProps,
  styleTypes,
  SquareBlockStyles,
  IKeysPressed,
  ICompass,
} from "./types";

const getShouldBeActivated = (
  action: commandType,
  isKeyPressed: IKeysPressed
): ICompass => {
  if (isKeyPressed.shiftKey) {
    return {
      top: true,
      right: true,
      bottom: true,
      left: true,
    };
  }

  if (isKeyPressed.commandKey || isKeyPressed.controlKey) {
    if (action === commandType.TOP || action === commandType.BOTTOM) {
      return {
        top: true,
        right: false,
        bottom: true,
        left: false,
      };
    }
    if (action === commandType.LEFT || action === commandType.RIGHT) {
      return {
        top: false,
        right: true,
        bottom: false,
        left: true,
      };
    }
  }

  return {
    top: action === commandType.TOP,
    right: action === commandType.RIGHT,
    bottom: action === commandType.BOTTOM,
    left: action === commandType.LEFT,
  };
};

const SqueryBlock: React.FC<SquareBlockProps> = ({ style, onChange }) => {
  const [isMarginHovered, setIsMarginHovered] = React.useState<any[]>([
    false,
    null,
  ]);

  const [isPaddingHovered, setIsPaddingHovered] = React.useState<any[]>([
    false,
    null,
  ]);

  const [originalStyle, setOriginalStyle] = React.useState(style);

  const marginStyles: SquareBlockStyles = generateStyles({
    clipSmallPercentage: 20,
    clipBigPercentage: 80,
  });

  const paddingStyles: SquareBlockStyles = generateStyles({
    clipSmallPercentage: 34,
    clipBigPercentage: 66,
  });

  const isKeyPressed: IKeysPressed = {
    shiftKey: useKeyPress("Shift"),
    commandKey: useKeyPress("Meta"),
    altKey: useKeyPress("Alt"),
    controlKey: useKeyPress("Control"),
  };

  const [mT, dragRefMarginTop, previewRefMarginTop] = useDrag({
    item: { type: styleTypes.MARGIN, command: commandType.TOP },
    end: () => setOriginalStyle(style),
  });
  const [mR, dragRefMarginRight, previewRefMarginRight] = useDrag({
    item: { type: styleTypes.MARGIN, command: commandType.RIGHT },
    end: () => setOriginalStyle(style),
  });
  const [mB, dragRefMarginBottom, previewRefMarginBottom] = useDrag({
    item: { type: styleTypes.MARGIN, command: commandType.BOTTOM },
    end: () => setOriginalStyle(style),
  });
  const [mL, dragRefMarginLeft, previewRefMarginLeft] = useDrag({
    item: { type: styleTypes.MARGIN, command: commandType.LEFT },
    end: () => setOriginalStyle(style),
  });

  const [mp, dragRefPaddingTop, previewRefPaddingTop] = useDrag({
    item: { type: styleTypes.PADDING, command: commandType.TOP },
    end: () => setOriginalStyle(style),
  });
  const [pR, dragRefPaddingRight, previewRefPaddingRight] = useDrag({
    item: { type: styleTypes.PADDING, command: commandType.RIGHT },
    end: () => setOriginalStyle(style),
  });
  const [pB, dragRefPaddingBottom, previewRefPaddingBottom] = useDrag({
    item: { type: styleTypes.PADDING, command: commandType.BOTTOM },
    end: () => setOriginalStyle(style),
  });
  const [pL, dragRefPaddingLeft, previewRefPaddingLeft] = useDrag({
    item: { type: styleTypes.PADDING, command: commandType.LEFT },
    end: () => setOriginalStyle(style),
  });

  const { currentOffset } = useDragLayer((monitor) => {
    const currentOffset = monitor.getDifferenceFromInitialOffset();
    const item = monitor.getItem();

    if (
      has(currentOffset, "y") &&
      has(currentOffset, "x") &&
      has(item, "command") &&
      has(item, "type")
    ) {
      if (item.command === commandType.TOP) {
        onChange(
          item.type,
          commandType.TOP,
          {
            ...originalStyle,
            top:
              Number(get(originalStyle, "marginTop", 0)) +
              Number(currentOffset.y),
          },
          isKeyPressed
        );
      }

      if (item.command === commandType.RIGHT) {
        onChange(
          item.type,
          commandType.RIGHT,
          {
            ...originalStyle,
            right:
              Number(get(originalStyle, "marginRight", 0)) +
              Number(currentOffset.x),
          },
          isKeyPressed
        );
      }

      if (item.command === commandType.BOTTOM) {
        onChange(
          item.type,
          commandType.BOTTOM,
          {
            ...originalStyle,
            bottom:
              Number(get(originalStyle, "marginBottom", 0)) +
              Number(currentOffset.y),
          },
          isKeyPressed
        );
      }

      if (item.command === commandType.LEFT) {
        onChange(
          item.type,
          commandType.LEFT,
          {
            ...originalStyle,
            left:
              Number(get(originalStyle, "marginLeft", 0)) +
              Number(currentOffset.x),
          },
          isKeyPressed
        );
      }
    }

    return { currentOffset };
  });

  const marginIncomingStyle = getObjectFromStyle(styleTypes.MARGIN, style);
  const paddingIncomingStyle = getObjectFromStyle(styleTypes.PADDING, style);

  const highlightMarginTop = getShouldBeActivated(
    isMarginHovered[1],
    isKeyPressed
  ).top;
  const highlightMarginRight = getShouldBeActivated(
    isMarginHovered[1],
    isKeyPressed
  ).right;
  const highlightMarginBottom = getShouldBeActivated(
    isMarginHovered[1],
    isKeyPressed
  ).bottom;
  const highlightMarginLeft = getShouldBeActivated(
    isMarginHovered[1],
    isKeyPressed
  ).left;

  const highlightPaddingTop = getShouldBeActivated(
    isPaddingHovered[1],
    isKeyPressed
  ).top;
  const highlightPaddingRight = getShouldBeActivated(
    isPaddingHovered[1],
    isKeyPressed
  ).right;
  const highlightPaddingBottom = getShouldBeActivated(
    isPaddingHovered[1],
    isKeyPressed
  ).bottom;
  const highlightPaddingLeft = getShouldBeActivated(
    isPaddingHovered[1],
    isKeyPressed
  ).left;

  const marginTopLayer = {
    ...marginStyles.topLayer,
    padding: "10px 22px",
    ...(isMarginHovered[0] &&
      highlightMarginTop && { opacity: 0.35, backgroundColor: "#1c2b3a" }),
  };
  const marginRightLayer = {
    ...marginStyles.rightLayer,
    padding: "10px 22px",
    ...(isMarginHovered[0] &&
      highlightMarginRight && { opacity: 0.35, backgroundColor: "#1c2b3a" }),
  };
  const marginBottomLayer = {
    ...marginStyles.bottomLayer,
    padding: "10px 22px",
    ...(isMarginHovered[0] &&
      highlightMarginBottom && { opacity: 0.35, backgroundColor: "#1c2b3a" }),
  };
  const marginLeftLayer = {
    ...marginStyles.leftLayer,
    padding: "10px 22px",
    ...(isMarginHovered[0] &&
      highlightMarginLeft && { opacity: 0.35, backgroundColor: "#1c2b3a" }),
  };

  const paddingTopLayer = {
    ...paddingStyles.topLayer,
    padding: "7px 0",
    ...(isPaddingHovered[0] &&
      highlightPaddingTop && { opacity: 0.35, backgroundColor: "#1c2b3a" }),
  };
  const paddingRightLayer = {
    ...paddingStyles.rightLayer,
    padding: "7px 0",
    ...(isPaddingHovered[0] &&
      highlightPaddingRight && { opacity: 0.35, backgroundColor: "#1c2b3a" }),
  };
  const paddingBottomLayer = {
    ...paddingStyles.bottomLayer,
    padding: "7px 0",
    ...(isPaddingHovered[0] &&
      highlightPaddingBottom && { opacity: 0.35, backgroundColor: "#1c2b3a" }),
  };
  const paddingLeftLayer = {
    ...paddingStyles.leftLayer,
    padding: "7px 0",
    ...(isPaddingHovered[0] &&
      highlightPaddingLeft && { opacity: 0.35, backgroundColor: "#1c2b3a" }),
  };

  // const useThisRef = { [styleType]: dragRefMarginTop };

  return (
    <div style={{ outline: "none" }}>
      <span style={marginStyles.label}>Margin</span>

      {[
        previewRefMarginTop,
        previewRefMarginRight,
        previewRefMarginBottom,
        previewRefMarginLeft,
      ].map((itm, k) => (
        <span key={k} style={marginStyles.preview} ref={itm} />
      ))}
      {[
        previewRefPaddingTop,
        previewRefPaddingRight,
        previewRefPaddingBottom,
        previewRefPaddingLeft,
      ].map((itm, k) => (
        <span key={k} style={marginStyles.preview} ref={itm} />
      ))}

      <Trapezoid
        onMouseEnter={() => setIsMarginHovered([true, commandType.RIGHT])}
        onMouseLeave={() => setIsMarginHovered([false, commandType.RIGHT])}
        style={marginRightLayer}
        ref={dragRefMarginRight}
      >
        {marginIncomingStyle.right}
      </Trapezoid>
      <Trapezoid
        onMouseEnter={() => setIsMarginHovered([true, commandType.LEFT])}
        onMouseLeave={() => setIsMarginHovered([false, commandType.LEFT])}
        style={marginLeftLayer}
        ref={dragRefMarginLeft}
      >
        {marginIncomingStyle.left}
      </Trapezoid>
      <div style={innerContainerStyle}>
        <span style={marginStyles.label}>Padding</span>
        <Trapezoid
          onMouseEnter={() => setIsPaddingHovered([true, commandType.RIGHT])}
          onMouseLeave={() => setIsPaddingHovered([false, commandType.RIGHT])}
          style={paddingRightLayer}
          ref={dragRefPaddingRight}
        >
          {paddingIncomingStyle.right}
        </Trapezoid>
        <Trapezoid
          onMouseEnter={() => setIsPaddingHovered([true, commandType.LEFT])}
          onMouseLeave={() => setIsPaddingHovered([false, commandType.LEFT])}
          style={paddingLeftLayer}
          ref={dragRefPaddingLeft}
        >
          {paddingIncomingStyle.left}
        </Trapezoid>
        <Trapezoid
          onMouseEnter={() => setIsPaddingHovered([true, commandType.TOP])}
          onMouseLeave={() => setIsPaddingHovered([false, commandType.TOP])}
          style={paddingTopLayer}
          ref={dragRefPaddingTop}
        >
          {paddingIncomingStyle.top}
        </Trapezoid>
        <Trapezoid
          onMouseEnter={() => setIsPaddingHovered([true, commandType.BOTTOM])}
          onMouseLeave={() => setIsPaddingHovered([false, commandType.BOTTOM])}
          style={paddingBottomLayer}
          ref={dragRefPaddingBottom}
        >
          {paddingIncomingStyle.bottom}
        </Trapezoid>
      </div>
      <Trapezoid
        onMouseEnter={() => setIsMarginHovered([true, commandType.TOP])}
        onMouseLeave={() => setIsMarginHovered([false, commandType.TOP])}
        style={marginTopLayer}
        ref={dragRefMarginTop}
      >
        {marginIncomingStyle.top}
      </Trapezoid>
      <Trapezoid
        onMouseEnter={() => setIsMarginHovered([true, commandType.BOTTOM])}
        onMouseLeave={() => setIsMarginHovered([false, commandType.BOTTOM])}
        style={marginBottomLayer}
        ref={dragRefMarginBottom}
      >
        {marginIncomingStyle.bottom}
      </Trapezoid>
    </div>
  );
};

/**
 * A property component that let's the user define the spacing (margin and padding) for an object
 *
 * Returns a modified version of the provided style object based on how the user clicks and drags the padding or margin controls
 *
 * User can apply their changes to all sides by holding down the [shift] key as they drag, or apply to the opposite side when the [ctrl] or [cmd] key is held
 *
 */
export const Spacing: React.FC<SpacingProps> = ({ style, onChange }) => {
  const handleChange = (
    type: styleTypes,
    action: commandType,
    newStyle: React.CSSProperties,
    isKeyPressed: IKeysPressed
  ) => {
    if (newStyle) {
      let value = get(newStyle, `${action.toLowerCase()}`, 0);
      if (type === styleTypes.PADDING && value < 0) value = 0;

      let modifiedStyle = {};

      const shouldBeActivated = getShouldBeActivated(action, isKeyPressed);
      if (shouldBeActivated.top) modifiedStyle[`${type}Top`] = value;
      if (shouldBeActivated.right) modifiedStyle[`${type}Right`] = value;
      if (shouldBeActivated.bottom) modifiedStyle[`${type}Bottom`] = value;
      if (shouldBeActivated.left) modifiedStyle[`${type}Left`] = value;

      // console.log(style, modifiedStyle);

      onChange({
        ...style,
        ...modifiedStyle,
      });
    }
  };

  return (
    <DndProvider backend={Backend} data-testid="Properties-Spacing">
      <div style={{ ...containerStyle, boxShadow: "0 0 20px rgb(0,0,0,0.25)" }}>
        <SqueryBlock style={style} onChange={handleChange} />
      </div>
    </DndProvider>
  );
};

export default Spacing;
