import { CartReturn, OptimisticCart } from "@shopify/hydrogen";
import { createContext, ReactNode, useContext, useEffect, useState } from "react";
import { CartInfoToEnable, RedoContextValue, RedoCoverageClient, RedoError, RedoErrorType } from "../types";
import { REDO_PUBLIC_API_HOSTNAME } from "../utils/security";
import {
  addProductToCartIfNeeded,
  removeProductFromCartIfNeeded,
  setCartRedoEnabledAttribute,
  useFetcherWithPromise,
  getCartLines,
  getCartEligibilityPriceKey,
  useWaitCartIdle,
  isOptimisticCart,
} from "../utils/cart";
import { CartWithActionsDocs } from "@shopify/hydrogen-react/dist/types/cart-types";

const DEFAULT_REDO_CONTEXT_VALUE: RedoContextValue = {
  enabled: false,
  loading: true,
};

const RedoContext = createContext<RedoContextValue>(DEFAULT_REDO_CONTEXT_VALUE);

const RedoProvider = ({
  cart,
  storeId,
  children,
}: {
  cart: CartReturn | CartWithActionsDocs | OptimisticCart;
  storeId: string;
  children: ReactNode;
}): ReactNode => {
  const [cartInfoToEnable, setCartInfoToEnable] = useState<CartInfoToEnable>();
  const [loading, setLoading] = useState<boolean>(true);
  const [errors, setErrors] = useState<RedoError[]>([]);

  const logUniqueError = (newError: RedoError) => {
    if (!errors.find((err) => err.type === newError.type)) {
      setErrors([...errors, newError]);
    }
    return newError;
  };

  useEffect(() => {
    if (!cart || !storeId || isOptimisticCart(cart)) {
      return;
    }

    const cartLines = getCartLines(cart);

    fetch(`https://${REDO_PUBLIC_API_HOSTNAME}/v2.2/stores/${storeId}/coverage-products`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        cart: {
          lineItems: cartLines.map((cartLine) => ({
            id: cartLine.id,
            originalPrice: {
              amount: cartLine.merchandise?.price?.amount,
              currency: cartLine.merchandise?.price?.currencyCode,
            },
            priceTotal: {
              amount: cartLine.cost?.totalAmount?.amount,
              currency: cartLine.cost?.totalAmount?.currencyCode,
            },
            product: {
              id: cartLine.merchandise?.product?.id,
            },
            variant: {
              id: cartLine.merchandise?.id,
            },
            quantity: cartLine.quantity,
          })),
          priceTotal: {
            amount: cart.cost?.totalAmount?.amount,
            currency: cart.cost?.totalAmount?.currencyCode,
          },
        },
        customer: {
          id: cart.buyerIdentity?.customer?.id || "",
          country: cart.buyerIdentity?.countryCode,
        },
      }),
    }).then(async (res) => {
      if (res.status === 500) {
        logUniqueError({
          type: RedoErrorType.ApiServerError,
          message:
            "Internal server error occured when getting available coverage products from Redo API.. Check your inputs are correct and storeId have been configured. Reach out to Redo support if the issue persists.",
          context: {
            json: await res.json(),
          },
        });
        return;
      } else if (res.status === 400) {
        logUniqueError({
          type: RedoErrorType.ApiBadRequest,
          message:
            "Bad request when getting available coverage products from Redo API. Check that the passed in cart is of the correct type Cart/CartReturn and includes all of the correct cart information.",
          context: {
            json: await res.json(),
          },
        });
        return;
      } else if (res.status !== 200) {
        logUniqueError({
          type: RedoErrorType.ApiUnknownError,
          message: "Unkown error occured while getting available coverage products from Redo API.",
          context: {
            status: res.status,
            json: await res.json(),
          },
        });
        return;
      }

      const json = await res.json();

      setLoading(false);

      if (!json?.coverageProducts?.[0]?.cartInfoToEnable) {
        return;
      }

      setCartInfoToEnable(json.coverageProducts[0].cartInfoToEnable);
    });
  }, [getCartEligibilityPriceKey(cart), storeId]);

  const contextVal: RedoContextValue = {
    enabled: true,
    loading,
    storeId,
    cartInfoToEnable,
    cart,
    errors: errors?.length && errors.length > 0 ? errors : undefined,
  };

  return <RedoContext.Provider value={contextVal}>{children}</RedoContext.Provider>;
};

const useRedoCoverageClient = (): RedoCoverageClient => {
  const redoContext = useContext(RedoContext);
  const fetcher = useFetcherWithPromise();
  const waitCartIdle = useWaitCartIdle(redoContext.cart);

  useEffect(() => {
    if (redoContext.loading || !redoContext.cartInfoToEnable) {
      return;
    }
    removeProductFromCartIfNeeded({
      cart: redoContext.cart,
      fetcher,
      waitCartIdle,
      cartInfoToEnable: redoContext.cartInfoToEnable,
    });
  }, [redoContext.loading]);

  return {
    enable: async () => {
      if (redoContext.loading || !redoContext.cartInfoToEnable) {
        return false;
      }
      await addProductToCartIfNeeded({
        fetcher,
        waitCartIdle,
        cart: redoContext.cart,
        cartInfoToEnable: redoContext.cartInfoToEnable,
      });
      await setCartRedoEnabledAttribute({
        cart: redoContext.cart,
        fetcher,
        waitCartIdle,
        cartInfoToEnable: redoContext.cartInfoToEnable,
        enabled: true,
      });
      return true;
    },
    disable: async () => {
      if (!redoContext.cartInfoToEnable) {
        return false;
      }
      await removeProductFromCartIfNeeded({
        fetcher,
        waitCartIdle,
        cart: redoContext.cart,
        cartInfoToEnable: redoContext.cartInfoToEnable,
      });
      await setCartRedoEnabledAttribute({
        cart: redoContext.cart,
        fetcher,
        waitCartIdle,
        cartInfoToEnable: redoContext.cartInfoToEnable,
        enabled: false,
      });
      return true;
    },
    get loading() {
      return redoContext.loading;
    },
    get eligible() {
      return !this.loading && !!this.price && !!this.cartProduct && !!this.cart?.cost;
    },
    get enabled() {
      return redoContext.enabled;
    },
    get price() {
      const priceToEnable = redoContext.cartInfoToEnable?.selectedVariant?.price?.amount;
      if (!priceToEnable || Number(priceToEnable).toString() === "NaN") {
        return undefined;
      }

      return Number(priceToEnable);
    },
    get cart() {
      return redoContext.cart;
    },
    get cartProduct() {
      return redoContext.cartInfoToEnable?.selectedVariant;
    },
    get cartAttribute() {
      return redoContext.cartInfoToEnable?.cartAttribute;
    },
    get storeId() {
      return redoContext.storeId;
    },
    get errors() {
      return redoContext.errors;
    },
  };
};

export { RedoProvider, useRedoCoverageClient };
