import * as apiActions from '../Actions';
import first from 'lodash/first';
import isObject from 'lodash/isObject';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import flatten from 'lodash/flatten';
import find from 'lodash/find';
import uniq from 'lodash/uniq';
import { t } from 'Framework/i18n';
import Api from 'Framework/Api';
import { createActionWithGlobalState } from 'Utils/Redux/createActionWithGlobalState';
import { getConf } from '../../configuration';
import { Services } from '../../ServiceLocator';
import {
    dismissError,
    reportConfigurationNotFoundError,
    reportError,
    setVisible,
    setWorkInProgress,
} from '../UI/Actions';
import { fetchFonts } from '../../Reducers/Fonts/Actions';
import { addImagesFromDesignArea } from '../../Reducers/ImageGallery/Actions';
import {
    getSelectedAmounts,
    getChildren,
    getClient,
    getDeepestChildIdentifiers,
    getConfiguration,
    getConfigurator,
    isDesignerProductPreviewMode,
    getItem,
} from './Selectors';
import { getAvailableConfigurationModes } from '../ConfigurationMode/Selectors';
import { EventTypes } from '../../Constant';
import { getUsedCustomFontsInEditor } from '../Fonts/Selectors';
import { addCustomFonts } from '../Fonts/Actions';
import { patchViewImages } from './Modifiers';
import { setInitialDesignData } from '../DesignData/Actions';
import { createInitialDesignDataFromDesignAreas } from '../DesignArea/Transformers';
import { getDesignData } from '../DesignData/Reducer';
import { selectConfigurationMode } from '../ConfigurationMode/Actions';

export const RECEIVE_CONFIGURATION = 'RECEIVE_CONFIGURATION';
export const RECEIVE_DESIGNER_DATA = 'RECEIVE_DESIGNER_DATA';
export const SELECT_OPTIONCLASSIFICATION = 'SELECT_OPTIONCLASSIFICATION';
export const RECEIVE_OPTIONS = 'RECEIVE_OPTIONS';
export const RECEIVE_SWITCHOPTIONSRESULT = 'RECEIVE_SWITCHOPTIONSRESULT';
export const RECEIVE_CALCULATION = 'RECEIVE_CALCULATION';
export const INVALIDATE_CALCULATION = 'INVALIDATE_CALCULATION';
export const RECEIVE_OPTIONDETAILS = 'RECEIVE_OPTIONDETAILS';
export const RECEIVE_SAVEDCONFIGURATION = 'RECEIVE_SAVEDCONFIGURATION';
export const RECEIVE_VALIDATION = 'RECEIVE_VALIDATION';
export const RECEIVE_CONFIGURATIONVARIANTS = 'RECEIVE_CONFIGURATIONVARIANTS';
export const RECEIVE_CODESNIPPETS = 'RECEIVE_CODESNIPPETS';
export const RECEIVE_CLIENT_DATA = 'RECEIVE_CLIENT_DATA';
export const RECEIVE_INSPIRATIONS = 'RECEIVE_INSPIRATIONS';
export const SET_CUSTOM_TITLE = 'SET_CUSTOM_TITLE';
export const SET_LOAD_CODE = 'SET_LOAD_CODE';
export const SET_LOAD_IDENTIFIER = 'SET_LOAD_IDENTIFIER';
export const SET_STOCK_INFORMATION_OPTION = 'SET_STOCK_INFORMATION_OPTION';
export const SET_SELECTED_VARIANT_IDENTIFIER = 'SET_SELECTED_VARIANT_IDENTIFIER';
export const SET_CALCULATION_ENABLED = 'SET_CALCULATION_ENABLED';
export const SET_CALCULATION_ERRORS_RESOLVED = 'SET_CALCULATION_ERRORS_RESOLVED';
export const SET_CALCULATION_AUTO_RESOLVE_ENABLED = 'SET_CALCULATION_AUTO_RESOLVE_ENABLED';
export const SET_OPTIONCLASSIFICATION_CHECKED = 'SET_OPTIONCLASSIFICATION_CHECKED';
export const SET_CONFLICTING_SWITCHOPTION_DATA = 'SET_CONFLICTING_SWITCHOPTION_DATA';
export const SET_SELECTED_AMOUNT = 'SET_SELECTED_AMOUNT';
export const SET_OPTION_INPUT_TEXT = 'SET_OPTION_INPUT_TEXT';
export const RESET_OPTION_INPUT_TEXTS = 'RESET_OPTION_INPUT_TEXTS';
export const SWITCH_SELECTED_AMOUNT = 'SWITCH_SELECTED_AMOUNT';
export const SAVE_BULKNAMES = 'SAVE_BULKNAMES';
export const CLEAR_BULKNAMES = 'CLEAR_BULKNAMES';
export const DELETE_SELECTED_AMOUNT = 'DELETE_SELECTED_AMOUNT';
export const SET_SELECTED_CALCULATION = 'SET_SELECTED_CALCULATION';
export const SET_COMMENT = 'SET_COMMENT';
export const SET_SELECTED_AMOUNTS = 'SET_SELECTED_AMOUNTS';
export const DELETE_SELECTED_AMOUNTS = 'DELETE_SELECTED_AMOUNTS';

export const receiveConfiguration = (json, { checkValidation }) => async (dispatch, getState) => {
    const prevState = getState();

    dispatch(setCalculationEnabled(false));

    if (checkValidation) {
        dispatch(inspectValidationResults(json));
    }

    const action = dispatch({
        type: RECEIVE_CONFIGURATION,
        data: patchViewImages(json),
        receivedAt: Date.now(),
    });

    dispatch(inspectCheckResults());

    const { configurator } = getState();
    const { configuration, selectedoptionclassification, loadIdentifier } = configurator;
    const { designer: designerEnabled } = getAvailableConfigurationModes(getState());

    if (configurator.loadCode) {
        dispatch(addImagesFromDesignArea(configurator));
    }

    dispatch(deleteMissingVariants(configurator));

    // load first optionclassification if not selected yet
    if (!selectedoptionclassification.identifier && getConf('showInitialOptionclassification')) {
        dispatch(loadInitialOptionclassification(configuration));
    }

    if (getConf('loadInspirations')) {
        dispatch(fetchInspirations(configuration.item.identifier));
    }

    dispatch(setVisible('configurationvariantSelection', false));

    if (
        prevState.configurator.configuration.item.identifier &&
        prevState.configurator.configuration.item.identifier !== configuration.item.identifier
    ) {
        dispatch(fetchConfigurationVariants(configuration.item.identifier));
    }

    applyVariantAmountPreSelection(configurator);

    setDefaultSelectedVariantIdentifier(getConfigurator(getState()), loadIdentifier);

    if (designerEnabled) {
        await dispatch(fetchFonts());
        dispatch(addCustomFonts(getUsedCustomFontsInEditor(getState())));
        await dispatch(loadDesignerData(getConfiguration(getConfigurator(getState()))));
        const initialDesignData = createInitialDesignDataFromDesignAreas(
            get(getState(), 'designArea.filteredDesignAreas') || [],
            getDesignData(getConfigurator(getState()))
        );
        dispatch(setInitialDesignData(initialDesignData));
    }

    // enable calculation and trigger it
    dispatch(setCalculationEnabled(true));
    dispatch(invalidateCalculation());
    dispatch(doCalculation(getConfiguration(getConfigurator(getState()))));
    return action;
};

export const deleteMissingVariants = configurator => async dispatch => {
    const children = getChildren(configurator).map(variant => variant.identifier);
    const selectedAmounts = getSelectedAmounts(configurator);
    const item = get(configurator, 'configuration.item');
    const result = [];
    const itemGroupChildren = get(item, 'itemGroup.children', []);
    if (itemGroupChildren.length > 0) {
        for (const itemGroup of item.itemGroup.children) {
            result.push(Object.values(getDeepestChildIdentifiers(itemGroup)));
        }
    } else {
        result.push(item.identifier);
    }
    const flattenendSubChildren = flatten(result);
    for (const selectedVariant in selectedAmounts) {
        if (!selectedAmounts.hasOwnProperty(selectedVariant)) {
            continue;
        }

        if (
            children.length !== 0 &&
            children.indexOf(selectedVariant) === -1 &&
            flattenendSubChildren.indexOf(selectedVariant) === -1
        ) {
            dispatch(deleteSelectedAmount(selectedVariant));

            dispatch(reportError(t('missingVariantError', { selectedVariant: selectedVariant })));
        }
    }
};

export const setDefaultSelectedVariantIdentifier = (configurator, loadIdentifier) => {
    const variantChildren = getChildren(configurator).map(variant =>
        variant.itemGroup ? variant.itemGroup.children : variant
    );
    const children = uniq(flatten(variantChildren).map(child => child.identifier));
    const firstSelectedAmountEntry = first(Object.keys(getSelectedAmounts(configurator)));
    const item = getItem(configurator);

    let selectedVariant = children.length ? children[0] : null;

    if (children.indexOf(loadIdentifier) !== -1) {
        selectedVariant = loadIdentifier;
    } else if (firstSelectedAmountEntry) {
        selectedVariant = firstSelectedAmountEntry;
    } else {
        selectedVariant = item.identifier;
    }

    Services.configurator.setSelectedVariantIdentifier(selectedVariant);
};

export const applyVariantAmountPreSelection = configurator => {
    const designerPreSelection = getConf('designerPreSelection', {});
    const itemAmounts = designerPreSelection.itemAmounts;

    if (itemAmounts) {
        const children = getChildren(configurator).map(variant => variant.identifier);

        for (const preSelectionVariant in itemAmounts) {
            if (!itemAmounts.hasOwnProperty(preSelectionVariant)) {
                continue;
            }

            const amount = +itemAmounts[preSelectionVariant];

            if (children.indexOf(preSelectionVariant) !== -1 && amount > 0) {
                Services.configurator.setSelectedAmount(preSelectionVariant, amount);
            }
        }
    }
};

export const selectOptionclassification = (
    optionclassification,
    triggerFetchOptions = false,
    showOptionlist = false
) => (dispatch, getState) => {
    if (getState().ui.visibilityMap.startHere) {
        dispatch(setVisible('startHere', false));
    }
    const action = dispatch({
        type: SELECT_OPTIONCLASSIFICATION,
        optionclassification,
    });

    const { configuration } = getState().configurator;
    if (triggerFetchOptions) {
        Services.analytics.triggerEvent(EventTypes['select_component'], optionclassification.identifier);
        Services.configurator.loadOptions(
            configuration.item.identifier,
            optionclassification.identifier,
            configuration
        );
    }

    dispatch(setVisible('optionlist', showOptionlist));

    return action;
};

export const receiveOptions = (json, extraData) => (dispatch, getState) => {
    // it should wait until the options are loaded for the optionclassification...
    const { optionclassification } = extraData;

    // make sure the received option list belongs to the selected optionclassification,
    // it might have changed since the request was sent
    if (optionclassification === getState().configurator.selectedoptionclassification.identifier) {
        if (getConf('checkOptionclassificationOnSelect')) {
            dispatch(setOptionclassificationChecked(optionclassification));
        }
        return dispatch({
            type: RECEIVE_OPTIONS,
            data: json,
            receivedAt: Date.now(),
        });
    }
};

export const receiveSwitchoptionsresult = (json, extraData) => (dispatch, getState) => {
    const { switchOptionData, noCheck, setChecked, refreshOptions } = extraData;

    const action = dispatch({
        type: RECEIVE_SWITCHOPTIONSRESULT,
        data: patchViewImages(json),
        receivedAt: Date.now(),
    });
    const { configurator } = getState();
    const { configuration, selectedoptionclassification } = configurator;

    // if it was sent with noCheck param, there is nothing to do with the check results..
    // it should be a temporary configuration state
    if (noCheck) {
        return action;
    }

    const checkResultOk = dispatch(inspectCheckResults());
    if (checkResultOk) {
        if (setChecked) {
            dispatch(setOptionclassificationChecked(first(Object.keys(switchOptionData))));
        }
        // do it only when there is a selected option classification
        if (refreshOptions && selectedoptionclassification) {
            Services.configurator.loadOptions(
                configuration.item.identifier,
                selectedoptionclassification.identifier,
                configuration
            );
        }
    } else {
        dispatch(setConflictingSwitchoptionData(switchOptionData));
    }

    return action;
};

export const receiveCalculation = json => ({
    type: RECEIVE_CALCULATION,
    data: json,
    receivedAt: Date.now(),
});

export const invalidateCalculation = () => ({
    type: INVALIDATE_CALCULATION,
});

export const setCalculationErrorsResolved = () => ({
    type: SET_CALCULATION_ERRORS_RESOLVED,
});

export const setCalculationAutoResolveEnabled = enabled => {
    return {
        type: SET_CALCULATION_AUTO_RESOLVE_ENABLED,
        enabled,
    };
};

export const skipNextAutoResolve = () => setCalculationAutoResolveEnabled('skipNext');

export const receiveOptionDetails = json => dispatch => {
    dispatch(setVisible('optionDetails', true));
    return dispatch({
        type: RECEIVE_OPTIONDETAILS,
        data: json,
        receivedAt: Date.now(),
    });
};

export const clearOptionDetails = () => ({
    type: RECEIVE_OPTIONDETAILS,
    data: {},
    receivedAt: Date.now(),
});

export const receiveSavedConfiguration = json => ({
    type: RECEIVE_SAVEDCONFIGURATION,
    data: patchViewImages(json),
    receivedAt: Date.now(),
});

export const receiveValidation = json => (dispatch, getState) => {
    const action = dispatch({
        type: RECEIVE_VALIDATION,
        data: json,
        receivedAt: Date.now(),
    });
    const validationResult = getState().configurator.validation;
    if (isObject(validationResult) && validationResult.success === false) {
        dispatch(setVisible('validationResult', true));
        throw 'Invalid configuration!';
    }
    return action;
};

export const receiveConfigurationVariants = json => ({
    type: RECEIVE_CONFIGURATIONVARIANTS,
    data: json,
    receivedAt: Date.now(),
});

export const receiveCodesnippets = json => ({
    type: RECEIVE_CODESNIPPETS,
    data: json,
    receivedAt: Date.now(),
});

export const fetchConfigurationForDesignerProductPreview = () => async (dispatch, getState) => {
    const itemIdentifier = getState().configurator.loadIdentifier;

    dispatch(setCalculationEnabled(false));
    await dispatch(
        apiActions.fetchData('configuration/loadbyitemidentifier/' + itemIdentifier, json => ({
            type: RECEIVE_CONFIGURATION,
            data: patchViewImages(json),
            receivedAt: Date.now(),
        }))
    );
    setDefaultSelectedVariantIdentifier(getConfigurator(getState()), itemIdentifier);

    const { designer: designerEnabled } = getAvailableConfigurationModes(getState());

    if (designerEnabled) {
        dispatch(selectConfigurationMode('designer'));
        await dispatch(loadDesignerData(getConfiguration(getConfigurator(getState()))));
        const initialDesignData = createInitialDesignDataFromDesignAreas(
            get(getState(), 'designArea.filteredDesignAreas') || [],
            getDesignData(getConfigurator(getState()))
        );
        dispatch(setInitialDesignData(initialDesignData));
    }
};

export const receiveClientData = json => (dispatch, getState) => {
    dispatch({
        type: RECEIVE_CLIENT_DATA,
        data: json,
        receivedAt: Date.now(),
    });

    if (json.success === false) {
        return dispatch(reportError(t('clientValidationError')));
    }

    const client = getClient(getState().configurator);

    Api.setChannel(client.channel);

    const requests = [];

    if (isDesignerProductPreviewMode()) {
        // initial configuration is not needed
    } else {
        requests.push(Services.configurator.loadInitialConfiguration());
    }
    requests.push(Services.configurator.loadCodeSnippets());

    return Promise.all(requests);
};

export const receiveInspirations = json => ({
    type: RECEIVE_INSPIRATIONS,
    data: json,
});

export const setCustomTitle = title => ({
    type: SET_CUSTOM_TITLE,
    title,
});

export const inspectCheckResults = () => (dispatch, getState) => {
    const { ui, configurator } = getState();
    const { configuration } = configurator;
    if (isObject(configuration.check_results)) {
        if (configuration.check_results.status) {
            if (ui.visibilityMap.incompatibility) {
                dispatch(setVisible('incompatibility', false));
            }
            return true;
        } else {
            dispatch(setVisible('incompatibility', true));
            return false;
        }
    }
    return true;
};

export const inspectValidationResults = configuration => dispatch => {
    const validationResult = get(configuration, 'information.validation_result');
    if (isObject(validationResult) && validationResult.success === false) {
        dispatch(setVisible('validationResult', true));
    }
};

export const fetchConfiguration = itemidentifier => {
    const selectOptions = getConf('selectOptions');
    let urlToCall = `configuration/loadbyitemidentifier/${itemidentifier}`;
    if (selectOptions) {
        const switchOptionData = JSON.stringify(getSwitchOptionData(selectOptions));
        urlToCall = `${urlToCall}?_switch_options=${switchOptionData}`;
    }

    return apiActions.fetchData(urlToCall, receiveConfiguration);
};

export const fetchConfigurationByCode = (code, checkValidation = true) => {
    return apiActions.fetchData('configuration/loadbyconfigurationcode/' + code, receiveConfiguration, {
        checkValidation,
    });
};

export const fetchConfigurationByCodeAsync = (code, checkValidation = true) => {
    return apiActions.fetchDataAsync('configuration/loadbyconfigurationcode/' + code, receiveConfiguration, {
        checkValidation,
    });
};

export const fetchOptions = (itemidentifier, optionclassification, configuration) => {
    return apiActions.postData(
        'options/list/' + itemidentifier + '/' + optionclassification,
        { configuration },
        receiveOptions,
        {}, // config
        { optionclassification } // extraData
    );
};

export const switchOptionsBasic = (configuration, switchedOptions, noCheck = false) => {
    const config = {};
    if (noCheck) {
        config.params = { nocheck: 1 };
    }
    return apiActions.postData(
        'configuration/switchoption',
        { configuration, switchedOptions },
        data => ({
            // dummy receiver
            type: 'RECEIVE_SWITCHOPTIONSRESULT_BASIC',
            data,
        }),
        config
    );
};

export const switchOptions = (
    configuration,
    switchOptionData,
    noCheck = false,
    refreshOptions = false,
    setChecked = false
) => dispatch => {
    return dispatch(switchOptionsBasic(configuration, switchOptionData, noCheck)).then(action => {
        const extraData = { switchOptionData, noCheck, refreshOptions, setChecked };
        return dispatch(receiveSwitchoptionsresult(action.data, extraData));
    });
};

export const doCalculation = () => (dispatch, getState) => {
    const configuration = getConfiguration(getConfigurator(getState()));
    if (!configuration.item.identifier) {
        return {};
    }
    return dispatch(apiActions.postDataAsync('calculation', configuration, receiveCalculation));
};

export const fetchOptionDetails = (optionidentifier, configuration) => {
    return apiActions.postData('options/detail/' + optionidentifier, { configuration }, receiveOptionDetails);
};

export const saveConfiguration = (configuration, configurationtype) => {
    return apiActions.postDataAsync(
        'configurations',
        {
            configuration,
            configurationtype,
        },
        receiveSavedConfiguration
    );
};

export const validateConfiguration = configuration => {
    return apiActions.postData('configuration/validate', { configuration }, receiveValidation);
};

export const fetchConfigurationVariants = itemidentifier => {
    return apiActions.fetchData(
        'items/getconfigurationvariants/' + itemidentifier,
        receiveConfigurationVariants
    );
};

export const fetchCodesnippets = () => {
    return apiActions.fetchData('codesnippets/getcurrent', receiveCodesnippets);
};

export const fetchClientData = () => {
    return apiActions.fetchData('client', receiveClientData);
};

export const fetchInspirations = itemidentifier => {
    return apiActions.fetchData('inspirations/' + itemidentifier, receiveInspirations);
};

export const setLoadCode = (loadCode = null) => ({
    type: SET_LOAD_CODE,
    loadCode,
});

export const setLoadIdentifier = loadIdentifier => ({
    type: SET_LOAD_IDENTIFIER,
    loadIdentifier,
});

export const setStockInformationOption = option => ({
    type: SET_STOCK_INFORMATION_OPTION,
    stockInformationOption: option,
});

export const setSelectedVariantIdentifier = identifier => ({
    type: SET_SELECTED_VARIANT_IDENTIFIER,
    selectedVariantIdentifier: identifier,
});

export const setCalculationEnabled = enabled => ({
    type: SET_CALCULATION_ENABLED,
    enabled,
});

export const setOptionclassificationChecked = optionclassification => (dispatch, getState) => {
    if (typeof optionclassification !== 'object') {
        optionclassification = find(getState().configurator.configuration.optionclassifications, {
            identifier: optionclassification,
        });
    }
    if (!getState().configurator.checkedOptionclassifications[optionclassification.identifier]) {
        return dispatch({
            type: SET_OPTIONCLASSIFICATION_CHECKED,
            optionclassification,
        });
    }
};

export const setConflictingSwitchoptionData = switchOptionData => ({
    type: SET_CONFLICTING_SWITCHOPTION_DATA,
    switchOptionData,
});

export const fetchInitialConfiguration = () => (dispatch, getState) => {
    const { loadCode, loadIdentifier } = getState().configurator;
    const defaultLoadIdentifier = getConf('defaultLoadIdentifier');

    if (loadCode) {
        // Load the configuration by code
        return dispatch(fetchConfigurationByCode(loadCode)).then(configuration => {
            if (configuration.error) {
                dispatch(reportConfigurationNotFoundError(t('loadConfiguration.error.loadingFailed')));
            } else {
                Services.analytics.triggerEvent(EventTypes.load_item, configuration.data.item.identifier);
                dispatch(setLoadCode(null));
            }
        });
    } else if (getConf('loadVariantsFirst')) {
        // Load variants by identifier
        dispatch(setVisible('configurationvariantSelection', true));

        return dispatch(fetchConfigurationVariants(loadIdentifier)).then(result => {
            // if the result contains error just return, do nothing else
            if (result.error) {
                return;
            }

            const { configurationvariants, loadIdentifier } = getState().configurator;

            // If there is only one variant or the list is empty, fall back to fetchConfiguration
            if (isArray(configurationvariants)) {
                if (configurationvariants.length === 0) {
                    // Load the options for the initially selected optionclassification
                    dispatch(fetchConfiguration(loadIdentifier));
                }
                if (configurationvariants.length === 1) {
                    // There is only one variant in the list, just load it
                    dispatch(fetchConfiguration(configurationvariants[0].identifier));
                }
            }
        });
    } else if (loadIdentifier === '__empty__') {
        return Promise.resolve();
    } else if (loadIdentifier) {
        return dispatch(fetchConfiguration(loadIdentifier));
    } else if (defaultLoadIdentifier) {
        return dispatch(fetchConfiguration(defaultLoadIdentifier));
    }
};

export const loadInitialOptionclassification = configuration => dispatch => {
    const { identifier } = configuration.item;
    const optionclassification = get(configuration, 'optionclassifications[0]');

    dispatch(selectOptionclassification(optionclassification));

    Services.configurator.loadOptions(identifier, optionclassification.identifier, configuration);

    dispatch(setVisible('optionlist', true));
};

export const resolveConflicts = () => (dispatch, getState) => {
    const { configurator } = getState();
    const { configuration } = configurator;
    const incompatibilityCheckResults = configuration.check_results;

    let p = false;
    if (isArray(incompatibilityCheckResults.check_results)) {
        dispatch(setCalculationEnabled(false)); // disable calculation while waiting for all options to be switched

        for (let result of incompatibilityCheckResults.check_results) {
            if (result.status === false) {
                // check for data to perform automated option switching
                const switchOptionsData = get(result, 'data.switchOptions');
                if (switchOptionsData) {
                    if (p) {
                        p = p.then(action => {
                            return dispatch(switchOptionsBasic(action.data, switchOptionsData, true));
                        });
                    } else {
                        p = dispatch(switchOptionsBasic(configuration, switchOptionsData, true));
                    }
                }
            }
        }

        if (p) {
            p = p
                .then(action => {
                    // after all conflicts resolved, the desired option can be set
                    const { conflictingSwitchOptionData } = getState().configurator;
                    return dispatch(
                        switchOptions(action.data, conflictingSwitchOptionData, false, true, true)
                    );
                })
                .then(action => {
                    if (get(action, 'data.check_results.status') === false) {
                        throw 'There are new conflicts..';
                    }

                    const { configuration } = getState().configurator;

                    // enable calculation and refresh it
                    dispatch(setCalculationEnabled(true));
                    dispatch(invalidateCalculation());
                    dispatch(doCalculation(configuration));

                    // hide the incompatibility dialog
                    dispatch(setVisible('incompatibility', false));

                    return action;
                })
                .catch(e => {
                    // eslint-disable-next-line no-console
                    console.warn('Problem when resolving conflicts:', e);
                });
        } else {
            dispatch(setVisible('incompatibility', false));
            dispatch(setCalculationEnabled(true));
        }
    }
    return p;
};

export const selectOption = (
    option,
    refreshOptionlist = true,
    showOptionDetails = false,
    optionclassification = false
) => (dispatch, getState) => {
    const { configuration, selectedoptionclassification } = getState().configurator;
    if (!optionclassification) {
        optionclassification = selectedoptionclassification;
    }
    Services.analytics.triggerEvent(EventTypes['switch_option'], {
        component: optionclassification.identifier,
        option: option.identifier,
    });

    const switchOptionData = {
        [optionclassification.identifier]: {
            ...option,
            amount: isNaN(parseInt(option.amount)) ? 0 : option.amount,
            check_results: null, // it can mess the response, so should remove it.
        },
    };

    dispatch(setVisible('optionDetails', showOptionDetails));

    return dispatch(switchOptions(configuration, switchOptionData, false, refreshOptionlist, true));
};

export const applyOptionInputText = optionClassification => (dispatch, getState) => {
    const switchOptionData = { [optionClassification.identifier]: [] };
    const { configuration, optionInputTexts } = getState().configurator;
    if (!optionClassification.selectedoptions) {
        return;
    }
    for (let selectedOption of optionClassification.selectedoptions) {
        const inputText = get(
            find(optionInputTexts, ['optionIdentifier', selectedOption.identifier]),
            'inputText',
            null
        );
        if (inputText && inputText !== selectedOption.inputText) {
            switchOptionData[optionClassification.identifier].push({
                ...selectedOption,
                inputText: inputText,
                check_results: null,
            });
        }
    }
    dispatch(resetOptionInputTexts());
    if (switchOptionData[optionClassification.identifier].length > 0) {
        return dispatch(switchOptions(configuration, switchOptionData, false, true, true));
    }
};

export const selectMultipleOptions = optionsList => (dispatch, getState) => {
    const configuration = getConfiguration(getConfigurator(getState()));
    const switchOptionData = getSwitchOptionData(optionsList);
    return dispatch(switchOptions(configuration, switchOptionData, false, false, true));
};

const getSwitchOptionData = optionsList => {
    const switchOptionData = {};
    for (const component of optionsList) {
        switchOptionData[component.identifier] = component.selectedoptions.map(
            ({ identifier, amount, inputText }) => ({
                identifier,
                inputText,
                amount: typeof amount === 'undefined' ? 1 : amount,
                check_results: null,
            })
        );
    }

    return switchOptionData;
};

export const viewOptionDetails = option => (dispatch, getState) => {
    if (!option) {
        return;
    }

    const { configuration, optiondetails } = getState().configurator;
    if (optiondetails.identifier !== option.identifier) {
        dispatch(fetchOptionDetails(option.identifier, configuration));
    } else {
        dispatch(setVisible('optionDetails', true));
    }
};

export const loadConfiguration = () => async (dispatch, getState) => {
    const { loadCode } = getState().configurator;
    if (!loadCode) {
        throw new Error(t('loadConfiguration.error.emptyCode'));
    }
    dispatch(dismissError());
    dispatch(setWorkInProgress('loadConfiguration', true));

    try {
        const action = await dispatch(fetchConfigurationByCodeAsync(loadCode));
        dispatch(setVisible('loadConfiguration', false));
        return action;
    } catch (e) {
        dispatch(dismissError()); // ignore original REQUEST_ERROR, should be handled by the caller
        throw new Error(t('loadConfiguration.error.loadingFailed'));
    } finally {
        dispatch(setWorkInProgress('loadConfiguration', false));
    }
};

export const receiveDesignerData = data =>
    createActionWithGlobalState({
        type: RECEIVE_DESIGNER_DATA,
        data,
        receivedAt: Date.now(),
    });

export const loadDesignerData = configuration => async dispatch => {
    if (!configuration || !configuration.item || !configuration.item.identifier) {
        return dispatch(reportError(t('loadConfigurationModes.error.missingItemIdentifier')));
    }
    dispatch(dismissError());
    dispatch(setWorkInProgress('loadConfigurationModes', true));

    try {
        return await dispatch(
            apiActions.postDataAsync(
                '/configurationmodes/designer/getdata',
                { configuration },
                receiveDesignerData
            )
        );
    } catch (e) {
        return dispatch(reportError(t('loadConfigurationModes.error.loadingFailed')));
    } finally {
        dispatch(setWorkInProgress('loadConfigurationModes', false));
    }
};

export const setSelectedAmount = (identifier, amount, isUpdatedByUser = false) => ({
    type: SET_SELECTED_AMOUNT,
    identifier,
    amount,
    isUpdatedByUser,
});

export const saveBulkNames = (identifier, bulkNamesData) => ({
    type: SAVE_BULKNAMES,
    identifier,
    bulkNamesData,
});

export const clearBulkNames = () => ({
    type: CLEAR_BULKNAMES,
});

export const deleteSelectedAmount = identifier => ({
    type: DELETE_SELECTED_AMOUNT,
    identifier,
});

export const setSelectedAmounts = (variant, sizes, isUpdatedByUser) => ({
    type: SET_SELECTED_AMOUNTS,
    variant,
    sizes,
    isUpdatedByUser,
});

export const deleteSelectedAmounts = (variant, sizes) => ({
    type: DELETE_SELECTED_AMOUNTS,
    variant,
    sizes,
});

export const setComment = comment => ({
    type: SET_COMMENT,
    comment,
});

export const switchSelectedAmount = (srcIdentifier, dstIdentifier) => ({
    type: SWITCH_SELECTED_AMOUNT,
    srcIdentifier,
    dstIdentifier,
});

export const setSelectedCalculation = (identifier, value) => ({
    type: SET_SELECTED_CALCULATION,
    identifier,
    value,
});

export const setOptionInputText = (identifier, value) => ({
    type: SET_OPTION_INPUT_TEXT,
    identifier,
    value,
});

export const resetOptionInputTexts = () => ({
    type: RESET_OPTION_INPUT_TEXTS,
});
