import { autoPlacement, autoUpdate, computePosition, offset } from '@floating-ui/react-dom';

let intervalIds: any[] = [];

const getIntersectionObserver = (loadCallback: (target: any) => void, observerOptions?: IntersectionObserverInit) => {
   observerOptions = observerOptions ?? {
      rootMargin: '300px 0px 300px 0px'
   };
   const intersectionCallback = (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {

      entries.forEach((entry) => {
         if (entry.isIntersecting && (!entry.target.children.length || entry.target.children[0].textContent === 'Načítám')) {
            loadCallback(entry.target);
            observer.unobserve(entry.target);
         }
      });
   };

   return new IntersectionObserver(intersectionCallback, observerOptions);
};

const decodeHtmlEntities = (value: string) => {
   if(!value) {
      return value;
   }
   let txt = document.createElement("textarea");
   txt.innerHTML = value;
   return txt.value;
}

enum Resolution {
   DEFAULT = 'default',
   MD = 'mqdefault',
   HD = 'hqdefault',
   SD = 'sddefault',
   MAX = 'maxresdefault',
}

const resolutions: string[] = [
   '0',
   'default',
   'mqdefault',
   'hqdefault'
];

type Mode = 'youtube' | 'vimeo' | 'cdn';

const blocekRuntime = {
   config: {},
   onReady: function() {
      wpj.blocekRuntime.initProductsLazyload();
      wpj.blocekRuntime.initComponentLazyload();
      wpj.blocekRuntime.initializeSliders();
      wpj.blocekRuntime.initializeAccordionClickAction();
      wpj.blocekRuntime.initTabs();
      wpj.blocekRuntime.initImap();
      wpj.blocekRuntime.initFaq();
      wpj.blocekRuntime.initVideoLazy();
   },
   insertProducts: function(filter: any, callback: any) {
      // @ts-ignore
      if (MODULES.COMPONENTS) {
         fetch(wpj.graphqlRoute, {
            method: 'POST',
            headers: {
               'Content-Type': 'application/json'
            },
            redirect: 'follow',
            body: JSON.stringify({
               query: `query ($data: String){
                             productsBlock(data: $data)
                            }
                            `,
               variables: {
                  data: filter
               }
            })
         }).then((response) => {
            // Examine the text in the response
            response.json().then((content: any) => {
               callback({ html: content.data.productsBlock, result: true });
               fireProductsLoadedEvent();
               wpj.blocekRuntime.initializeSliders();
            });
         });
      } else {
         fetch('/_blocek/ProductsBlock/', {
            method: 'POST',
            headers: {
               'Content-Type': 'application/json'
            },
            redirect: 'follow',
            body: filter
         }).then(function(response) {
            response.json().then(function(data) {
               callback(data);
               fireProductsLoadedEvent();
               wpj.blocekRuntime.initializeSliders();
            });
         });
      }
   },
   insertComponent: (body: string, callback: any) => {
      if (!MODULES.COMPONENTS) {
         return;
      }
      fetch(wpj.graphqlRoute, {
         method: 'POST',
         headers: {
            'Content-Type': 'application/json'
         },
         redirect: 'follow',
         body: JSON.stringify({
            query: `query ($data: String){
               componentBlock(data: $data)
            }`,
            variables: {
               data: body
            }
         })
      }).then((response) => {
         // Examine the text in the response
         response.json().then((content: any) => {
            callback({ html: content.data.componentBlock, result: true });
         });
      });
   },
   loadComponent: function(component: any) {
      if (!component || !MODULES.COMPONENTS) {
         return;
      }

      let body = component.getAttribute('data-block-component');

      // @ts-ignore
      window.wpj.blocekRuntime.insertComponent(body, function(data: any) {
         component.innerHTML = data.html;
         // @ts-ignore
         if (typeof window.wpj !== 'undefined' && typeof window.wpj.tracking !== 'undefined') {
            // @ts-ignore
            window.wpj.tracking.handleImpressions();
         }
      });
   },
   initializeSliders: () => {
      for (let i = 0; i <= intervalIds.length; i++) {
         clearInterval(intervalIds[i]);
      }
      intervalIds = [];

      const containers = document.querySelectorAll('[data-slide-settings]') as NodeListOf<HTMLElement>;
      containers.forEach((container) => {
         const containerSettings = JSON.parse(decodeHtmlEntities(container.getAttribute('data-slide-settings') ?? '{}'));
         const slideBox = container.querySelector('[data-slide-init=\'true\']') as HTMLElement;

         if (containerSettings['buttons']) {
            const prevButton = container.querySelector('[data-slide-controls=\'prev\']') as HTMLElement;
            prevButton.classList.add('disabled');
            const nextButton = container.querySelector('[data-slide-controls=\'next\']') as HTMLElement;
            let sliderOriginalX: number[] = [];
            const slideBoxX: number = slideBox.getBoundingClientRect().x;
            const slideBoxViewport: number = slideBox.getBoundingClientRect().width;

            let sliderElements: HTMLElement[] = Array.from(container.querySelectorAll('[data-blocek-col]') as NodeListOf<HTMLElement>);

            // X, kupkolo fallback na productList
            if (sliderElements.length <= 0 && container.querySelector('.catalog-row')) {
               sliderElements = Array.prototype.slice.call(container.querySelector('.catalog-row').children);
            }

            if (sliderElements.length <= 0) {
               sliderElements = Array.from(container.querySelectorAll('[class^="wpj-col-"]') as NodeListOf<HTMLElement>).filter((element: HTMLElement) => {
                  return !(element.querySelector('[class^="wpj-col-"]') !== null);
               });
            }

            sliderOriginalX.push(0); // start slideru
            sliderElements.forEach((element, index) => {
               if ((element.getBoundingClientRect().x + element.getBoundingClientRect().width) >= (slideBoxX + slideBoxViewport)) {
                  const el_offset = (element.getBoundingClientRect().x + element.getBoundingClientRect().width) - (slideBoxX + slideBoxViewport);
                  sliderOriginalX.push(el_offset);
               }
            });
            let currentIndex = 0;
            sliderOriginalX = [...new Set(sliderOriginalX)];

            function scrollCarousel(items: number) {
               currentIndex += items;
               if (currentIndex < 0) {
                  currentIndex = 0;
               } else if (currentIndex >= sliderOriginalX.length) {
                  currentIndex = sliderOriginalX.length - 1;
               }

               if (currentIndex == 0) {
                  prevButton.classList.add('disabled');
               } else {
                  prevButton.classList.remove('disabled');
               }

               if (currentIndex >= (sliderOriginalX.length - 1)) {
                  nextButton.classList.add('disabled');
               } else {
                  nextButton.classList.remove('disabled');
               }

               let offset = 0;
               if (currentIndex > 0) {
                  offset = sliderOriginalX[currentIndex];
               }
               slideBox.style.transform = `translateX(${-1 * offset}px)`;
            }

            prevButton.addEventListener('click', function() {
               scrollCarousel(-1);
            });
            nextButton.addEventListener('click', function() {
               scrollCarousel(1);
            });
         } else if (containerSettings['speed'] > 0) {
            slideBox.style.width = (2 * container.getBoundingClientRect().width) + 'px';
            slideBox.style.transition = 'unset';
            const elements = container.querySelectorAll('[data-blocek-col]') as NodeListOf<HTMLElement>;
            let elementsWidth = 0;
            elements.forEach((element, index) => {
               elements[0]?.parentNode?.appendChild(element.cloneNode(true));
               elementsWidth += element.getBoundingClientRect().width;
               if (index == 0 && elements.length > 1) {
                  elementsWidth += (elements[index + 1].getBoundingClientRect().x - element.getBoundingClientRect().x - element.getBoundingClientRect().width) * elements.length;
               }
            });

            const id = setInterval(frame, 50 - containerSettings['speed']);
            intervalIds.push(id);
            let pos = 0;

            function frame() {
               if (pos >= elementsWidth) {
                  pos = 0;
               } else {
                  pos++;
                  slideBox.style.transform = `translateX(${(-pos)}px)`;
               }
            }
         }
      });
   },
   initializeAccordionClickAction: () => {
      document.addEventListener('click', (e) => {
         const container = (e.target as HTMLElement)
            .closest('[data-accordion-click]') as HTMLElement;
         if (!container) return;

         const value = container.getAttribute('data-accordion-click');
         if (value === 'disabled') return;

         const body = container.parentElement
            ?.querySelector('[data-accordion-body]') as HTMLElement;
         if (!body) return;

               const icon = container
            .querySelector('[data-accordion-open-icon]') as HTMLElement;

               if (body.style.maxHeight && body.style.maxHeight !== '0px') {
                  icon?.classList.remove('wpj-icon-active');
                  body.style.maxHeight = '0px';
               } else {
                  icon?.classList.add('wpj-icon-active');
                  body.style.maxHeight = getTotalScrollHeight(body) + 'px';
            const speed = getAnimationSpeed(body);
                  body.style.transition = `max-height ${speed}s ease`;
               }

         const onEnd = () => {
               if (body.style.maxHeight !== '0px') {
                  body.style.maxHeight = 'auto';
                  body.style.transition = 'max-height .3s ease';
               }
            body.removeEventListener('transitionend', onEnd);
         };
         body.addEventListener('transitionend', onEnd);
      });

   function getAnimationSpeed(element) {
         const childrenBodies = element.querySelectorAll('[data-accordion-body]') as NodeListOf<HTMLElement>;
         const closedBodies = Array.from(childrenBodies).filter(body => !body.style.maxHeight || body.style.maxHeight === '0px');

         return closedBodies.length ? closedBodies.length * 0.3 : 0.3;
      }

      function getTotalScrollHeight(element) {
         let totalHeight = element.scrollHeight;
         const childrenBodies = element.querySelectorAll('[data-accordion-body]');

         childrenBodies.forEach((body) => {
            totalHeight += body.scrollHeight;
         });

         return totalHeight;
      }
   },
   initComponentLazyload: function() {
      if (!MODULES.COMPONENTS) {
         return;
      }
      const observer = getIntersectionObserver((target) => {
         wpj.blocekRuntime.loadComponent(target);
      });

      const targets = document.querySelectorAll('[data-block-lazy]');

      targets.forEach(target => {
         observer.observe(target);
      });
   },
   loadProducts: function(products: any) {
      if (!products) {
         return;
      }

      var filter = decodeHtmlEntities(products.getAttribute('data-products-filter'));

      try {
         // Find closest "data-products-listId"
         let element = products;
         while (element.parentElement) {
            element = element.parentElement;
            if (element.hasAttribute('data-products-listType')) {
               let filter_decoded = JSON.parse(filter);
               filter_decoded.listType = element.getAttribute('data-products-listType');
               filter_decoded.listId = element.getAttribute('data-products-listId');
               filter = JSON.stringify(filter_decoded);
               break;
            }
         }
      } catch (e) {
         // Ignore listId detection exceptions
      }

      // @ts-ignore
      window.wpj.blocekRuntime.insertProducts(filter, function(data: any) {
         products.innerHTML = data.html;
         // Trigger imagesLoaded event to notify lazy load
         products.dispatchEvent(new CustomEvent('imagesLoaded', { bubbles: true }));
         // @ts-ignore
         if (typeof window.wpj !== 'undefined' && typeof window.wpj.tracking !== 'undefined') {
            // @ts-ignore
            window.wpj.tracking.handleImpressions();
         }
      });
   },
   initProductsLazyload: function() {
      const observer = getIntersectionObserver((target) => {
         wpj.blocekRuntime.loadProducts(target);
      });
      const targets = document.querySelectorAll('[data-products-filter]');

      targets.forEach(target => {
         observer.observe(target);
      });
   },
   initImap: function() {
      document.addEventListener('click', (e) => {
         // @ts-ignore
         const selector = e.target.closest('[data-imap="points-wrapper"] [data-imap="icon"]');
         if (selector) {
            const point = selector.closest('[data-imap="point"]');
            point.classList.toggle('active');

            if (point.classList.contains('active')) {
               const content = point.querySelector('.w-imap-content');

               autoUpdate(point, content, () => {
                  computePosition(point, content, {
                     strategy: 'absolute',
                     middleware: [
                        autoPlacement({
                           crossAxis: true,
                           allowedPlacements: ['bottom-start', 'bottom-end', 'top-end', 'top-start', 'bottom', 'top']
                        }),
                        offset({
                           mainAxis: -20,
                           alignmentAxis: 20
                        })
                     ]
                  }).then(({ x, y }) => {
                     Object.assign(content.style, {
                        left: `${x}px`,
                        top: `${y}px`,
                        height: 'fit-content'
                     });
                  });
               }, {
                  ancestorScroll: false,
                  layoutShift: false
               });
            }

            [...point.parentNode.querySelectorAll('.active')].filter((child) => child !== point)[0]?.classList.remove('active');
         }
      });

      document.addEventListener('click', (e) => {
         // @ts-ignore
         const selector = e.target.closest('a.imap-close-button');
         if (selector) {
            const point = selector.closest('[data-imap="point"]');

            point.classList.toggle('active');
            [...point.parentNode.querySelectorAll('.active')].filter((child) => child !== point)[0]?.classList.remove('active');
         }
      });
   },
   initFaq: function() {
      document.addEventListener('click', (e) => {

         // @ts-ignore
         const selector = e.target.closest('.wpj-faq-wrapper h4');
         if (selector) {
            if (selector.classList.contains('active')) {
               selector.classList.remove('active');
               selector.closest('.wpj-faq-wrapper').classList.remove('opened');
            } else {
               selector.classList.add('active');
               selector.closest('.wpj-faq-wrapper').classList.add('opened');
            }
         }
      });
   },
   initTabs: function() {
      document.addEventListener('click', (e) => {
         // @ts-ignore
         const trigger = e.target.closest('[data-tabs-open]');
         if (trigger) {
            const id = trigger.getAttribute('data-tabs-open');
            const content = document.querySelector(`[data-tabs-content="${id}"]`);
            const parent = trigger.closest('.wpj-tabs');

            if (!content || !parent) return;

            const allHeaders = parent.querySelectorAll('[data-tabs-open]');
            const allContents = parent.querySelectorAll('[data-tabs-content]');

            allHeaders.forEach(header => header.classList.remove('active'));
            allContents.forEach(content => content.classList.remove('active'));

            trigger.classList.add('active');
            content.classList.add('active');
         }
      });
   },
   setYoutubeImages: function(url: string, code: string, imageThumb: HTMLImageElement) {
      const fallbackUrl = `https://i.ytimg.com/vi/${code}/sddefault.jpg`;

      fetch(`https://www.youtube.com/oembed?format=json&url=${url}`)
         .then((res) => res.json())
         .then((res) => {
            if (!res?.thumbnail_url) {
               imageThumb.setAttribute('src', fallbackUrl);
            }
            imageThumb.setAttribute('src', res.thumbnail_url);
         })
         .catch((err) => {
         imageThumb.setAttribute('src', fallbackUrl);
      });
   },

   getVimeoImages: async function(code: string, imageThumb: HTMLImageElement): Promise<string> {
      const fallbackUrl = `https://i.ytimg.com/vi/${code}/sddefault.jpg`;
      try {
         const res = await fetch(`https://vimeo.com/api/oembed.json?url=https://player.vimeo.com/video/${code}&width=1280&height=720`);

         if (!res.ok) {
            imageThumb.setAttribute('src', fallbackUrl);
            return;
         }

         const data = await res.json();

         const url = data ? data.thumbnail_url : fallbackUrl;
         imageThumb.setAttribute('src', url);

      } catch (error) {
         imageThumb.setAttribute('src', fallbackUrl);
      }
   },

   getImages: function(url: string, code: string, mode: Mode, imageThumb: HTMLImageElement) {
      switch (mode) {
         case 'youtube':
            url = url.replace('embed/', 'watch?v=').replace('?autoplay=1&mute=0', '');
            this.setYoutubeImages(url, code, imageThumb);
            return;
         case 'vimeo':
            this.getVimeoImages(code, imageThumb);
            return;
      }
   },
   initVideoLazy: function () {
      document.addEventListener('click', (e: Event) => {
         // @ts-ignore
         const selector = e.target.closest('.w-lazy-play-btn');

         if (!selector) {
            return;
         }

         e.preventDefault();

         selector.style.display = 'none';

         if (selector.hasAttribute('data-i-frame-loaded')) {
            return false;
         }

         const wrapper = selector.closest('.w-lazy-video-wrapper');
         const url = wrapper.getAttribute('data-url');
         const mode = wrapper.getAttribute('data-mode') as Mode;

         if (mode === "cdn" && wrapper.getAttribute('data-autoplay') !== "true") {
            injectVideoElement(wrapper, false);
         } else {
            let iframeElement: HTMLIFrameElement = document.createElement('iframe');
            iframeElement.src = url;
            iframeElement.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture';
            iframeElement.frameBorder = '0';
            iframeElement.allowFullscreen = true;
            wrapper.append(iframeElement);
         }

         selector.setAttribute('data-i-frame-loaded', true);
      });

      document.querySelectorAll('.w-lazy-video-wrapper').forEach((el) => {
         const imageThumb: HTMLImageElement = el.querySelector('.w-lazy-video-poster-img');
         let imgSrc: string = imageThumb?.getAttribute('src');
         const url = el.getAttribute('data-url');
         const mode = el.getAttribute('data-mode') as Mode;

         if (mode === 'cdn' && el.getAttribute('data-autoplay') === 'true') {
            getIntersectionObserver(() => {
               injectVideoElement(el as HTMLDivElement, true);
            }).observe(el.querySelector('.w-lazy-video-poster-img'));
            return;
         }

         getIntersectionObserver(() => {
            if (!imgSrc || (imageThumb.naturalWidth <= 120 && imageThumb.naturalHeight <= 90)) {
               const code = el.getAttribute('data-code');
               this.getImages(url, code, mode, imageThumb);
            }
         }).observe(imageThumb);

         el.addEventListener('mouseenter', (e: Event) => {
            // @ts-ignore
            const wrapper: any = e.target;

            if (!wrapper) {
               return;
            }
            const mode = wrapper.getAttribute('data-mode') as Mode;
            const url = wrapper.getAttribute('data-url');

            if (wrapper.getAttribute('data-warm') || !mode) {
               return false;
            }
            wrapper.setAttribute('data-warm', true);

            let preconnectLinksTemplate;
            if (mode === 'youtube') {
               preconnectLinksTemplate = `<link rel="preconnect" ` + `href="` + url + `"/>
                <link rel="preconnect" href="https://www.google.com"/>
                <link rel="preconnect" href="https://static.doubleclick.net"/>
                <link rel="preconnect" href="https://googleads.g.doubleclick.net"/>`;
            } else if (mode === 'vimeo') {
               preconnectLinksTemplate = `<link rel="preconnect" href="https://player.vimeo.com"/>
                <link rel="preconnect" href="https://i.vimeocdn.com"/>
                <link rel="preconnect" href="https://f.vimeocdn.com"/>
                <link rel="preconnect" href="https://fresnel.vimeocdn.com"/>`;
            }

            if (preconnectLinksTemplate) {
               wrapper.insertAdjacentHTML('beforeend', preconnectLinksTemplate);
            }
         });
      });
   }
};

const injectVideoElement = (wrapper: HTMLDivElement, autoplay: boolean) => {
   const mobileUrl = wrapper.getAttribute('data-mobile-url');

   let videoElement: HTMLVideoElement = document.createElement('video');
   videoElement.loop = autoplay;
   videoElement.autoplay = true;
   videoElement.playsInline = autoplay;
   videoElement.preload = 'auto';
   videoElement.muted = autoplay;
   videoElement.controls = !autoplay;
   videoElement.className = 'video';
   videoElement.style.zIndex = '1';
   videoElement.className = 'w-lazy-video-poster-img';

   const sourceMP4 = document.createElement('source');
   sourceMP4.src = wrapper.getAttribute('data-url');
   sourceMP4.type = 'video/mp4';
   videoElement.appendChild(sourceMP4);

   const sourceHLS = document.createElement('source');
   sourceHLS.src = mobileUrl;
   sourceHLS.type = 'application/x-mpegURL';
   videoElement.appendChild(sourceHLS);

   wrapper.append(videoElement);
};

const fireProductsLoadedEvent = () => {
   // event for js-shop - so favorites/compare icons can be rendered
   const loadedEvent = new CustomEvent('wpj-products-loaded', {
      detail: { type: 'blocek' }
   });
   document.dispatchEvent(loadedEvent);
};

declare global {
   interface Wpj {
      blocekRuntime: typeof blocekRuntime;
   }
}

wpj.blocekRuntime = blocekRuntime;

if (MODULES.COMPONENTS) {
   window.onload = () => {
      wpj.blocekRuntime.onReady();
   };
} else {
   wpj.onReady.push(wpj.blocekRuntime.onReady);
}
