import React, { useEffect, useState } from 'react';

import theme from '../../helpers/theme';
import useCursorPosition from '../../hooks/useCursorPosition';
import { getAbsoluteCoordinate } from '../../helpers/position';
import { calcDragAndDropScale } from '../../helpers/utility';

import generateCirclesData from './data';
import { SVG, Rect, Circle } from './styled';

type Props = {
  left: number;
  top: number;
  width: number;
  height: number;
  onMove?: (moveCoord: CoordType) => void;
  onScale?: (scaleCoord: CoordType) => void;
  onClick?: (event: React.MouseEvent) => void;
  onDoubleClick?: () => void;
  onMouseUp?: () => void;
};

type ObjPositionType = {
  top: number;
  left: number;
  width: number;
  height: number;
  operator: string;
  clickX: number;
  clickY: number;
};

const initState = {
  top: 0,
  left: 0,
  width: 0,
  height: 0,
  operator: '',
  clickX: 0,
  clickY: 0,
};

const OuterRect: React.FC<Props> = ({
  left,
  top,
  width,
  height,
  onMove,
  onScale,
  onClick,
  onDoubleClick,
  onMouseUp,
}: Props) => {
  const data = generateCirclesData(width, height);
  const [state, setState] = useState(initState);
  const [cursorPosition, setRef] = useCursorPosition(25);

  const handleMouseDown = (e: React.MouseEvent | React.TouchEvent): void => {
    e.preventDefault();
    const operatorId = (e.target as HTMLElement).getAttribute(
      'data-id',
    ) as string;
    const coord = getAbsoluteCoordinate(document.body, e);

    setRef(document.body);
    setState({
      top,
      left,
      width,
      height,
      operator: operatorId,
      clickX: coord.x,
      clickY: coord.y,
    });
  };

  const handleMouseUp = (): void => {
    setRef(null);
    setState(initState);
    if (onMouseUp) {
      onMouseUp();
    }
  };

  const calcMoveResult = (
    currentPosition: PointType,
    startPosition: PointType,
    objPosition: ObjPositionType,
  ): CoordType => ({
    left: currentPosition.x - (startPosition.x - objPosition.left),
    top: currentPosition.y - (startPosition.y - objPosition.top),
    width: objPosition.width,
    height: objPosition.height,
  });

  const calcScaleResult = (
    currentPosition: PointType,
    objPosition: ObjPositionType,
  ): CoordType => {
    const scaleData = calcDragAndDropScale({
      ...objPosition,
      moveX: currentPosition.x || 0,
      moveY: currentPosition.y || 0,
    });
    const scaleWidth = scaleData.width || 0;
    const scaleHeight = scaleData.height || 0;

    const maxTop = objPosition.top + objPosition.height;
    const maxLeft = objPosition.left + objPosition.width;
    scaleData.left = scaleData.left > maxLeft ? maxLeft : scaleData.left;
    scaleData.top = scaleData.top > maxTop ? maxTop : scaleData.top;
    scaleData.width = scaleWidth > 0 ? scaleWidth : 0;
    scaleData.height = scaleHeight > 0 ? scaleHeight : 0;
    return scaleData;
  };

  useEffect(() => {
    if (cursorPosition.x && cursorPosition.y && state.clickX) {
      if (state.operator === 'move' && onMove) {
        onMove(
          calcMoveResult(
            { x: cursorPosition.x, y: cursorPosition.y },
            { x: state.clickX, y: state.clickY },
            state,
          ),
        );
      } else if (onScale) {
        onScale(
          calcScaleResult(
            {
              x: cursorPosition.x,
              y: cursorPosition.y,
            },
            state,
          ),
        );
      }
    }
  }, [cursorPosition, state]);

  useEffect(() => {
    window.addEventListener('mouseup', handleMouseUp);
    window.addEventListener('touchend', handleMouseUp);

    return (): void => {
      window.removeEventListener('mouseup', handleMouseUp);
      window.removeEventListener('touchend', handleMouseUp);
    };
  }, []);

  return (
    <SVG
      style={{
        left: `${left - 12}px`,
        top: `${top - 12}px`,
        width: `${width + 24}px`,
        height: `${height + 24}px`,
      }}
      onClick={onClick}
      onDoubleClick={onDoubleClick}
    >
      <Rect
        x={6}
        y={6}
        width={width + 12}
        height={height + 12}
        stroke={onScale ? theme.colors.primary : 'transparency'}
        onMouseDown={handleMouseDown}
        onTouchStart={handleMouseDown}
        data-id="move"
      />
      {onScale &&
        data.map((attr: CircleType) => (
          <Circle
            key={attr.direction}
            data-id={attr.direction}
            onMouseDown={handleMouseDown}
            onTouchStart={handleMouseDown}
            fill={theme.colors.primary}
            {...attr}
          />
        ))}
    </SVG>
  );
};

export default OuterRect;
