import React, { useEffect, useState, useCallback } from 'react';
import { v4 as uuidv4 } from 'uuid';

import { ANNOTATION_TYPE } from '../constants';
import Button from '../components/Button';
import ExpansionPanel from '../components/ExpansionPanel';
import Icon from '../components/Icon';
import ShapeOption from '../components/ShapeOption';

import { getAbsoluteCoordinate } from '../helpers/position';
import {
  parseAnnotationObject,
  appendUserIdAndDate,
} from '../helpers/annotation';
import { switchPdfViewerScrollState } from '../helpers/pdf';
import useCursorPosition from '../hooks/useCursorPosition';

import useActions from '../actions';
import useStore from '../store';

type Props = {
  title: string;
  isActive: boolean;
  onClick: () => void;
};

let shapeEle: SVGElement | null = null;

const ShapeTool: React.FC<Props> = ({ title, isActive, onClick }: Props) => {
  const [startPoint, setStartPoint] = useState({ x: 0, y: 0 });
  const [endPoint, setEndPoint] = useState({ x: 0, y: 0 });

  const [data, setData] = useState({
    page: 0,
    shape: 'square',
    type: 'fill',
    color: '#FBB705',
    opacity: 35,
    width: 0,
  });
  const [cursorPosition, setRef] = useCursorPosition(30);

  const [{ viewport, scale }, dispatch] = useStore();
  const { addAnnots } = useActions(dispatch);

  const setDataState = (obj: OptionPropsType): void => {
    setData((prev) => ({
      ...prev,
      ...obj,
    }));
  };

  const convertPosition = (
    type: string,
    x1: number,
    y1: number,
    x2: number,
    y2: number,
  ): PositionType | LinePositionType => {
    switch (type) {
      case 'Line':
        return {
          start: {
            x: x1,
            y: y1,
          },
          end: {
            x: x2,
            y: y2,
          },
        };
      default:
        return {
          top: y2 > y1 ? y1 : y2,
          left: x2 > x1 ? x1 : x2,
          right: x2 > x1 ? x2 : x1,
          bottom: y2 > y1 ? y2 : y1,
        };
    }
  };

  const handleMouseDown = (event: MouseEvent): void => {
    switchPdfViewerScrollState('hidden');

    const pageEle = (event.target as HTMLElement).parentNode as HTMLElement;

    if (pageEle.hasAttribute('data-page-num')) {
      setRef(pageEle);
      const pageNum = pageEle.getAttribute('data-page-num') || '0';
      const coordinate = getAbsoluteCoordinate(pageEle, event);
      setData((current) => ({
        ...current,
        page: parseInt(pageNum, 10),
      }));
      setStartPoint(coordinate);
    }
  };

  const handleMouseUp = useCallback((): void => {
    switchPdfViewerScrollState('scroll');
    const shapeType = ANNOTATION_TYPE[data.shape];
    const id = uuidv4();

    if (startPoint.x !== endPoint.x && endPoint.y) {
      const position = convertPosition(
        shapeType,
        startPoint.x,
        startPoint.y,
        endPoint.x,
        endPoint.y,
      );

      const annoteData = {
        id,
        obj_type: shapeType,
        obj_attr: {
          page: data.page as number,
          position,
          bdcolor: data.color,
          fcolor: data.type === 'fill' ? data.color : undefined,
          transparency: data.opacity,
          ftransparency: data.type === 'fill' ? data.opacity : undefined,
          bdwidth: data.width,
          is_arrow: data.shape === 'arrow',
        },
      };
      const shapeAnnotation = appendUserIdAndDate(
        parseAnnotationObject(annoteData, viewport.height, scale),
      );

      addAnnots([shapeAnnotation]);

      setRef(null);
      setStartPoint({ x: 0, y: 0 });
      setEndPoint({ x: 0, y: 0 });
    }
  }, [startPoint, endPoint, viewport, data, scale]);

  const subscribeEvent = (): void => {
    const pdfViewer = document.getElementById('pdf_viewer') as HTMLDivElement;
    pdfViewer.addEventListener('mousedown', handleMouseDown);
    pdfViewer.addEventListener('mouseup', handleMouseUp);
  };

  const unsubscribeEvent = (): void => {
    const pdfViewer = document.getElementById('pdf_viewer') as HTMLDivElement;
    pdfViewer.removeEventListener('mousedown', handleMouseDown);
    pdfViewer.removeEventListener('mouseup', handleMouseUp);
  };

  useEffect(() => {
    const pdfViewer = document.getElementById('pdf_viewer') as HTMLDivElement;

    if (isActive && pdfViewer) {
      subscribeEvent();
    }

    return (): void => {
      if (pdfViewer) {
        unsubscribeEvent();
      }
    };
  }, [isActive, handleMouseUp]);

  useEffect(() => {
    if (cursorPosition.x && cursorPosition.y) {
      setEndPoint({ x: cursorPosition.x, y: cursorPosition.y });
    }
  }, [cursorPosition]);

  const checkSvgChild = (type: string) => {
    switch (type) {
      case 'square':
        return 'rect';
      case 'circle':
        return 'ellipse';
      default:
        return 'line';
    }
  };

  useEffect(() => {
    const pageEle = document.getElementById(`page_${data.page}`);
    const canvas = pageEle?.getElementsByClassName('canvas')[0] as HTMLElement;

    if (endPoint.x && endPoint.y) {
      canvas.style.display = 'block';

      if (shapeEle) {
        const { top, left, right, bottom } = convertPosition(
          ANNOTATION_TYPE[data.shape],
          startPoint.x,
          startPoint.y,
          endPoint.x,
          endPoint.y,
        ) as PositionType;

        if (data.shape === 'square') {
          shapeEle.setAttribute('x', `${left}`);
          shapeEle.setAttribute('y', `${top}`);
          shapeEle.setAttribute('width', `${right - left}`);
          shapeEle.setAttribute('height', `${bottom - top}`);
        } else if (data.shape === 'circle') {
          const xRadius = (right - left) / 2;
          const yRadius = (bottom - top) / 2;
          shapeEle.setAttribute('cx', `${left + xRadius}`);
          shapeEle.setAttribute('cy', `${top + yRadius}`);
          shapeEle.setAttribute('rx', `${xRadius}`);
          shapeEle.setAttribute('ry', `${yRadius}`);
        } else {
          const actualWidth = data.width * scale;
          shapeEle.setAttribute('x1', `${startPoint.x + actualWidth}`);
          shapeEle.setAttribute('y1', `${startPoint.y + actualWidth}`);
          shapeEle.setAttribute('x2', `${endPoint.x + actualWidth}`);
          shapeEle.setAttribute('y2', `${endPoint.y + actualWidth}`);
        }
      } else {
        shapeEle = document.createElementNS(
          'http://www.w3.org/2000/svg',
          checkSvgChild(data.shape),
        );
        shapeEle.setAttribute('fill', data.color);
        shapeEle.setAttribute('fill-opacity', `${data.opacity * 0.01}`);
        shapeEle.setAttribute('stroke', data.color);
        shapeEle.setAttribute('stroke-width', `${data.width * scale}`);
        shapeEle.setAttribute('stroke-opacity', `${data.opacity * 0.01}`);
        canvas.appendChild(shapeEle);
      }
    } else if (canvas && shapeEle) {
      canvas.style.display = 'none';
      canvas.removeChild(shapeEle);
      shapeEle = null;
    }
  }, [startPoint, endPoint, data, scale]);

  const Label = (
    <Button
      shouldFitContainer
      align="left"
      onClick={onClick}
      isActive={isActive}
    >
      <Icon glyph="shape" style={{ marginRight: '10px' }} />
      {title}
    </Button>
  );

  return (
    <ExpansionPanel isActive={isActive} label={Label}>
      <ShapeOption {...data} setDataState={setDataState} />
    </ExpansionPanel>
  );
};

export default ShapeTool;
