import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { containerConnect } from 'Framework/ComponentContainer';
import createSelector from 'Utils/Function/createSelector';
import {
    getSelectedAmounts,
    getItem,
    getCalculation,
    getSelectedVariantIdentifier,
    getBulkNames,
    showReceiveOfferForm,
} from '../../../Reducers/Configurator/Selectors';
import { Services } from '../../../ServiceLocator';
import { getConf } from '../../../configuration';
import { isActiveBulkName, isBulkNamesSelected } from '../../../Reducers/DesignArea/Selectors';
import * as configuratorActions from '../../../Reducers/Configurator/Actions';
import ProductVariant from '../../Components/ProductVariant';
import getImageUrl from '../../../Utils/getImageUrl';

export const itemGroupMap = (rootItem, fn) => {
    if (typeof fn !== 'function') {
        throw new TypeError('The second argument must be a function!');
    }
    const data = [];
    if (get(rootItem, 'itemGroup.children.length', 0) > 0) {
        for (let item of rootItem.itemGroup.children) {
            const mappedData = fn(item, data);
            mappedData && data.push(mappedData);
        }
    }
    return data;
};

export const createAmountData = (item, selectedAmounts, bulkNames, selectedVariantIdentifier) => {
    const restrictAmountselectionToSelectedVariant = getConf(
        'restrictAmountselectionToSelectedVariant',
        false
    );
    if (isEmpty(item)) {
        return [];
    }
    //check if item has children or if the amountData should be created based on the item data
    let amountData;
    if (get(item, 'itemGroup.children.length', 0) > 0) {
        amountData = itemGroupMap(item, colorGroupItem => ({
            title: colorGroupItem.itemgroupentryTitle,
            identifier: colorGroupItem.identifier,
            amount: get(selectedAmounts, colorGroupItem.identifier, 0),
            url: getImageUrl(colorGroupItem.thumbnail),
            isDisabled:
                restrictAmountselectionToSelectedVariant &&
                colorGroupItem.identifier !== selectedVariantIdentifier,
            bulkNames: get(bulkNames, colorGroupItem.identifier, []),
            sizes: itemGroupMap(colorGroupItem, sizeGroupItem => ({
                size: sizeGroupItem.itemgroupentryTitle,
                bulkNames: get(bulkNames, sizeGroupItem.identifier, []),
                amount: get(selectedAmounts, sizeGroupItem.identifier, 0),
                label: sizeGroupItem.itemgroupentryTitle,
                key: sizeGroupItem.identifier,
                identifier: sizeGroupItem.identifier,
                url: getImageUrl(sizeGroupItem.thumbnail),
            })),
        }));
    } else {
        const itemIdentifier = item && item.identifier;
        amountData = [
            {
                title: item && item.title,
                identifier: itemIdentifier,
                amount: get(selectedAmounts, itemIdentifier, 0),
                url: getImageUrl(item.thumbnail),
                isDisabled:
                    restrictAmountselectionToSelectedVariant && item.identifier !== selectedVariantIdentifier,
                bulkNames: get(bulkNames, itemIdentifier, []),
                sizes: [],
            },
        ];
    }

    return amountData;
};

export const createAmountMap = amountData =>
    amountData.reduce((result, { identifier, amount, sizes }) => {
        if (sizes && sizes.length) {
            return {
                ...result,
                ...sizes.reduce(
                    (result, { identifier, amount }) =>
                        amount > 0 ? { ...result, [identifier]: amount } : result,
                    {}
                ),
            };
        }
        return amount > 0 ? { ...result, [identifier]: amount } : result;
    }, {});

export const createPropsFromState = createSelector(
    getItem,
    getSelectedAmounts,
    getBulkNames,
    getCalculation,
    getSelectedVariantIdentifier,
    (item, selectedAmounts, bulkNames, selectedVariantIdentifier, calculation) => {
        const amountData = createAmountData(item, selectedAmounts, bulkNames, selectedVariantIdentifier);

        const amountMap = createAmountMap(amountData);

        let error = null;

        if (calculation && calculation.success === false) {
            error = {
                code: calculation.code,
                errors: calculation.errors,
            };
        }

        return {
            error,
            amountData,
            amountMap,
        };
    }
);

const findItemsWithAmount = amountData =>
    amountData
        .filter(
            item =>
                item.amount ||
                (item.sizes &&
                    item.sizes.reduce(
                        (amountSum, { amount }) => (amount ? amountSum + amount : amountSum),
                        0
                    ))
        )
        .map(({ identifier }) => identifier);

const moveItemsToTop = (amountData, identifiers) => {
    const inList = ({ identifier }) => identifiers.indexOf(identifier) !== -1;
    const not = f => (...args) => !f(...args);
    return [...amountData.filter(inList), ...amountData.filter(not(inList))];
};

export const ProductVariantContainer = ({
    ProductVariantComponent,
    ProgressComponent,
    getPreviewImages,
    renderPreviewImagesForCheckout,
    amountData,
    ...props
}) => {
    const [loading, setLoading] = useState(false);
    const [previewImages, setPreviewImages] = useState({});
    const [itemsWithAmount] = useState(findItemsWithAmount(amountData));
    const [orderedAmountData, setOrderedAmountData] = useState([]);
    useEffect(() => {
        renderPreviewImagesForCheckout &&
            (async () => {
                setLoading(true);
                try {
                    setPreviewImages(await getPreviewImages());
                } finally {
                    setLoading(false);
                }
            })();
    }, [getPreviewImages, renderPreviewImagesForCheckout]);
    useEffect(() => {
        setOrderedAmountData(moveItemsToTop(amountData, itemsWithAmount));
    }, [itemsWithAmount, amountData]);
    return (
        <ProductVariantComponent
            variantListLoaderElement={ProgressComponent && <ProgressComponent loading={loading} />}
            previewImages={previewImages}
            amountData={orderedAmountData}
            {...props}
        />
    );
};
ProductVariantContainer.propTypes = {
    ProductVariantComponent: PropTypes.elementType.isRequired,
    ProgressComponent: PropTypes.elementType,
    getPreviewImages: PropTypes.func.isRequired,
    renderPreviewImagesForCheckout: PropTypes.bool.isRequired,
    amountData: PropTypes.array.isRequired,
};
ProductVariantContainer.defaultProps = {
    amountData: [],
};

const mapStateToProps = state => ({
    Component: ProductVariantContainer,
    ProductVariantComponent: ProductVariant,
    showReceiveOfferForm: showReceiveOfferForm(state.configurator),
    activeBulkNames: isBulkNamesSelected(state.configurator) && isActiveBulkName(state),
    renderPreviewImagesForCheckout: getConf('renderPreviewImagesForCheckout', true),
    ...createPropsFromState(state.configurator),
});

const mapDispatchToProps = dispatch => ({
    handleChange: (variant, size, amount) =>
        Services.configurator.setSelectedAmount((size || variant).identifier, +amount, true),
    saveConfigurationBulkNames: (identifier, bulkNamesData) =>
        Services.configurator.saveBulkNames(identifier, bulkNamesData),
    handleMultipleAmountInputsChange: (variant, sizes) =>
        dispatch(configuratorActions.setSelectedAmounts(variant, sizes, true)),
    deleteSelectedAmounts: (variant, sizes) =>
        dispatch(configuratorActions.deleteSelectedAmounts(variant, sizes)),
    getPreviewImages: () => Services.configurator.getPreviewImagesForVariants(),
});

export default containerConnect(mapStateToProps, mapDispatchToProps);
