import { API_URL_CURRENT_FRAGMENT } from "@insite/client-framework/Services/ApiService";
import { Cart, GetCartsApiParameter } from "@insite/client-framework/Services/CartService";
import siteMessage from "@insite/client-framework/SiteMessage";
import ApplicationState from "@insite/client-framework/Store/ApplicationState";
import { getById, getDataView } from "@insite/client-framework/Store/Data/DataState";
import { getQueryStrings } from "@insite/client-framework/Store/Data/Pages/PageSelectors";
import translate from "@insite/client-framework/Translate";
import { CartLineModel } from "@insite/client-framework/Types/ApiModels";
import { createContext } from "react";

export function getCurrentCartState(state: ApplicationState) {
    return getById(state.data.carts, API_URL_CURRENT_FRAGMENT, o => state.data.carts.currentId || o);
}

export function getCartState(state: ApplicationState, cartId: string | undefined) {
    return getById(state.data.carts, cartId);
}

export function getCartsDataView(state: ApplicationState, getCartsApiParameter: GetCartsApiParameter) {
    return getDataView(state.data.carts, getCartsApiParameter);
}

export const CartsDataViewContext = createContext<ReturnType<typeof getCartsDataView>>({
    value: undefined,
    isLoading: false,
});

export function isOutOfStock(cartLine: CartLineModel) {
    return cartLine.availability!.messageType === 2 && !cartLine.canBackOrder;
}

export const isPunchOutOrder = (cart: Cart | undefined) => !!cart && !!cart.properties["isPunchout"];

export const isCartEmpty = (cart: Cart | undefined) =>
    cart && (cart.lineCount === 0 || (!!cart.cartLines && cart.cartLines.length === 0));

export const canCheckoutWithCart = (cart: Cart | undefined) =>
    cart &&
    (cart.canCheckOut || hasRestrictedCartLines(cart) || hasProductsWithInvalidPrice(cart)) &&
    cart.status !== "AwaitingApprovalEditDraft";

export const isCartCheckoutDisabled = (cart: Cart | undefined) =>
    cart &&
    ((!cart.canCheckOut && !cart.canRequisition) ||
        isCartEmpty(cart) ||
        hasRestrictedCartLines(cart) ||
        hasProductsWithInvalidPrice(cart));

export const canSaveOrder = (cart: Cart | undefined) => cart && cart.canSaveOrder && !isCartEmpty(cart);

export const canAddAllToList = (cart: Cart | undefined) =>
    !isCartEmpty(cart) &&
    cart &&
    !!cart.cartLines &&
    cart.cartLines.every(line => line.canAddToWishlist && !line.isRestricted);

export const hasRestrictedCartLines = (cart: Cart | undefined) =>
    cart && !!cart.cartLines && cart.cartLines.filter(cartLine => cartLine.isRestricted).length > 0;

export const canPlaceOrder = (cart: Cart | undefined) =>
    cart &&
    !cart.requiresApproval &&
    (!cart.paymentMethod || (cart.paymentMethod && !cart.paymentMethod.isPaymentProfileExpired));

export const canSubmitForApprovalOrder = (cart: Cart | undefined) => cart && cart.requiresApproval && cart.hasApprover;

export const canSubmitForQuote = (cart: Cart | undefined) =>
    cart && cart.canRequestQuote && !cart.isAwaitingApproval && !isCartEmpty(cart);

export const canSubmitRequisition = (cart: Cart | undefined) => cart && cart.canRequisition && !isCartEmpty(cart);

export const canApplyPromotionsToCart = (cart: Cart | undefined) =>
    cart && !cart.paymentOptions?.isPayPal && cart.type !== "Quote" && cart.type !== "Job";

export const hasQuoteRequiredProducts = (cart: Cart | undefined) =>
    cart?.cartLines?.some(cartLine => cartLine.quoteRequired);

export const hasOnlyQuoteRequiredProducts = (cart: Cart | undefined) =>
    cart?.cartLines?.every(cartLine => cartLine.quoteRequired);

export const hasProductsWithInvalidPrice = (cart: Cart | undefined) =>
    cart?.cartLines?.some(
        cartLine =>
            !cartLine.isPromotionItem &&
            !cartLine.quoteRequired &&
            !cartLine.allowZeroPricing &&
            cartLine.pricing?.unitNetPrice === 0 &&
            cartLine.pricing?.unitRegularPrice === 0,
    );

export function getReviewAndPayCartState(state: ApplicationState) {
    const { cartId } = getQueryStrings(state);
    if (cartId) {
        return getCartState(state, cartId);
    }
    return getCurrentCartState(state);
}

export function isCartLoadedWithLines(cartState: ReturnType<typeof getCartState>) {
    if (cartState.isLoading || !cartState.value) {
        return false;
    }

    if (!cartState.value.cartLines || (cartState.value.cartLines.length === 0 && cartState.value.lineCount > 0)) {
        return false;
    }

    return true;
}

export function isCartLoadedWithoutLines(cartState: ReturnType<typeof getCartState>) {
    if (cartState.isLoading || !cartState.value) {
        return false;
    }

    if (cartState.value.cartLines && (cartState.value.cartLines.length > 0 || cartState.value.lineCount === 0)) {
        return false;
    }

    return true;
}

export function getSumQtyPerUom(cart: Cart, cartLine: CartLineModel) {
    return cart.cartLines!.reduce((sum, current) => {
        if (current.isFixedConfiguration) {
            var sectionOption = current.sectionOptions?.find(o => o.productId === cartLine.productId);
            if (sectionOption) {
                return sum + sectionOption.quantity * current.qtyOrdered!;
            }
        }

        return current.productId === cartLine.productId
            ? sum + current.qtyPerBaseUnitOfMeasure * current.qtyOrdered!
            : sum;
    }, 0);
}

export function getErrorMessages(cart: Cart, cartLine: CartLineModel, showInventoryAvailability?: boolean) {
    const errorMessages: React.ReactNode[] = [];

    const hasInsufficientInventoryInSections =
        cartLine.isFixedConfiguration &&
        cartLine.sectionOptions?.some(
            o => cart.cartLines?.find(p => p.productId === o.productId)?.hasInsufficientInventory,
        );
    if (showInventoryAvailability && hasInsufficientInventoryInSections && !isOutOfStock(cartLine)) {
        const cartLineWithInsufficientInventory = cart.cartLines?.find(
            o => o.hasInsufficientInventory && cartLine.sectionOptions?.some(p => p.productId === o.productId),
        );

        const tooManyRequestedMessage = siteMessage(
            "Cart_ToManyQtyRequested",
            cartLineWithInsufficientInventory!.qtyOnHand.toLocaleString(),
            getSumQtyPerUom(cart, cartLineWithInsufficientInventory!).toLocaleString(),
        );
        errorMessages.push(tooManyRequestedMessage);
    } else if (showInventoryAvailability && cartLine.hasInsufficientInventory && !isOutOfStock(cartLine)) {
        const tooManyRequestedMessage = siteMessage(
            "Cart_ToManyQtyRequested",
            cartLine.qtyOnHand.toLocaleString(),
            getSumQtyPerUom(cart, cartLine).toLocaleString(),
        );
        errorMessages.push(tooManyRequestedMessage);
    }

    if (cartLine.isRestricted) {
        errorMessages.push(translate("Restricted product"));
    }

    if (!cartLine.isActive) {
        errorMessages.push(translate("Inactive Product"));
    }

    if (cartLine.cantBuy) {
        errorMessages.push(translate("View-only product"));
    }

    return errorMessages;
}

export const hasCartLinesWithErrors = (cart: Cart | undefined, showInventoryAvailability?: boolean) =>
    cart && !!cart.cartLines && cart.cartLines.some(o => cartLineHasErrors(cart, o, showInventoryAvailability));

function cartLineHasErrors(cart: Cart, cartLine: CartLineModel, showInventoryAvailability?: boolean) {
    if (
        showInventoryAvailability &&
        cartLine.isFixedConfiguration &&
        cartLine.sectionOptions?.some(
            o => cart.cartLines?.find(p => p.productId === o.productId)?.hasInsufficientInventory,
        )
    ) {
        return true;
    }

    if (showInventoryAvailability && cartLine.hasInsufficientInventory) {
        return true;
    }

    if (cartLine.isRestricted) {
        return true;
    }

    if (!cartLine.isActive) {
        return true;
    }

    if (cartLine.cantBuy) {
        return true;
    }

    if (
        !cartLine.isPromotionItem &&
        !cartLine.quoteRequired &&
        !cartLine.allowZeroPricing &&
        cartLine.pricing?.unitNetPrice === 0 &&
        cartLine.pricing?.unitRegularPrice === 0
    ) {
        return true;
    }

    return false;
}
