/* eslint-disable no-param-reassign */
import { v4 as uuidv4 } from 'uuid';
import dayjs from 'dayjs';
import queryString from 'query-string';

import { ANNOTATION_TYPE } from '../constants';
import { getPdfPage, renderTextLayer } from './pdf';
import { getPosition, parsePositionForBackend } from './position';
import { normalizeRound, floatToHex } from './utility';
import { xmlParser, getElementsByTagName } from './dom';

type GetFontAttributeFunc = (
  type: string,
  element: Record<string, any>
) => Record<string, any>;

const getContent = (type: string, element: Record<string, any>): string => {
  if (type !== 'Text' && type !== 'FreeText') return '';

  let content = '';
  let nodes: any = element.childNodes;
  nodes = Array.prototype.slice.call(nodes);

  nodes.forEach((ele: HTMLElement) => {
    if (ele.tagName === 'contents') {
      content = ele.innerHTML || ele.textContent || '';
    }
  });
  return content;
};

const getFontAttribute: GetFontAttributeFunc = (type, element) => {
  if (type !== 'FreeText') return {};
  const appearanceString =
    element.childNodes[1].innerHTML || element.childNodes[1].textContent;
  const arr = appearanceString.split(' ');

  return {
    fontsize: parseInt(arr[5], 10),
    fontname: arr[4].substr(1),
    textcolor: floatToHex(
      parseFloat(arr[0]),
      parseFloat(arr[1]),
      parseFloat(arr[2])
    ),
  };
};

export const parseAnnotationFromXml = (xmlString: string): AnnotationType[] => {
  if (!xmlString) return [];

  const xmlDoc = xmlParser(xmlString);
  const elements = xmlDoc.firstElementChild || xmlDoc.firstChild;
  const element = getElementsByTagName(elements as ChildNode, 'annots') || [];
  const annotations: Element[] = Array.prototype.slice.call(element);
  const filterAnnots = annotations.reduce((acc: any[], cur: any) => {
    const type = ANNOTATION_TYPE[cur.tagName];

    if (type) {
      const page = parseInt(cur.attributes.page.value, 10);
      acc.push({
        id: uuidv4(),
        obj_type: type,
        obj_attr: {
          title: cur.attributes.title ? cur.attributes.title.value : undefined,
          date: cur.attributes.date ? cur.attributes.date.value : undefined,
          page,
          position: getPosition(type, cur),
          bdcolor: cur.attributes.color
            ? cur.attributes.color.value
            : undefined,
          bdwidth: cur.attributes.width
            ? parseInt(cur.attributes.width.value, 10)
            : 0,
          transparency: cur.attributes.opacity
            ? cur.attributes.opacity.value
            : 1,
          content: getContent(type, cur) || undefined,
          fcolor: cur.attributes['interior-color']
            ? cur.attributes['interior-color'].value
            : undefined,
          ftransparency: cur.attributes['interior-opacity']
            ? cur.attributes['interior-opacity'].value
            : undefined,
          is_arrow: cur.attributes.tail,
          ...getFontAttribute(type, cur),
        },
      });
    }

    return acc;
  }, []);

  return filterAnnots;
};

// eslint-disable-next-line consistent-return
const getEleText = (
  coord: any,
  elements: any,
  viewport: any,
  scale: any
): string => {
  const top = normalizeRound(viewport.height - coord.top * scale);
  const left = normalizeRound(coord.left * scale);
  const bottom = normalizeRound(viewport.height - coord.bottom * scale);
  const right = normalizeRound(coord.right * scale);

  for (let i = 0, len = elements.length; i <= len; i += 1) {
    const element = elements[i];
    if (element) {
      const eleTop = normalizeRound(element.offsetTop);
      const eleLeft = normalizeRound(element.offsetLeft);
      const eleRight = normalizeRound(element.offsetLeft + element.offsetWidth);

      if (eleTop >= top && eleTop <= bottom) {
        const textLength = element.innerText.length;
        const width = element.offsetWidth;

        if (eleLeft < left && eleRight > right) {
          const distanceL = left - eleLeft;
          const rateL = distanceL / width;
          const start = Math.floor(textLength * rateL);
          const distanceR = eleRight - right;
          const rateR = distanceR / width;
          const end = Math.floor(textLength - textLength * rateR);
          return ` ${element.innerText.slice(start, end)}`;
        }
        if (eleLeft < left && eleRight > left) {
          const distance = left - eleLeft;
          const rate = distance / width;
          const start = Math.floor(textLength * rate);
          return ` ${element.innerText.slice(start)}`;
        }
        if (eleRight > right && eleLeft < right) {
          const distance = eleRight - right;
          const rate = distance / width;
          const end = Math.floor(textLength - textLength * rate);
          return ` ${element.innerText.slice(0, end)}`;
        }
        if (eleLeft >= left && eleRight <= right) {
          return ` ${element.innerText}`;
        }
      }
    }
  }
  return '';
};

export const getAnnotationText = async ({
  viewport,
  scale,
  page,
  coords,
  pdf,
}: {
  viewport: ViewportType;
  scale: number;
  page: number;
  coords: PositionType[];
  pdf: any;
}): Promise<any> => {
  const pageContainer = document.getElementById(`page_${page}`) as HTMLElement;
  const textLayer = pageContainer.querySelector(
    '[data-id="text-layer"]'
  ) as HTMLElement;
  const pdfPage = await getPdfPage(pdf, page);

  if (!textLayer.childNodes.length) {
    await renderTextLayer({
      textLayer,
      pdfPage,
      viewport,
    });
  }
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
  // @ts-ignore
  const textElements = [...textLayer.childNodes];
  let text = '';

  for (let i = 0, len = coords.length; i < len; i += 1) {
    const coord = coords[i];

    text += getEleText(coord, textElements, viewport, scale);
  }

  return text;
};

export const parseAnnotationObject = (
  {
    id,
    obj_type,
    obj_attr: {
      page,
      bdcolor,
      transparency,
      fcolor,
      ftransparency,
      position = '',
      content,
      style,
      bdwidth,
      fontname,
      fontsize,
      textcolor,
      is_arrow,
    },
  }: AnnotationType,
  pageHeight: number,
  scale: number
): AnnotationType => ({
  id: id || uuidv4(),
  obj_type,
  obj_attr: {
    page: page - 1,
    bdcolor,
    position: parsePositionForBackend(obj_type, position, pageHeight, scale),
    transparency: transparency ? transparency * 0.01 : 0,
    content: content || undefined,
    style,
    fcolor,
    ftransparency: ftransparency ? ftransparency * 0.01 : 0,
    bdwidth,
    fontname,
    fontsize,
    textcolor,
    is_arrow,
  },
});

export const appendUserIdAndDate = (
  annotateObj: AnnotationType
): AnnotationType => {
  const parsed = queryString.parse(window.location.search);

  if (parsed.watermark) {
    annotateObj.obj_attr.title = parsed.watermark;
  }

  const datetime = dayjs().format('YYYY-MM-DD_HH:mm:ss');
  annotateObj.obj_attr.date = datetime;

  return annotateObj;
};
