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

import { setTimeout } from 'timers';
import useActions from '../actions';
import useStore from '../store';
import SearchComponent from '../components/Search';
import { getPdfPage, normalize, calculatePhraseMatch } from '../helpers/pdf';
import { extractTextItems } from '../helpers/search';
import { scrollIntoView } from '../helpers/utility';

let timer: ReturnType<typeof setTimeout> = setTimeout(() => '', 100);

let localMatchesMap: MatchType[] = [];

const Search: React.FC = () => {
  const [matchTotal, setMatchTotal] = useState(0);

  const [
    { navbarState, pdf, totalPage, queryString, currentIndex, matchesMap },
    dispatch,
  ] = useStore();
  const {
    setNavbar,
    setQueryString,
    setMatchesMap,
    setCurrentIndex,
  } = useActions(dispatch);

  const getMatchTextIndex = async (
    pageNum: number,
    queryStr: string
  ): Promise<void> => {
    const contentItems = await extractTextItems(
      (): Promise<any> => getPdfPage(pdf, pageNum)
    );
    const content = normalize(contentItems.join('').toLowerCase());
    const matches = calculatePhraseMatch(content, queryStr);

    if (matches.length) {
      matches.forEach(ele => {
        localMatchesMap.push({
          page: pageNum,
          index: ele,
        });
        setMatchesMap(localMatchesMap);
      });

      setMatchTotal(cur => cur + matches.length);
    }

    if (pageNum < totalPage) {
      await getMatchTextIndex(pageNum + 1, queryStr);
    }
  };

  const startSearchPdf = async (queryStr: string): Promise<void> => {
    getMatchTextIndex(1, queryStr);
  };

  const reset = (): void => {
    localMatchesMap = [];
    setMatchesMap([]);
    setMatchTotal(0);
    setCurrentIndex(-1);
    setQueryString('');
  };

  const handleSearch = (val: string): void => {
    if (!val) return;

    const queryStr = normalize(val.toLowerCase());
    if (queryStr !== queryString) {
      reset();
      setQueryString(queryStr);
      startSearchPdf(queryStr);
    }
  };

  const scrollToPage = (pageNum: number) => {
    const pageDiv: HTMLDivElement = document.getElementById(
      `page_${pageNum}`
    ) as HTMLDivElement;

    scrollIntoView(pageDiv);
  };

  const highlightTarget = (match: MatchType, color: string) => {
    timer = setTimeout(() => {
      const id = `${match.page}_${match.index}`;
      const pageElement: HTMLDivElement = document.getElementById(
        `page_${match.page}`
      ) as HTMLDivElement;
      const textLayer: HTMLDivElement = pageElement.querySelector(
        '[data-id="text-layer"]'
      ) as HTMLDivElement;

      if (textLayer) {
        const spans = textLayer.querySelectorAll(`[class="${id}"]`) as NodeList;

        if (spans.length > 0) {
          spans.forEach((ele: Node) => {
            // eslint-disable-next-line no-param-reassign
            (ele as HTMLElement).style.backgroundColor = color;
          });
        } else {
          highlightTarget(match, color);
        }
      }
    }, 200);
  };

  const handleClickPrev = (): void => {
    if (currentIndex > 0) {
      const currentMatch = matchesMap[currentIndex];
      if (currentMatch) {
        highlightTarget(currentMatch, 'rgba(255, 211, 0, 0.7)');
      }

      setCurrentIndex(currentIndex - 1);
      const nextMatch = matchesMap[currentIndex - 1];
      scrollToPage(nextMatch.page);
    }
  };

  const handleClickNext = (): void => {
    if (currentIndex + 1 < matchTotal) {
      const currentMatch = matchesMap[currentIndex];
      if (currentMatch) {
        highlightTarget(currentMatch, 'rgba(255, 211, 0, 0.7)');
      }

      setCurrentIndex(currentIndex + 1);
      const indexObj = matchesMap[currentIndex + 1];
      scrollToPage(indexObj.page);
    }
  };

  const handleClose = (): void => {
    setNavbar('');
    reset();
  };

  useEffect(() => {
    if (matchTotal >= 1 && currentIndex === -1) {
      setCurrentIndex(currentIndex + 1);
      const indexObj = localMatchesMap[currentIndex + 1];
      scrollToPage(indexObj.page);
    }
  }, [matchTotal, currentIndex]);

  useEffect(() => {
    if (currentIndex >= 0) {
      clearTimeout(timer);
      const match = matchesMap[currentIndex];
      highlightTarget(match, 'rgba(255, 141, 0)');
    }
  }, [currentIndex, matchesMap]);

  return (
    <SearchComponent
      matchesTotal={matchTotal}
      current={currentIndex + 1}
      onPrev={handleClickPrev}
      onNext={handleClickNext}
      onEnter={handleSearch}
      isActive={navbarState === 'search'}
      close={handleClose}
    />
  );
};

export default Search;
