'use strict';

import {
    actionTypes,
    createDesignDataAction,
    setCanvasData,
    setColorAmount,
    setDesignProductionMethod,
    setInitialDesignData,
    updateLinkedImagesWithImageData,
} from '../Actions';
import { designDataHandler } from '../Reducer';
import { replaceColorOnOperations } from '../Modifiers';
import { createInitialDesignDataFromDesignAreas } from '../../DesignArea/Transformers';

// pre-defined mock data constants
const designAreaIdentifier = 'area1';
const configuratorState = {
    configuration: {
        designdata: {
            [designAreaIdentifier]: {
                canvasData: {
                    objects: [
                        {
                            content: 'Hello',
                            rotation: 270,
                            scaling: 6.87943856080878,
                            type: 'Text',
                            x: 120.5,
                            y: 355.5,
                        },
                    ],
                },
            },
        },
    },
};
const mockReducerState = {
    designArea: {
        selectedDesignArea: {
            identifier: designAreaIdentifier,
        },
    },
    configurator: configuratorState,
};
const canvasData = {
    objects: [{}],
};
const getState = () => mockReducerState;

describe('Reducers/DesignData', () => {
    test('createDesignDataAction creates and dispatches the correct action', () => {
        const actionObject = { type: 'THE_ACTION', foo: 'bar' };
        const actionFunc = createDesignDataAction(actionObject);
        const dispatch = jest.fn();
        actionFunc(dispatch, getState);
        expect(dispatch).toBeCalledWith({
            ...actionObject,
            designAreaIdentifier,
        });
    });
    test('setCanvasData creates and dispatches the correct action', () => {
        const actionFunc = setCanvasData(canvasData);
        const dispatch = jest.fn();
        actionFunc(dispatch, getState);
        expect(dispatch).toBeCalledWith({
            type: actionTypes.SET_CANVAS_DATA,
            canvasData,
            designAreaIdentifier,
        });
    });
    test('setColorAmount creates and dispatches the correct action', () => {
        const colorAmount = 5;
        const actionFunc = setColorAmount(colorAmount);
        const dispatch = jest.fn();
        actionFunc(dispatch, getState);
        expect(dispatch).toBeCalledWith({
            type: actionTypes.SET_COLOR_AMOUNT,
            colorAmount,
            designAreaIdentifier,
        });
    });
    test('setDesignProductionMethod creates and dispatches the correct action', () => {
        const designProductionMethodIdentifier = 'print';
        const actionFunc = setDesignProductionMethod(designProductionMethodIdentifier);
        const dispatch = jest.fn();
        actionFunc(dispatch, getState);
        expect(dispatch).toBeCalledWith({
            type: actionTypes.SET_DESIGN_PRODUCTION_METHOD,
            designProductionMethodIdentifier,
            designAreaIdentifier,
        });
    });
    test('setInitialDesignData creates the correct action', () => {
        const initialDesignData = {
            area1: {
                canvasData,
                designProductionMethodIdentifier: 'print',
                colorAmount: 5,
            },
        };
        expect(setInitialDesignData(initialDesignData)).toEqual({
            type: actionTypes.SET_INITIAL_DESIGN_DATA,
            initialDesignData,
        });
    });
    test('designDataHandler returns the correct state for setCanvasData action', () => {
        const action = {
            type: actionTypes.SET_CANVAS_DATA,
            canvasData,
            designAreaIdentifier,
        };
        const state = designDataHandler(configuratorState, action);
        expect(state.configuration.designdata[designAreaIdentifier].canvasData).toEqual(canvasData);
    });
    test('designDataHandler returns the correct state for setColorAmount action', () => {
        const colorAmount = 3;
        const action = {
            type: actionTypes.SET_COLOR_AMOUNT,
            colorAmount,
            designAreaIdentifier,
        };
        const state = designDataHandler(configuratorState, action);
        expect(state.configuration.designdata[designAreaIdentifier].colorAmount).toEqual(colorAmount);
    });
    test('designDataHandler returns the correct state for setDesignProductionMethod action', () => {
        const designProductionMethodIdentifier = 'print';
        const action = {
            type: actionTypes.SET_DESIGN_PRODUCTION_METHOD,
            designProductionMethodIdentifier,
            designAreaIdentifier,
        };
        const state = designDataHandler(configuratorState, action);
        expect(state.configuration.designdata[designAreaIdentifier].designProductionMethodIdentifier).toEqual(
            designProductionMethodIdentifier
        );
    });
    test('designDataHandler returns the correct state for setInitialDesignData action', () => {
        const initialDesignData = {
            area1: {
                canvasData: null,
                colorAmount: 1,
                designProductionMethodIdentifier: 'print',
            },
            area2: {
                canvasData: null,
                colorAmount: 1,
                designProductionMethodIdentifier: 'print',
            },
        };
        const action = {
            type: actionTypes.SET_INITIAL_DESIGN_DATA,
            initialDesignData,
            designAreaIdentifier,
        };
        const state = designDataHandler(configuratorState, action);
        // the initial design area values should be assigned only to the non-existing values,
        // so if there was a canvasData it should remain after this operation
        expect(state.configuration.designdata).toEqual({
            ...initialDesignData,
            ...configuratorState.configuration.designdata,
            [designAreaIdentifier]: {
                ...initialDesignData[designAreaIdentifier],
            },
        });
    });

    test('createInitialDesignDataFromDesignAreas replaces the invalid design production method', () => {
        const designAreas = [
            {
                identifier: 'area1',
                designProductionMethods: [
                    {
                        identifier: 'digital_printing',
                    },
                ],
            },
            {
                identifier: 'area2',
                designProductionMethods: [
                    {
                        identifier: 'pad_printing',
                        options: {
                            maxColorAmount: 4,
                        },
                    },
                ],
            },
        ];
        const designData = {
            area1: {
                canvasData: null,
                designProductionMethodIdentifier: 'digital_printing',
            },
            area2: {
                canvasData: {
                    objects: [
                        {
                            type: 'Image',
                        },
                    ],
                },
                colorAmount: 1,
                designProductionMethodIdentifier: 'digital_printing',
            },
        };
        const expectedResult = {
            area1: {
                canvasData: null,
                designProductionMethodIdentifier: 'digital_printing',
            },
            area2: {
                canvasData: null,
                colorAmount: 1,
                designProductionMethodIdentifier: 'pad_printing',
            },
        };

        const initialDesignData = createInitialDesignDataFromDesignAreas(designAreas, designData);
        expect(initialDesignData).toEqual(expectedResult);
    });

    test('replaceColorOnOperations returns the correct operations', () => {
        const colors = {
            red: { identifier: '{"color":"red","palette":"default"}', value: 'rgb(255, 0, 0)' },
            black: { identifier: '{"color":"black","palette":"default"}', value: 'rgb(0, 0, 0)' },
            green: { identifier: '{"color":"green","palette":"default"}', value: 'rgb(0, 255, 0)' },
        };

        const prevOperations = {
            vectorizeColorsMap: {
                '#000000': colors.red,
            },
        };

        expect(replaceColorOnOperations(prevOperations, colors.red, colors.black)).toEqual({
            vectorizeColorsMap: {
                '#000000': colors.black,
            },
        });

        expect(replaceColorOnOperations(prevOperations, colors.green, colors.black)).toEqual({
            vectorizeColorsMap: {
                '#000000': colors.red,
            },
        });
    });

    test(
        'updateLinkedImagesWithImageData updates the imageData property of the canvas images ' +
            'where the imageData.identifier is the given imageIdentifier',
        () => {
            const dispatch = jest.fn();
            const getState = () => ({
                configurator: {
                    configuration: {
                        designdata: {
                            ['area1']: {
                                canvasData: {
                                    objects: [
                                        {
                                            type: 'Image',
                                            identifier: 'a canvas image identifier',
                                            imageData: {
                                                identifier: 'image1',
                                                displayColorPreview: false,
                                            },
                                        },
                                    ],
                                },
                            },
                            ['area2']: {
                                canvasData: {
                                    objects: [
                                        {
                                            type: 'Image',
                                            identifier: 'other canvas image identifier',
                                            imageData: {
                                                identifier: 'image1',
                                                displayColorPreview: false,
                                            },
                                        },
                                    ],
                                },
                            },
                        },
                    },
                },
            });

            const expectedCanvasDataMap = {
                ['area1']: {
                    objects: [
                        {
                            type: 'Image',
                            identifier: 'a canvas image identifier',
                            imageData: {
                                identifier: 'image1',
                                displayColorPreview: true,
                            },
                        },
                    ],
                },
                ['area2']: {
                    objects: [
                        {
                            type: 'Image',
                            identifier: 'other canvas image identifier',
                            imageData: {
                                identifier: 'image1',
                                displayColorPreview: true,
                            },
                        },
                    ],
                },
            };

            updateLinkedImagesWithImageData('image1', { identifier: 'image1', displayColorPreview: true })(
                dispatch,
                getState
            );
            expect(dispatch).toBeCalledWith({
                type: actionTypes.SET_CANVAS_DATA_MAP,
                canvasDataMap: expectedCanvasDataMap,
            });
        }
    );

    test('SET_CANVAS_DATA_MAP action sets the canvas data by the given map correctly', () => {
        const initialState = {
            configuration: {
                designdata: {
                    ['area1']: {
                        canvasData: {
                            objects: [
                                {
                                    type: 'Image',
                                    identifier: 'a canvas image identifier',
                                    imageData: {
                                        identifier: 'image1',
                                        displayColorPreview: false,
                                    },
                                },
                            ],
                        },
                    },
                    ['area2']: {
                        canvasData: {
                            objects: [
                                {
                                    type: 'Image',
                                    identifier: 'other canvas image identifier',
                                    imageData: {
                                        identifier: 'image1',
                                        displayColorPreview: false,
                                    },
                                },
                            ],
                        },
                    },
                },
            },
        };

        const canvasDataMap = {
            ['area1']: {
                objects: [
                    {
                        type: 'Image',
                        identifier: 'a canvas image identifier',
                        imageData: {
                            identifier: 'image1',
                            displayColorPreview: true,
                        },
                    },
                ],
            },
            ['area2']: {
                objects: [
                    {
                        type: 'Image',
                        identifier: 'other canvas image identifier',
                        imageData: {
                            identifier: 'image1',
                            displayColorPreview: true,
                        },
                    },
                ],
            },
        };

        const expectedState = {
            configuration: {
                designdata: {
                    ['area1']: {
                        canvasData: {
                            objects: [
                                {
                                    type: 'Image',
                                    identifier: 'a canvas image identifier',
                                    imageData: {
                                        identifier: 'image1',
                                        displayColorPreview: true,
                                    },
                                },
                            ],
                        },
                    },
                    ['area2']: {
                        canvasData: {
                            objects: [
                                {
                                    type: 'Image',
                                    identifier: 'other canvas image identifier',
                                    imageData: {
                                        identifier: 'image1',
                                        displayColorPreview: true,
                                    },
                                },
                            ],
                        },
                    },
                },
            },
        };

        const state = designDataHandler(initialState, {
            type: actionTypes.SET_CANVAS_DATA_MAP,
            canvasDataMap,
        });

        expect(state).toEqual(expectedState);
    });
});
