import { nanoid } from 'nanoid';
import {
  Cart,
  CartItem,
  IframeExtended,
  DolaExtendedWindow,
  IDola,
  VariantInfo,
  IDataset,
} from '../types';
import { isNil } from './typeCheck';

const DolaCheckoutURL: string | undefined = process.env.DOLA_CHECKOUT_APP_URL;

export const Dolapay: IDola = ((window as unknown) as DolaExtendedWindow).Dolapay;

export const loadIframe = (merchantId: string) => {
  try {
    if (document.getElementById('dolapayIframe')) throw new Error('Iframe already exists');
    return createDolaIframe(merchantId);
  } catch (error) {
    throw new Error(error.toString());
  }
};

export const createDolaIframe = (merchantId: string) => {
  try {
    let dolaIframe: IframeExtended = document.createElement('iframe');

    const testingState = Dolapay.testing === true ? 'true' : 'false';

    dolaIframe.src = `${DolaCheckoutURL}/${merchantId}?url=${location.origin}&testing=${testingState}`;
    dolaIframe.style.width = '100%';
    dolaIframe.title = 'dola-bep-checkout';
    dolaIframe.style.height = '100%';
    dolaIframe.style.border = 'none';
    dolaIframe.loading = 'lazy';
    dolaIframe.id = 'dolapayIframe';
    dolaIframe.style.position = 'fixed';
    dolaIframe.style.top = '0';
    dolaIframe.style.zIndex = '-9999';
    dolaIframe.style.overflow = 'hidden';

    document.body.prepend(dolaIframe);
    return dolaIframe;
  } catch (error) {
    throw new Error(`error creating dola iframe: ${error.toString()}`);
  }
};

export const attachDolaEventListeners = (dolaIframe: IframeExtended) => {
  try {
    window.addEventListener('message', (event) => {
      if (event.origin !== DolaCheckoutURL) return;

      if (event.data['action'] === 'close-dola') {
        dolaIframe.style.zIndex = '-99999';
      }

      if (event.data['action'] === 'shopperId') {
        Dolapay.shopperID = event.data['payload'];
      }

      if (event.data === 'opened-dola') {
        dolaIframe.style.zIndex = '99999';
      }
    });
  } catch (error) {
    throw new Error(`error attaching listeners: ${error.toString()}`);
  }
};

export const showIframe = (cart: Cart, merchantId: string) => {
  try {
    if (isNil(merchantId)) throw new Error('missing merchant id');

    setTimeout(() => {
      const iframe = document?.getElementById('dolapayIframe') as HTMLIFrameElement;

      if (iframe && iframe.contentWindow) {
        iframe.contentWindow.postMessage(
          { cart, secret: `dola_${merchantId}` },
          `${DolaCheckoutURL}`
        );
      }
    }, 350);
  } catch (error) {
    throw new Error(error.toString());
  }
};

export const dolaCheckoutEventHandler = (event: any, callback: () => void) => {
  if (event.origin !== DolaCheckoutURL) return;

  Dolapay.orderCompleted = true;
  if (event.data === 'order-complete') {
    callback();
  }
};

export const addListenerToInstances = (buyNowInstances: HTMLCollectionOf<HTMLDivElement>) => {
  try {
    for (let index = 0; index < buyNowInstances.length; index++) {
      const currentInstance = buyNowInstances[index];
      if (currentInstance?.id) return;

      if (currentInstance.dataset?.dolaCartaction === 'true') {
        currentInstance.id = nanoid();
        currentInstance.addEventListener('click', () => {
          return attachDolaToCart(currentInstance.dataset, buyNowInstances);
        });
      } else if (currentInstance.dataset?.dolaBuynow === 'true') {
        currentInstance.id = nanoid();
        currentInstance.addEventListener('click', () => {
          return attachDolaToOne(currentInstance.dataset);
        });
      }

      currentInstance.classList.remove('dola-bep-loading');
    }
  } catch (error) {
    throw new Error('error attaching dola action to instance');
  }
};

export const fetchDolaInstances = () => {
  try {
    return document.getElementsByClassName(
      'dola-dola-bills-yall'
    ) as HTMLCollectionOf<HTMLDivElement>;
  } catch (error) {
    throw new Error(error.toString());
  }
};

const attachDolaToOne = (dataset: IDataset) => {
  try {
    const parsedItem = parseVariantsIntoItem(dataset, composeItemObject(dataset));

    const cartObject: Cart = {
      currency: validateField(dataset.dolaCurrency, 'Invalid currency'),
      items: [parsedItem],
    };

    showIframe(cartObject, Dolapay.id);

    window.addEventListener('message', (event) =>
      dolaCheckoutEventHandler(event, () => {
        // plug in various no code integrations for basic implementation
      })
    );
  } catch (error) {
    console.error('Error attaching Dola to product: ', error.toString());
  }
};

const attachDolaToCart = (dataset: IDataset, instances: HTMLCollectionOf<HTMLDivElement>) => {
  try {
    const items = parseItems(instances);

    const cartObject: Cart = {
      currency: validateField(dataset.dolaCurrency, 'Invalid currency'),
      items: items,
    };

    showIframe(cartObject, Dolapay.id);

    window.addEventListener('message', (event) =>
      dolaCheckoutEventHandler(event, () => {
        // plug in various no code integrations for basic implementation
      })
    );
  } catch (error) {
    console.error('Error attaching Dola to cart: ', error.toString());
  }
};

const parseItems = (instances: HTMLCollectionOf<HTMLDivElement>) => {
  const rawCartDatasets: CartItem[] = [];

  for (let index = 0; index < instances.length; index++) {
    const currentInstance = instances[index];

    if (currentInstance.dataset?.dolaCart === 'true') {
      const parsedItem = parseVariantsIntoItem(
        currentInstance.dataset,
        composeItemObject(currentInstance.dataset)
      );

      rawCartDatasets.push(parsedItem);
    }
  }
  return rawCartDatasets;
};

const validateField = (field: string | undefined, error: string) => {
  if (isNil(field)) throw new Error(error);
  else {
    return field;
  }
};

export const validateCart = (cart: Cart) => {
  const validatedItems = cart.items.map((eachCartDetail) => {
    const item: CartItem = {
      id: validateField(eachCartDetail.id, 'Invalid product Id'),
      image: validateField(eachCartDetail.image, 'Invalid product Image'),
      quantity: parseInt(validateField(eachCartDetail.quantity.toString(), 'Invalid quantity'), 10),
      title: validateField(eachCartDetail.title, 'Invalid title'),
      price: parseInt(validateField(eachCartDetail.price.toString(), 'Invalid price'), 10),
      grams: parseInt(validateField(eachCartDetail.grams.toString(), 'Invalid weight'), 10),
      sku: validateField(eachCartDetail.sku, 'Invalid sku'),
      variantInfo: eachCartDetail.variantInfo ? eachCartDetail.variantInfo : [],
    };

    if (!isNil(eachCartDetail.willBeShipped)) {
      item.willBeShipped = eachCartDetail.willBeShipped;
    }

    if (!isNil(eachCartDetail.isTaxable)) {
      item.isTaxable = eachCartDetail.isTaxable;
    }

    return item;
  });

  return {
    currency: validateField(cart.currency, 'Invalid currency'),
    items: validatedItems,
  };
};

const composeItemObject = (dataset: IDataset): CartItem => {
  const item: CartItem = {
    id: validateField(dataset.dolaId, 'Invalid product Id'),
    image: validateField(dataset.dolaImage, 'Invalid product Image'),
    quantity: parseInt(validateField(dataset.dolaQuantity, 'Invalid quantity'), 10),
    title: validateField(dataset.dolaTitle, 'Invalid title'),
    price: parseInt(validateField(dataset.dolaPrice, 'Invalid price'), 10),
    grams: parseInt(validateField(dataset.dolaWeight, 'Invalid weight'), 10),
    sku: validateField(dataset.dolaSku, 'Invalid sku'),
    variantInfo: [],
  };

  if (!isNil(dataset.dolaWillbeshipped)) {
    item.willBeShipped = dataset.dolaWillbeshipped === 'true';
  }

  if (!isNil(dataset.dolaIstaxable)) {
    item.isTaxable = dataset.dolaIstaxable === 'true';
  }

  return item;
};

const parseVariantsIntoItem = (dataset: IDataset, item: CartItem) => {
  const variantPrefix = 'dolaVariant';
  const variants = Object.keys(dataset).filter((e) => e.includes(variantPrefix));

  const variantInfo = variants.map((key, index) => {
    const tempObj: VariantInfo = {
      id: nanoid(),
      name: variants[index].slice(variantPrefix.length, key.length),
      value: dataset[key] as string,
    };
    return tempObj;
  });

  return {
    ...item,
    variantInfo,
  };
};