import { Controller } from '@hotwired/stimulus';
import { Component, getComponent } from '@symfony/ux-live-component';
import { initializeTooltips } from '@assets/components/tooltips';
import { IAddedProduct } from '@assets/js-shop/Interfaces';
import { BUY_FORM_REFRESH } from '@assets/components/constants/eventTypes';
import { createBuyFormCollectDataEvent } from '@assets/components/utils';
import { BuyFormCollectDataEvent, BuyFormError, BuyFormErrorType } from '@assets/components/interfaces';

export default class extends Controller<HTMLDivElement> {
   static targets = ['addToCartButton'];
   static values = {
      productId: Number,
      variationId: Number || undefined,
   };

   declare productIdValue: number;
   declare variationIdValue?: number;

   declare hasAddToCartButtonTarget: boolean;
   declare addToCartButtonTarget: HTMLButtonElement;

   declare liveComponent: Component;
   declare addToCartData: {};

   private refreshDataListener: (event: Event) => void;
   private closeModalListener: (event: Event) => void;

   handleCloseModal(modal: HTMLElement) {
      if (modal) {
         const modalCloseButton: HTMLElement = modal.querySelector('[data-modal-close]');
         if (modalCloseButton) {
            modalCloseButton.click();
         }
      }
   }

   connect() {
      this.addToCartData = {
         productId: this.productIdValue
      };

      if (this.variationIdValue) {
         this.addToCartData['variationId'] = this.variationIdValue;
      }

      this.refreshDataListener = this.handleCollectData.bind(this, true);
      this.element.addEventListener(BUY_FORM_REFRESH, this.refreshDataListener);

      const modal: HTMLElement = this.element.closest('.wpj-modal-overlay');
      if (modal) {
         this.closeModalListener = this.handleCloseModal.bind(this, modal);
         document.addEventListener('cartOpened', this.closeModalListener);
      }
   }

   disconnect() {
      this.element.removeEventListener(BUY_FORM_REFRESH, this.refreshDataListener);
      document.removeEventListener('cartOpened', this.closeModalListener);
   }

   async initialize() {
      this.liveComponent = await getComponent(this.element);
      const observer = new MutationObserver(() => {
         this.enableAddToCartButton();
         initializeTooltips();
      });
      observer.observe(this.element, { childList: true, subtree: true });
   }

   private handleCollectData(isPreview: boolean = false) {
      const event = createBuyFormCollectDataEvent(isPreview);
      this.element.dispatchEvent(event);

      if (isPreview) {
         void this.refreshBuyForm(event);
      }

      this.handleEventErrors(event);

      return event;
   }

   private async refreshBuyForm(event: BuyFormCollectDataEvent) {
      if (this.liveComponent) {
         this.liveReload(event);
      } else {
         await getComponent(this.element).then(() => {
            this.liveReload(event);
         });
      }
   }

   private liveReload(event: BuyFormCollectDataEvent) {
      Object.entries(event.getAddToCartData()).forEach(([key, value]) => {
         if (!Array.isArray(event.getAddToCartData())) {
            this.liveComponent.set((key === 'variationId' ? 'id_variation' : key), value, false);
         }
      });

      this.handleEventErrors(event);

      void this.liveComponent.render();
   }

   addToCartHover(e: MouseEvent) {
      const event = this.handleCollectData();

      if (event.hasErrors()) {
         if (e.type == 'mouseenter') {
            this.handleEventErrors(event);
            this.disableAddToCartButton();
         } else if (e.type == 'mouseleave' && !this.addToCartButtonTarget.classList.contains('clicked')) {
            this.enableAddToCartButton();
         }
      } else {
         void this.clearErrorText();
         this.enableAddToCartButton();
      }
   }

   addToCart() {
      const event = this.handleCollectData();

      if (!event.hasErrors()) {
         if (event.isMultiInsert()) {
            this.addToCartData = [];
         }
         Object.assign(this.addToCartData, event.getAddToCartData());

         const pieces = event.detail.pieces;
         const clickAddToCart = this.addToCartButtonTarget?.getAttribute('data-tracking-clickaddtocart');
         const data = (typeof clickAddToCart === 'string')
            ? JSON.parse(clickAddToCart)
            : (clickAddToCart as object);

         if (this.hasAddToCartButtonTarget) {
            this.addToCartButtonTarget.classList.add('disabled');
            this.addToCartButtonTarget.insertAdjacentHTML('beforeend', '<div class="add-to-cart-loader"></div>');

            let gtmData = { 'quantity': pieces ?? 1, 'ecommerce': { 'items': {} } };
            let id = this.productIdValue.toString();
            if (this.addToCartData['variationId']) {
               id += '_' + this.addToCartData['variationId'];
            }
            gtmData.ecommerce.items[0] = { item_id: '*' + id + '' };
            this.addToCartButtonTarget.setAttribute('data-gtm-placeholders', JSON.stringify(gtmData));
         }

         if (!Array.isArray(this.addToCartData)) {
            if (data?.add?.products?.[0]) {
               const productJson = data.add.products[0];
               this.addToCartData = {
                  ...this.addToCartData,
                  name: productJson.name,
                  price: {
                     withVat: productJson.priceWithVat,
                     withoutVat: productJson.priceWithoutVat
                  }
               };
            }
         }

         const options = {
            buttonElement: this.addToCartButtonTarget,
            additionalData: {
               title: data?.add?.products[0]?.name
            },
            blockingPromise: true
         };

         if (Array.isArray(this.addToCartData)) {
            if (this.addToCartData.length <= 0) {
               this.addToCartButtonTarget.classList.remove('disabled');
               this.addToCartButtonTarget.querySelector('.add-to-cart-loader').remove();

               return;
            }
            window.wpj.jsShop.addProductsToCart(this.addToCartData as [], this.addToCartData.map(val => Number(parseFloat(val.pieces))), options)
               .then(() => this.productAddedToCart());
         } else {
            window.wpj.jsShop.addProductToCart(this.addToCartData as IAddedProduct, (!pieces || Number.isNaN(pieces)) ? 1 : +pieces, options)
               .then(() => this.productAddedToCart());
         }
      } else {
         this.handleEventErrors(event);
         void this.disableAddToCartButton();
         this.addToCartButtonTarget.classList.add('clicked');
         this.addToCartButtonTarget.setAttribute('data-gtm-placeholders', JSON.stringify({ quantity: 'has-variation-error' }));
      }
   }

   enableAddToCartButton() {
      if (this.hasAddToCartButtonTarget) {
         this.addToCartButtonTarget.classList.remove('has-variation-error');
         this.addToCartButtonTarget.disabled = false;
      }
   }

   disableAddToCartButton() {
      if (this.hasAddToCartButtonTarget) {
         this.addToCartButtonTarget.classList.add('has-variation-error');
         this.addToCartButtonTarget.disabled = true;
      }
   }

   productAddedToCart() {
      this.addToCartButtonTarget.classList.add('btn-product-added');
      this.addToCartButtonTarget.classList.remove('disabled');

      this.addToCartButtonTarget.querySelector('.add-to-cart-loader').remove();

      setTimeout(() => this.addToCartButtonTarget.classList.remove('btn-product-added'), 600);
   }

   private async setErrorText(message: string) {
      if (this.liveComponent) {
         void this.liveComponent.set('error_text', message);
      } else {
         await getComponent(this.element).then(() => {
            void this.liveComponent.set('error_text', message);
         });
      }
   }

   private async clearErrorText() {
      if (this.liveComponent) {
         void this.liveComponent.set('error_text', null);
      } else {
         await getComponent(this.element).then(() => {
            void this.liveComponent.set('error_text', null);
         });
      }
   }

   private handleEventErrors(event: BuyFormCollectDataEvent) {
      const componentError = event.getLatestComponentError();

      if (componentError) {
         void this.setErrorText(componentError.message);
      } else {
         void this.clearErrorText();
      }

      if (event.hasErrors()) {
         this.showToastErrors(event);
      }
   }

   private showToastErrors(event: BuyFormCollectDataEvent) {
      if (wpj.jsShop && 'addToast' in wpj.jsShop) {
         event.getErrors().forEach((e) => {
            e.type === BuyFormErrorType.Toast && wpj.jsShop.addToast(e.message);
         });
      }
   }
}
