import get from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep';
import escape from 'lodash/escape';
import unescape from 'lodash/unescape';
import immutableSet from 'Utils/Immutable/set';

import { actionTypes } from './Actions';
import { getPreviewUrlFromImageData } from '../ImageGallery/Selectors';
import { hasBackgroundColor } from './Selectors';

export function getDesignData(configuratorState) {
    return get(configuratorState, 'configuration.designdata');
}

export function updateConfiguratorState(state, designAreaIdentifier, designData) {
    const currentDesignData = getDesignData(state) || {};

    return {
        ...state,
        configuration: {
            ...state.configuration,
            designdata: {
                ...currentDesignData,
                [designAreaIdentifier]: {
                    ...currentDesignData[designAreaIdentifier],
                    ...designData,
                },
            },
        },
    };
}

const regExpEscape = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

function shouldItemHaveBackgroundColor(item) {
    if (item.type === 'Text') {
        return hasBackgroundColor(item.style.colors);
    } else if (item.type === 'Image') {
        if (get(item, 'imageData.operations.vectorize')) {
            const objectColors = Object.values(get(item, 'imageData.operations.vectorizeColorsMap') || {});
            return hasBackgroundColor(objectColors);
        }
    }
    return false;
}

function updateItemHasBackgroundColor(state, designAreaIdentifier) {
    const objectsPath = `configuration.designdata.${designAreaIdentifier}.canvasData.objects`;
    const objects = get(state, objectsPath, []);
    return immutableSet(
        state,
        objectsPath,
        objects.map(object => {
            return shouldItemHaveBackgroundColor(object)
                ? {
                      ...object,
                      hasBackgroundColor: true,
                  }
                : object;
        })
    );
}

export function designDataHandler(state, action) {
    const { designAreaIdentifier } = action;

    switch (action.type) {
        case actionTypes.SET_CANVAS_DATA: {
            const { canvasData } = action;
            const nextState = updateConfiguratorState(state, designAreaIdentifier, { canvasData });
            return updateItemHasBackgroundColor(nextState, designAreaIdentifier);
        }
        case actionTypes.SET_CANVAS_DATA_MAP: {
            const { canvasDataMap } = action;
            return Object.keys(canvasDataMap).reduce((state, designAreaIdentifier) => {
                const nextState = updateConfiguratorState(state, designAreaIdentifier, {
                    canvasData: canvasDataMap[designAreaIdentifier],
                });
                return updateItemHasBackgroundColor(nextState, designAreaIdentifier);
            }, state);
        }
        case actionTypes.SET_DESIGN_PRODUCTION_METHOD: {
            const { designProductionMethodIdentifier } = action;
            return updateConfiguratorState(state, designAreaIdentifier, { designProductionMethodIdentifier });
        }
        case actionTypes.SET_COLOR_AMOUNT: {
            const { colorAmount } = action;
            return updateConfiguratorState(state, designAreaIdentifier, { colorAmount });
        }
        case actionTypes.SET_INITIAL_DESIGN_DATA: {
            const { initialDesignData } = action;
            return {
                ...state,
                configuration: {
                    ...state.configuration,
                    designdata: initialDesignData,
                },
            };
        }
        case actionTypes.UPDATE_IMAGE_DATA: {
            const { designAreaIdentifier, imageData, originalImageIdentifier } = action;
            const designdata = cloneDeep(getDesignData(state));

            const areas = designAreaIdentifier ? [designAreaIdentifier] : Object.keys(designdata);

            areas.forEach(area => {
                const objects = get(designdata[area], 'canvasData.objects', []);

                objects.forEach(object => {
                    if (
                        object.imageData &&
                        ((originalImageIdentifier &&
                            object.imageData.identifier === originalImageIdentifier) ||
                            object.imageData.identifier === imageData.identifier)
                    ) {
                        object.imageData = cloneDeep(imageData);

                        object.src = getPreviewUrlFromImageData(object.imageData.preview);
                    }
                });
            });

            return {
                ...state,
                configuration: {
                    ...state.configuration,
                    designdata,
                },
            };
        }
        case actionTypes.SET_DESIGN_AREA_TEXTS_COLOR: {
            const objectsPath = `configuration.designdata.${action.identifier}.canvasData.objects`;
            const objects = get(state, objectsPath, []);

            return immutableSet(
                state,
                objectsPath,
                objects.map(object => {
                    if (object.type !== 'Text') {
                        return object;
                    }

                    const content = object.content
                        .replace(/#[0-9a-f]{3,6}/gi, action.color.value)
                        .replace(/rgb\([^)]+\)/gi, action.color.value)
                        .replace(/data-color="([^"]+)"/gi, `data-color='${action.color.identifier}'`);
                    return {
                        ...object,
                        content,
                    };
                })
            );
        }
        case actionTypes.REPLACE_DESIGN_AREA_TEXTS_COLOR: {
            const objectsPath = `configuration.designdata.${action.identifier}.canvasData.objects`;
            const objects = get(state, objectsPath, []);

            return immutableSet(
                state,
                objectsPath,
                objects.map(object => {
                    if (object.type !== 'Text') {
                        return object;
                    }

                    const colorToReplace = Object.values(action.oldValue)
                        .map(regExpEscape)
                        .join('|');

                    const content = object.content
                        .replace(/data-color="(.*?)"/gi, (originalAttr, dataAttr) => {
                            return unescape(dataAttr) === action.oldColorData
                                ? `data-color="${escape(action.colorData)}"`
                                : originalAttr;
                        })
                        .replace(
                            new RegExp(`<([^<]*)color: (${colorToReplace});([^>]*)>`, 'gi'),
                            `<$1color: ${action.value};$3>`
                        );

                    return {
                        ...object,
                        content,
                    };
                })
            );
        }
        default: {
            return state;
        }
    }
}
