import { createFromProduct, ProductInfo } from "@insite/client-framework/Common/ProductInfo";
import { SafeDictionary } from "@insite/client-framework/Common/Types";
import waitFor from "@insite/client-framework/Common/Utilities/waitFor";
import { getIsWebCrawler } from "@insite/client-framework/Common/WebCrawler";
import { createHandlerChainRunner, Handler } from "@insite/client-framework/HandlerCreator";
import loadRealTimeInventory from "@insite/client-framework/Store/CommonHandlers/LoadRealTimeInventory";
import loadRealTimePricing from "@insite/client-framework/Store/CommonHandlers/LoadRealTimePricing";
import { getSettingsCollection } from "@insite/client-framework/Store/Context/ContextSelectors";
import {
    getProductState,
    getVariantChildrenDataView,
} from "@insite/client-framework/Store/Data/Products/ProductsSelectors";
import { ProductModel } from "@insite/client-framework/Types/ApiModels";

type Parameter = {
    product?: ProductModel;
    variantChildren?: ProductModel[];
    productInfosById?: SafeDictionary<ProductInfo>;
    styledOption?: string;
    waitForDataLoaded?: boolean;
};
type Props = {
    pricingLoaded?: true;
    inventoryLoaded?: true;
};

type HandlerType = Handler<Parameter, Props>;

export const LoadRealTimePrices: HandlerType = async props => {
    if (!props.parameter.product || !props.parameter.productInfosById) {
        return;
    }

    const productIds = Object.keys(props.parameter.productInfosById);
    const productPriceParameters = productIds.map(productId => {
        const productInfo = props.parameter.productInfosById![productId];
        return {
            productId: productInfo!.productId,
            unitOfMeasure: productInfo!.unitOfMeasure,
            qtyOrdered: productInfo!.qtyOrdered,
        };
    });

    props.dispatch(
        loadRealTimePricing({
            productPriceParameters,
            onComplete: pricingProps => {
                if (pricingProps.apiResult?.realTimePricingResults) {
                    const pricing = pricingProps.apiResult.realTimePricingResults!.find(
                        o => o.productId === props.parameter.product?.id,
                    );
                    if (pricing) {
                        props.dispatch({
                            type: "Pages/ProductDetails/CompleteLoadRealTimePricings",
                            pricings: pricingProps.apiResult.realTimePricingResults,
                        });
                    } else {
                        props.dispatch({
                            type: "Pages/ProductDetails/FailedLoadRealTimePricings",
                            productIds,
                        });
                    }
                } else if (pricingProps.error) {
                    props.dispatch({
                        type: "Pages/ProductDetails/FailedLoadRealTimePricings",
                        productIds,
                    });
                }

                props.pricingLoaded = true;
            },
        }),
    );

    if (getSettingsCollection(props.getState()).productSettings.inventoryIncludedWithPricing) {
        await waitFor(() => !!props.pricingLoaded);
    }
};

export const LoadRealTimeInventory: HandlerType = props => {
    const {
        productSettings: { showInventoryAvailability },
        websiteSettings: { includeRealTimeInventoryAndPricingForSeo },
    } = getSettingsCollection(props.getState());
    if (!props.parameter.product || (getIsWebCrawler() && !includeRealTimeInventoryAndPricingForSeo)) {
        return;
    }

    props.dispatch(
        loadRealTimeInventory({
            productIds: [props.parameter.product.id, ...(props.parameter.variantChildren?.map(o => o.id) ?? [])],
            onComplete: realTimeInventoryProps => {
                const realTimeInventory = realTimeInventoryProps?.apiResult;

                if (realTimeInventoryProps?.error) {
                    if (props.parameter.product) {
                        props.dispatch({
                            type: "Pages/ProductDetails/FailedLoadRealTimeInventory",
                            productId: props.parameter.product.id,
                        });
                    }
                } else if (realTimeInventory) {
                    if (
                        realTimeInventory.realTimeInventoryResults &&
                        props.parameter.variantChildren &&
                        props.parameter.product
                    ) {
                        const discontinued: SafeDictionary<boolean> = {};
                        for (const result of realTimeInventory.realTimeInventoryResults) {
                            if (!result.qtyOnHand && showInventoryAvailability) {
                                discontinued[result.productId] = true;
                            }
                        }
                        const initialCount = props.parameter.variantChildren.length;
                        const newVariantChildren = props.parameter.variantChildren.filter(
                            o => !o.isDiscontinued || (o.isDiscontinued && !discontinued[o.id]),
                        );

                        if (initialCount !== newVariantChildren.length) {
                            const variantChildrenDataView = getVariantChildrenDataView(
                                props.getState(),
                                props.parameter.product.id,
                            );
                            const { value, ...otherProps } = variantChildrenDataView;
                            props.dispatch({
                                type: "Data/Products/ReplaceDataView",
                                parameter: {
                                    productId: props.parameter.product.id,
                                    variantChildren: true,
                                },
                                dataView: {
                                    value: newVariantChildren,
                                    ...otherProps,
                                },
                            });

                            const productInfo =
                                props.getState().pages.productDetails.productInfosById?.[props.parameter.product.id] ??
                                createFromProduct(
                                    getProductState(props.getState(), props.parameter.product.id).value ??
                                        props.parameter.product,
                                );

                            props.dispatch({
                                type: "Pages/ProductDetails/UpdateVariantSelection",
                                variantSelection: getVariantSelection(
                                    props.parameter.product,
                                    newVariantChildren,
                                    props.parameter.styledOption,
                                ),
                                variantSelectionCompleted: false,
                                selectedProductInfo: productInfo,
                            });
                        }
                    }

                    props.dispatch({
                        type: "Pages/ProductDetails/CompleteLoadRealTimeInventory",
                        realTimeInventory,
                    });
                }

                props.inventoryLoaded = true;
            },
        }),
    );
};

export const WaitForDataIfNeeded: HandlerType = async props => {
    if (props.parameter.waitForDataLoaded) {
        const checkData = () => {
            return !!props.pricingLoaded && !!props.inventoryLoaded;
        };
        await waitFor(checkData);
    }
};

function getVariantSelection(product?: ProductModel, variantChildren?: ProductModel[], styledOption?: string) {
    const variantSelection: SafeDictionary<string> = {};
    if (!product?.isVariantParent || !variantChildren) {
        return variantSelection;
    }

    if (styledOption || product.defaultChildProductId) {
        const variantChild = styledOption
            ? variantChildren.find(o => o.productNumber.toLowerCase() === styledOption.toLowerCase())
            : variantChildren.find(o => o.id === product.defaultChildProductId);

        variantChild?.childTraitValues?.forEach(o => {
            variantSelection[o.styleTraitId] = o.id;
        });

        if (Object.keys(variantSelection).length > 0) {
            return variantSelection;
        }
    }

    product.variantTraits?.forEach(variantTrait => {
        const defaultVariantValue = variantTrait.traitValues?.find(traitValue => traitValue.isDefault);
        variantSelection[variantTrait.id] = defaultVariantValue?.id ?? "";
    });

    return variantSelection;
}

export const chain = [LoadRealTimePrices, LoadRealTimeInventory, WaitForDataIfNeeded];

const loadProductPricingAndInventory = createHandlerChainRunner(chain, "LoadProductPricingAndInventory");
export default loadProductPricingAndInventory;
