import { ProductInfo } from "@insite/client-framework/Common/ProductInfo";
import { SafeDictionary } from "@insite/client-framework/Common/Types";
import { ProductContextModel } from "@insite/client-framework/Components/ProductContext";
import {
    GetProductCollectionApiV2Parameter,
    GetRelatedProductCollectionApiV2Parameter,
} from "@insite/client-framework/Services/ProductServiceV2";
import { FulfillmentMethod } from "@insite/client-framework/Services/SessionService";
import ApplicationState from "@insite/client-framework/Store/ApplicationState";
import { getSession, getSettingsCollection } from "@insite/client-framework/Store/Context/ContextSelectors";
import { getCurrentCartState } from "@insite/client-framework/Store/Data/Carts/CartsSelector";
import { getById, getDataView } from "@insite/client-framework/Store/Data/DataState";
import {
    AttributeTypeFacetModel,
    AvailabilityMessageType,
    ProductModel,
} from "@insite/client-framework/Types/ApiModels";
import cloneDeep from "lodash/cloneDeep";

export function getProductState(state: ApplicationState, id: string | undefined) {
    return getById(state.data.products, id);
}

export function getProductStateByPath(state: ApplicationState, path: string | undefined) {
    const productState = getById(state.data.products, path, id => state.data.products.idByPath[id]);
    if (path) {
        const attributeTypes = state.data.products.attributeTypesByPath[path];
        if (attributeTypes && productState.value) {
            return {
                isLoading: productState.isLoading,
                id: productState.id,
                value: { ...productState.value, attributeTypes },
            };
        }
    }

    return productState;
}

export function getVariantChildrenDataView(state: ApplicationState, productId: string | undefined) {
    return getDataView(state.data.products, { productId, variantChildren: true });
}

export function getProductsDataView(
    state: ApplicationState,
    getProductsParameter?: GetProductCollectionApiV2Parameter | GetRelatedProductCollectionApiV2Parameter,
) {
    return getDataView(state.data.products, getProductsParameter);
}

export function getMergedAttributeTypeFacets(
    state: ApplicationState,
    unfilteredApiParameter?: GetProductCollectionApiV2Parameter,
    filteredApiParameter?: GetProductCollectionApiV2Parameter,
) {
    const unfilteredDataView = getProductsDataView(state, unfilteredApiParameter);
    const unfilteredProductCollection = unfilteredDataView.value ? unfilteredDataView : undefined;

    const filteredDataView = getProductsDataView(state, filteredApiParameter);
    const filteredProductCollection = filteredDataView.value ? filteredDataView : undefined;

    if (!unfilteredProductCollection?.attributeTypeFacets) {
        return filteredProductCollection?.attributeTypeFacets;
    }

    if (!filteredProductCollection?.attributeTypeFacets) {
        return unfilteredProductCollection?.attributeTypeFacets;
    }

    return unfilteredProductCollection.attributeTypeFacets.concat(
        filteredProductCollection.attributeTypeFacets.filter(
            o => !unfilteredProductCollection.attributeTypeFacets!.some(p => p.attributeTypeId === o.attributeTypeId),
        ),
    );
}

export function canAddToCart(
    state: ApplicationState,
    product: ProductModel,
    productInfo?: ProductInfo,
    configurationCompleted?: boolean,
    variantSelectionCompleted?: boolean,
    configurationSelection?: SafeDictionary<string>,
) {
    if (
        !product ||
        (!configurationCompleted &&
            product.detail?.configuration &&
            !product.detail?.configuration.isKit &&
            product.detail?.configuration.configSections &&
            product.detail?.configuration.configSections.length > 0)
    ) {
        return false;
    }

    if (product.quoteRequired) {
        var currentCart = getCurrentCartState(state).value;
        if (currentCart && currentCart.status === "AwaitingApprovalEditDraft") {
            return false;
        }
    }

    // Fixed configuration, if any section has CantBuy is true, we return false.
    if (
        product.detail?.configuration &&
        product.detail?.configuration.isKit &&
        product.detail?.configuration.configSections &&
        product.detail?.configuration.configSections.length > 0
    ) {
        for (const configSection of product.detail!.configuration.configSections) {
            if (configSection.sectionOptions?.some(x => x.cantBuy)) {
                return false;
            }
        }
    }

    // Standard configuration, check CantBuy property of selected options.
    if (
        configurationCompleted &&
        product.detail?.configuration &&
        !product.detail?.configuration.isKit &&
        product.detail?.configuration.configSections &&
        product.detail?.configuration.configSections.length > 0 &&
        configurationSelection &&
        Object.values(configurationSelection).length > 0
    ) {
        const sectionOptions = Object.values(configurationSelection).filter(sectionOptionId => !!sectionOptionId);
        if (sectionOptions) {
            for (const configSection of product.detail!.configuration.configSections) {
                const selectedOptions = configSection.sectionOptions?.filter(x => sectionOptions.indexOf(x.id) > -1);
                if (selectedOptions && selectedOptions.every(x => x.cantBuy)) {
                    return false;
                }
            }
            return true;
        }
    }

    const { canAddToCart, allowBackOrder } = getSettingsCollection(state).productSettings;
    const availability =
        productInfo?.inventory?.inventoryAvailabilityDtos?.find(
            o => o.unitOfMeasure.toLowerCase() === (productInfo?.unitOfMeasure?.toLowerCase() || ""),
        )?.availability || undefined;
    return (
        canAddToCart &&
        !product.cantBuy &&
        (product.canAddToCart ||
            ((configurationCompleted || variantSelectionCompleted) &&
                (allowBackOrder || (availability && availability.messageType !== 2)) &&
                !product.canConfigure))
    );
}

export function hasEnoughInventory(state: ApplicationState, productContext: ProductContextModel) {
    const {
        product,
        productInfo: { inventory, unitOfMeasure },
    } = productContext;
    const { fulfillmentMethod } = getSession(state);
    const { productSettings } = getSettingsCollection(state);
    const allowBackOrder =
        fulfillmentMethod === FulfillmentMethod.PickUp
            ? productSettings.allowBackOrderForPickup
            : productSettings.allowBackOrderForDelivery;
    const inventoryAvailability = inventory?.inventoryAvailabilityDtos?.find(o => o.unitOfMeasure === unitOfMeasure);

    return (
        allowBackOrder ||
        product.isVariantParent ||
        !product.trackInventory ||
        (inventoryAvailability?.availability &&
            inventoryAvailability.availability.messageType !== AvailabilityMessageType.OutOfStock)
    );
}

// it currently doesn't appear that we need to memoize this. We may need to later after we resolve some issues with DataState not caching results as well as it could.
export function getComputedVariantProduct(
    parentProductState: ReturnType<typeof getProductStateByPath>,
    variantProductState: ReturnType<typeof getProductState>,
) {
    if (!variantProductState.value) {
        return parentProductState;
    }

    if (!parentProductState.value) {
        return parentProductState;
    }

    if (parentProductState.value.id === variantProductState.value.id) {
        return parentProductState;
    }

    const { value: variantProduct } = variantProductState;

    const computedProduct = cloneDeep(parentProductState.value);
    Object.assign(computedProduct, {
        productNumber: variantProduct.productNumber,
        customerProductNumber: variantProduct.customerProductNumber,
        smallImagePath: variantProduct.smallImagePath,
        mediumImagePath: variantProduct.mediumImagePath,
        largeImagePath: variantProduct.largeImagePath,
        imageAltText: variantProduct.imageAltText,
        id: variantProduct.id,
        quoteRequired: variantProduct.quoteRequired,
        productTitle: variantProduct.productTitle,
        images: variantProduct.images,
        trackInventory: variantProduct.trackInventory,
        minimumOrderQty: variantProduct.minimumOrderQty,
        canonicalUrl: variantProduct.canonicalUrl,
        unitOfMeasures: variantProduct.unitOfMeasures,
        isVariantParent: false,
        canAddToWishlist: variantProduct.canAddToWishlist,
        cantBuy: variantProduct.cantBuy,
        allowZeroPricing: variantProduct.allowZeroPricing,
        manufacturerItem: variantProduct.manufacturerItem,
        detail: variantProduct.detail,
        content:
            variantProduct.content && variantProduct.content.htmlContent !== ""
                ? variantProduct.content
                : parentProductState.value.content,
        documents:
            variantProduct.documents && variantProduct.documents.length > 0
                ? variantProduct.documents.concat(parentProductState.value.documents || [])
                : parentProductState.value.documents,
        specifications:
            variantProduct.specifications && variantProduct.specifications.length > 0
                ? variantProduct.specifications
                : parentProductState.value.specifications,
        attributeTypes:
            variantProduct.attributeTypes && variantProduct.attributeTypes.length > 0
                ? variantProduct.attributeTypes
                : parentProductState.value.attributeTypes,
        badges:
            variantProduct.badges && variantProduct.badges.length > 0
                ? variantProduct.badges
                : parentProductState.value.badges,
        properties: variantProduct.properties,
    });

    return {
        isLoading: parentProductState.isLoading,
        id: variantProductState.id,
        value: computedProduct,
    };
}
