import { FocusEvent, useContext, useEffect, useRef, useState } from 'react';
import { DataContext } from '@assets/js-shop/context';
import { SEARCH_AUTOCOMPLETE, SEARCH_PRESEARCH } from '@assets/js-shop/query';
import { IGraphQLAutocompleteResult } from '@assets/interfaces/gqlApi/graphql';
import { ShopUtils } from '@assets/js-shop/ShopUtils';
import { AutocompleteData } from '@assets/js-shop/autocomplete/Autocomplete';
import { useDebounced } from '@assets/js-shop/hooks';
import config from '@assets/js-shop/config';
import * as GTM from '@assets/filters/common/utils/gtm';

export function useAutocomplete() {
   const context = useContext(DataContext);
   const [searchValue, setSearchValue] = useState('');
   const [searchLoading, setSearchLoading] = useState(false);
   const [searchData, setSearchData] = useState(null);
   const [active, setActive] = useState(false);
   const [presearchData, setPresearchData] = useState(null);
   const debouncedSearchValue = useDebounced(searchValue, 500);
   const resultCache: Map<string, AutocompleteData> = new Map();
   const initialRun = useRef(false);

   const handleInputChange = (event: InputEvent) => {
      const value = (event.target as HTMLInputElement).value;
      setSearchValue(value);
   };

   const handleInputFocusIn = (event: FocusEvent<HTMLInputElement>) => {
      const { value } = event.target;

      if (presearchData === null) {
         loadPresearch();
      }

      if (value) {
         setSearchValue(value);
      }

      if (presearchData || value) {
         document.documentElement.classList.add('autocomplete-active');
         setActive(true);
      }
   };

   const handleInputFocusOut = (e: FocusEvent) => {
      const customEvent = e as unknown as CustomEvent;
      const isMobileUserAgent = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
      // nezavři, když klikneme do searche
      if ((!document.querySelector(config.autocompleteParentSelector).contains(e.relatedTarget) && !isMobileUserAgent) ||
         (customEvent.detail && customEvent.detail.close)) {
         document.documentElement.classList.remove('autocomplete-active');
         setActive(false);
      } else if (!document.querySelector(config.autocompleteParentSelector).contains(e.relatedTarget) && isMobileUserAgent) {
         // detokování skrytí klávesnice na mobilech & snaha o animaci
         const windowHeight = (window.visualViewport?.height || window.innerHeight);
         document.documentElement.style.setProperty('--windowHeight', `${windowHeight + 400}px`);
      }
   };

   const loadPresearch = () => {
      context.client
         .query({
            query: SEARCH_PRESEARCH,
            variables: { types: ['products', 'sections', 'producers'] },
         })
         .then((result) => {
            if (!result.data.presearch.length) {
               return;
            }

            const data: { [key: string]: IGraphQLAutocompleteResult } = {};

            for (const typeResult of result.data.presearch) {
               data[typeResult.type] = typeResult as IGraphQLAutocompleteResult;
            }

            setActive(true);
            setPresearchData(data);
            document.documentElement.classList.add('autocomplete-active');
         })
         .catch((e) => ShopUtils.handleFetchError(e, {query: 'SEARCH_PRESEARCH'}));
   };

   const setSearchMetaAndData = (data: AutocompleteData) => {
      setSearchData(data);
      setSearchLoading(false);
      setActive(true);
      document.documentElement.classList.add('autocomplete-active');
   };

   const search = (value: string) => {
      if (!value) {
         setSearchLoading(false);
         setActive(presearchData !== null);
         document.documentElement.classList.toggle('autocomplete-active', presearchData !== null);
         return;
      }

      if (resultCache.has(value)) {
         setSearchMetaAndData(resultCache.get(value));
         GTM.dataCollector.whisperSearch(value, resultCache.get(value));
         return;
      }

      resultCache.set(value, null);

      setSearchLoading(true);
      setActive(true);
      document.documentElement.classList.add('autocomplete-active');

      const variables = {
         term: value,
         types: ['products', 'sections', 'producers', 'articles', 'pages'],
      };
      context.client
         .query({ query: SEARCH_AUTOCOMPLETE, variables, })
         .then((result) => {
            const data: { [key: string]: IGraphQLAutocompleteResult } = {};

            for (const typeResult of result.data.autocomplete) {
               data[typeResult.type] = typeResult as IGraphQLAutocompleteResult;
            }

            resultCache.set(value, data);
            GTM.dataCollector.whisperSearch(value, data);
            setSearchMetaAndData(data);
         })
         .catch((e) => ShopUtils.handleFetchError(e, {query: 'SEARCH_AUTOCOMPLETE', variables}));
   };

   useEffect(() => {
      // don't run effect on mount, only when value changes
      if (!initialRun.current) {
         initialRun.current = true;
         return;
      }

      search(debouncedSearchValue);
   }, [debouncedSearchValue]);

   useEffect(() => {
      if (!active) {
         return;
      }

      const setRealVh = () => {
         const vh = (window.visualViewport?.height || window.innerHeight);
         document.documentElement.style.setProperty('--windowHeight', `${vh}px`);
      };

      setRealVh();
      window.addEventListener('orientationchange', setRealVh);
      window.visualViewport?.addEventListener('resize', setRealVh);
      window.visualViewport?.addEventListener('scroll', setRealVh);

      const input = document.querySelector(config.autocompleteParentSelector)?.querySelector('input[name="search"]');
      input.addEventListener('focus', setRealVh);
      input.addEventListener('blur', setRealVh);


      return () => {
         window.removeEventListener('orientationchange', setRealVh);
         window.visualViewport?.removeEventListener('resize', setRealVh);
         window.visualViewport?.removeEventListener('scroll', setRealVh);
         input.removeEventListener('focus', setRealVh);
         input.removeEventListener('blur', setRealVh);
      };

   }, [active]);

   return {
      search: {
         loading: searchLoading,
         value: searchValue,
         data: searchData,
         setSearchValue,
      },
      inputHandlers: {
         handleInputChange,
         handleInputFocusIn,
         handleInputFocusOut,
      },
      active,
      presearchData,
   };
}
