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,
  parsePositionForBackend,
} 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;
};

const Shape: React.FC<Props> = ({ title, isActive, onClick }: Props) => {
  const [startPosition, setStartPosition] = useState({ x: 0, y: 0 });
  const [uuid, setUuid] = useState('');
  const [data, setData] = useState({
    shape: 'circle',
    type: 'border',
    color: '#FBB705',
    opacity: 100,
    width: 3,
  });
  const [cursorPosition, setRef] = useCursorPosition();

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

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

  const convertPosition = (
    type: string,
    x1: number,
    y1: number,
    x2: number,
    y2: number
  ): any => {
    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 addShape = useCallback(
    (
      pageEle: HTMLElement,
      event: MouseEvent | TouchEvent,
      attributes: OptionPropsType
    ): void => {
      const { shape = '', type, opacity, color, width = 0 } = attributes;
      const pageNum = pageEle.getAttribute('data-page-num') || 0;
      const coordinate = getAbsoluteCoordinate(pageEle, event);
      const id = uuidv4();

      setUuid(id);
      setStartPosition(coordinate);

      const shapeType = ANNOTATION_TYPE[shape];
      const position = convertPosition(
        shapeType,
        coordinate.x - 8,
        coordinate.y - 8,
        coordinate.x + 8,
        coordinate.y + 8
      );
      const annoteData = {
        id,
        obj_type: shapeType,
        obj_attr: {
          page: pageNum as number,
          position,
          bdcolor: color,
          fcolor: type === 'fill' ? color : undefined,
          transparency: opacity,
          ftransparency: type === 'fill' ? opacity : undefined,
          bdwidth: width,
          is_arrow: shape === 'arrow',
        },
      };
      const shapeAnnotation = appendUserIdAndDate(
        parseAnnotationObject(annoteData, viewport.height, scale)
      );

      addAnnots([shapeAnnotation]);
    },
    [viewport, scale, data]
  );

  const handleMouseDown = (event: MouseEvent | TouchEvent): void => {
    event.preventDefault();
    switchPdfViewerScrollState('hidden');

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

    if (pageEle.hasAttribute('data-page-num')) {
      addShape(pageEle, event, data);
      setRef(pageEle);
    }
  };

  const handleMouseUp = (): void => {
    switchPdfViewerScrollState('scroll');
    setRef(null);
    setUuid('');
    setStartPosition({ x: 0, y: 0 });
  };

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

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

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

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

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

  const handleUpdate = useCallback(
    (start: Record<string, any>, end: Record<string, any>): void => {
      const index = annotations.length - 1;
      const { x: x1, y: y1 } = start;
      const { x: x2, y: y2 } = end;

      if (annotations[index] && annotations[index].id === uuid) {
        const type = annotations[index].obj_type;
        const position = convertPosition(type, x1, y1, x2, y2);

        annotations[index].obj_attr.position = parsePositionForBackend(
          type,
          position,
          viewport.height,
          scale
        );
        annotations[index] = appendUserIdAndDate(annotations[index]);

        updateAnnots([...annotations]);
      }
    },
    [annotations, viewport, scale, uuid]
  );

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

  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 Shape;
