import isApiError from "@insite/client-framework/Common/isApiError";
import caseInsensitiveSort from "@insite/client-framework/Common/Utilities/caseInsensitiveSort";
import {
    createHandlerChainRunner,
    executeAwaitableHandlerChain,
    Handler,
    HasOnError,
    HasOnSuccess,
} from "@insite/client-framework/HandlerCreator";
import {
    GetAlsoPurchasedProductCollectionApiV2Parameter,
    getAlsoPurchasedProductsCollectionV2,
    GetProductCollectionApiV2Parameter,
    getProductCollectionV2,
    GetRelatedProductCollectionApiV2Parameter,
    getRelatedProductsCollectionV2,
} from "@insite/client-framework/Services/ProductServiceV2";
import { StorefrontAccess } from "@insite/client-framework/Services/SettingsService";
import { getSettingsCollection } from "@insite/client-framework/Store/Context/ContextSelectors";
import { getCurrentPage } from "@insite/client-framework/Store/Data/Pages/PageSelectors";
import sortProductCollections from "@insite/client-framework/Store/Data/Products/Handlers/SortProductCollections";
import { getProductsDataView } from "@insite/client-framework/Store/Data/Products/ProductsSelectors";
import { ProductCollectionModel, ProductModel } from "@insite/client-framework/Types/ApiModels";
import sortBy from "lodash/sortBy";

type Parameter = (
    | GetProductCollectionApiV2Parameter
    | GetRelatedProductCollectionApiV2Parameter
    | GetAlsoPurchasedProductCollectionApiV2Parameter
) & { isLoadMore?: boolean } & HasOnSuccess<ProductModel[]> &
    HasOnError<string>;
type Props = {
    apiParameter:
        | GetProductCollectionApiV2Parameter
        | GetRelatedProductCollectionApiV2Parameter
        | GetAlsoPurchasedProductCollectionApiV2Parameter;
    apiResult: ProductCollectionModel;
};

type HandlerType = Handler<Parameter, Props>;

export const DispatchBeginLoadProducts: HandlerType = props => {
    props.dispatch({
        type: "Data/Products/BeginLoadProducts",
        parameter: props.parameter,
    });
};

export const PopulateApiParameter: HandlerType = props => {
    const { onSuccess, onError, isLoadMore, ...parameter } = props.parameter;
    props.apiParameter = parameter;

    if (props.parameter.isLoadMore) {
        const prevApiParams: typeof props.apiParameter = { ...props.apiParameter };
        prevApiParams.page =
            props.apiParameter.page && props.apiParameter.page > 1 ? props.apiParameter.page - 1 : undefined;
        const prevProducts = getProductsDataView(props.getState(), prevApiParams).value;
        if (!prevProducts && props.parameter.page && props.parameter.page > 1) {
            props.apiParameter.page = 1;
            props.apiParameter.pageSize = props.parameter.pageSize! * props.parameter.page;
        }
    }
};

export const RequestDataFromApi: HandlerType = async props => {
    const state = props.getState();
    const currentPage = getCurrentPage(state);
    const { storefrontAccess } = getSettingsCollection(state).productSettings;
    if (
        storefrontAccess === StorefrontAccess.SignInRequiredToBrowse &&
        !state.context.session.isAuthenticated &&
        (currentPage.fields.excludeFromSignInRequired || currentPage.type === "SignInPage")
    ) {
        return false;
    }

    try {
        if ("relationship" in props.apiParameter) {
            props.apiResult = await getRelatedProductsCollectionV2(props.apiParameter);
        } else if ("productId" in props.apiParameter && props.apiParameter?.type === "alsoPurchased") {
            props.apiResult = await getAlsoPurchasedProductsCollectionV2(props.apiParameter);
        } else {
            props.apiResult = await getProductCollectionV2(props.apiParameter);
        }
    } catch (error) {
        if (isApiError(error) && error.status === 400) {
            props.parameter.onError?.(error.errorJson.message);
            return false;
        }

        if (storefrontAccess === StorefrontAccess.SignInRequiredToBrowse && error.status === 403) {
            // auth timed out with SignInRequiredToBrowsee - throw this up as a 401 so DisplayError reloads the site
            error.status = 401;
            error.errorJson = { message: "error" };
        }

        throw error;
    }
};

export const SortFacets: HandlerType = ({ apiResult }) => {
    if (!apiResult) {
        return;
    }

    if (apiResult.brandFacets) {
        apiResult.brandFacets = caseInsensitiveSort(apiResult.brandFacets, "name");
    }

    if (apiResult.productLineFacets) {
        apiResult.productLineFacets = caseInsensitiveSort(apiResult.productLineFacets, "name");
    }

    if (apiResult.priceRange?.priceFacets) {
        apiResult.priceRange.priceFacets = sortBy(apiResult.priceRange.priceFacets, o => o.minimumPrice);
    }

    if (apiResult.categoryFacets) {
        apiResult.categoryFacets = caseInsensitiveSort(apiResult.categoryFacets, "shortDescription");
    }

    if (apiResult.attributeTypeFacets) {
        apiResult.attributeTypeFacets = sortBy(
            apiResult.attributeTypeFacets,
            o => o.sortOrder,
            o => o.nameDisplay,
        );
        apiResult.attributeTypeFacets.forEach(a => {
            if (a.attributeValueFacets) {
                a.attributeValueFacets = sortBy(
                    a.attributeValueFacets,
                    o => o.sortOrder,
                    o => o.valueDisplay,
                );
            }
        });
    }
};

export const SortCollections: HandlerType = async props => {
    await executeAwaitableHandlerChain(sortProductCollections, { products: props.apiResult.products! }, props);
};

export const DispatchCompleteLoadProducts: HandlerType = props => {
    const { onSuccess, onError, isLoadMore, ...parameter } = props.parameter;
    props.apiParameter = parameter;

    if (props.parameter.isLoadMore) {
        const prevApiParams: typeof props.apiParameter = { ...props.apiParameter };
        prevApiParams.page =
            props.apiParameter.page && props.apiParameter.page > 1 ? props.apiParameter.page - 1 : undefined;
        const prevProducts = getProductsDataView(props.getState(), prevApiParams).value;
        if (prevProducts && props.apiResult.products) {
            props.apiResult.products = [...prevProducts, ...props.apiResult.products];
        }
        if (!prevProducts && props.parameter.page && props.parameter.page > 1) {
            props.apiParameter.page = props.parameter.page;
            props.apiParameter.pageSize = props.parameter.pageSize;
        }
    }

    props.dispatch({
        type: "Data/Products/CompleteLoadProducts",
        collection: props.apiResult,
        parameter: props.apiParameter,
    });
};

export const ExecuteOnSuccessCallback: HandlerType = props => {
    props.parameter.onSuccess?.(props.apiResult.products!);
};

export const chain = [
    PopulateApiParameter,
    DispatchBeginLoadProducts,
    RequestDataFromApi,
    SortFacets,
    SortCollections,
    DispatchCompleteLoadProducts,
    ExecuteOnSuccessCallback,
];

const loadProducts = createHandlerChainRunner(chain, "LoadProducts");
export default loadProducts;
