import ServiceLocator from '../../ServiceLocator';
import ConfiguratorService from '../ConfiguratorService';
import {
    SET_SELECTED_AMOUNT,
    SET_SELECTED_CALCULATION,
    CLEAR_BULKNAMES,
    SAVE_BULKNAMES,
} from '../../Reducers/Configurator/Actions';
import { setConf } from '../../configuration';

describe('Services/ConfiguratorService', () => {
    // mock services
    class StoreService {
        static serviceName = 'StoreService';

        observeStore() {}

        get state() {
            return {
                configurator: {
                    configuration: {
                        item: {
                            identifier: 'test_identifier',
                        },
                    },
                },
            };
        }

        dispatch = jest.fn();
    }

    // add the mock services
    const storeSrvc = new StoreService();
    ServiceLocator.provide(storeSrvc);

    // note: we should only test that the dispatch method of the StoreService is called with the correct action
    test('setSelectedAmount dispatches the correct action', () => {
        const configuratorSrvc = new ConfiguratorService();
        configuratorSrvc.setSelectedAmount('ABC123', 4);
        expect(storeSrvc.dispatch).toBeCalledWith({
            type: SET_SELECTED_AMOUNT,
            identifier: 'ABC123',
            isUpdatedByUser: false,
            amount: 4,
        });
    });
    test('setSelectedAmount uses the current item identifier when not specified in the args', () => {
        const configuratorSrvc = new ConfiguratorService();
        configuratorSrvc.setSelectedAmount(false, 3);
        expect(storeSrvc.dispatch).toBeCalledWith({
            type: SET_SELECTED_AMOUNT,
            identifier: 'test_identifier',
            isUpdatedByUser: false,
            amount: 3,
        });
    });
    test('setSelectedAmount dispatches the correct action when isUpdatedByUser is specified in the args', () => {
        const configuratorSrvc = new ConfiguratorService();
        configuratorSrvc.setSelectedAmount('ABC123', 3, true);
        expect(storeSrvc.dispatch).toBeCalledWith({
            type: SET_SELECTED_AMOUNT,
            identifier: 'ABC123',
            amount: 3,
            isUpdatedByUser: true,
        });
    });
    test('setSelectedCalculation dispatches the correct action', () => {
        const configuratorSrvc = new ConfiguratorService();
        configuratorSrvc.setSelectedCalculation('calculation_id', true);
        expect(storeSrvc.dispatch).toBeCalledWith({
            type: SET_SELECTED_CALCULATION,
            identifier: 'calculation_id',
            value: true,
        });
    });

    test('getRoughConfiguration returns the data that has influence on the price calculation', () => {
        const confData = {
            item: {
                identifier: 'test_item',
                price: 99,
                irrelevant_data_in_the_item: 'should not taken',
            },
            optionclassifications: [
                {
                    identifier: 'test_option',
                    selectedoptions: [
                        {
                            identifier: 'option_1',
                            amount: 1,
                            price: 17,
                            irrelevant_test_data: 'bar',
                        },
                    ],
                },
            ],
            designdata: {
                test_canvas: {
                    canvasData: {
                        objects: [
                            { type: 'Image', uuid: 'image_1' },
                            { type: 'Text', uuid: 'text_1' },
                        ],
                    },
                    colorAmount: 1,
                    designProductionMethodIdentifier: 'print',
                },
            },
            customdata: {
                test_property: 'test_value',
            },
            other: 'irrelevant data',
        };
        const configuratorSrvc = new ConfiguratorService();

        // it contains the simplified data of the relevant properties,
        // and does not contain the irrelevant data
        expect(configuratorSrvc.getRoughConfiguration(confData)).toEqual({
            item: {
                identifier: 'test_item',
                price: 99,
            },
            optionclassifications: [
                {
                    identifier: 'test_option',
                    selectedoptions: [
                        {
                            identifier: 'option_1',
                            amount: 1,
                            price: 17,
                        },
                    ],
                },
            ],
            designdata: {
                test_canvas: {
                    objectCount: 2,
                    colorAmount: 1,
                    designProductionMethodIdentifier: 'print',
                    objects: [{ uuid: 'image_1' }, { uuid: 'text_1' }],
                },
            },
            customdata: {
                test_property: 'test_value',
            },
        });
    });

    test('getRoughConfiguration returns correct data if the configuration is empty', () => {
        const confData = {};
        const configuratorSrvc = new ConfiguratorService();

        expect(configuratorSrvc.getRoughConfiguration(confData)).toEqual({
            item: undefined,
            optionclassifications: undefined,
            designdata: undefined,
            customdata: undefined,
        });
    });

    test('addToCartCallback calls the corrent parent functions', () => {
        const configuratorSrvc = new ConfiguratorService();

        const mockedParentWindow = {
            postMessage: jest.fn(),
            addToCart: jest.fn(),
        };

        delete global.parent;
        global.parent = mockedParentWindow;

        const dummyCode = 'dummyCode';
        const dummyOrigin = 'dummyOrigin';

        setConf('addToCartMethod', 'postMessage');
        setConf('addToCartPostMessageTargetOrigin', dummyOrigin);

        configuratorSrvc.addToCartCallback({ code: dummyCode });

        expect(mockedParentWindow.postMessage).toBeCalledWith(
            { code: dummyCode, method: 'addtocart' },
            dummyOrigin
        );

        setConf('addToCartMethod', 'functionCall');

        configuratorSrvc.addToCartCallback({ code: dummyCode });

        expect(mockedParentWindow.addToCart).toBeCalledWith(dummyCode);
    });
    test('saveBulkNames dispatches the correct action', () => {
        const identifier = 'hoodie_blue_s';
        const bulkNamesData = ['Alex', 'Leon', 'Lea'];
        const configuratorSrvc = new ConfiguratorService();
        configuratorSrvc.saveBulkNames(identifier, bulkNamesData);
        expect(storeSrvc.dispatch).toBeCalledWith({
            type: SAVE_BULKNAMES,
            identifier,
            bulkNamesData,
        });
    });

    test('clearBulkNames dispatches the correct action', () => {
        const configuratorSrvc = new ConfiguratorService();
        const storeSrvc = new StoreService();
        ServiceLocator.provide(storeSrvc);
        configuratorSrvc.clearBulkNames();
        expect(storeSrvc.dispatch).toBeCalledWith({
            type: CLEAR_BULKNAMES,
        });
    });

    describe('getPreviewImagesForVariants', () => {
        it('calls VisualizationService.getScreenshots with the correct params for multiple variants', async () => {
            class StoreService {
                static serviceName = 'StoreService';

                observeStore() {}

                get state() {
                    return {
                        configurator: {
                            configuration: {
                                designdata: {
                                    area1: {
                                        canvasData: {
                                            objects: [{ type: 'Text', content: 'hello' }],
                                        },
                                    },
                                    area2: {
                                        canvasData: {
                                            objects: [{ type: 'Text', content: 'hello from the other side' }],
                                        },
                                    },
                                },
                                item: {
                                    identifier: 'rootItem',
                                    itemGroup: {
                                        children: [
                                            {
                                                identifier: 'variant1',
                                            },
                                            {
                                                identifier: 'variant2',
                                            },
                                        ],
                                    },
                                },
                            },
                        },
                        designArea: {
                            filteredDesignAreas: [
                                {
                                    identifier: 'area1',
                                    designProductionMethods: [],
                                    designView: {
                                        identifier: 'view1',
                                    },
                                },
                                {
                                    identifier: 'area2',
                                    designProductionMethods: [
                                        {
                                            identifier: 'dpm1',
                                            isDefault: true,
                                        },
                                    ],
                                    designView: {
                                        identifier: 'view1',
                                    },
                                    customData: {
                                        selectedCameraView: 'view1',
                                    },
                                },
                            ],
                        },
                        designView: {
                            designViews: [
                                {
                                    identifier: 'view1',
                                    designAreas: [
                                        {
                                            identifier: 'area1',
                                        },
                                        {
                                            identifier: 'area2',
                                        },
                                    ],
                                },
                            ],
                        },
                    };
                }

                dispatch = jest.fn();
            }
            const storeSrvc = new StoreService();
            ServiceLocator.provide(storeSrvc);

            const visualizationSrvc = new (class VisualizationService {
                static serviceName = 'VisualizationService';
                getScreenshots = jest.fn(() => ({
                    variant1: {
                        area2: 'theImageDataURLForVariant1',
                    },
                    variant2: {
                        area2: 'theImageDataURLForVariant2',
                    },
                }));
            })();
            ServiceLocator.provide(visualizationSrvc);

            const designDataSrvc = new (class DesignDataService {
                static serviceName = 'DesignDataService';
                createHighlightMemo = () => ({ restore() {} });
                closeDesignAreaEditor = () => null;
                clearDesignAreaSelection = () => null;
                updateVisualization = () => null;
            })();
            ServiceLocator.provide(designDataSrvc);

            const configuratorSrv = new ConfiguratorService();
            const screenshots = await configuratorSrv.getPreviewImagesForVariants();
            expect(visualizationSrvc.getScreenshots).toHaveBeenCalledWith({
                areaList: ['area2'],
                viewList: ['view1'],
                variantList: ['variant1', 'variant2'],
                size: { width: 100, height: 100 },
            });
            expect(screenshots).toEqual({
                variant1: 'theImageDataURLForVariant1',
                variant2: 'theImageDataURLForVariant2',
            });
        });

        it('calls VisualizationService.getScreenshots with the correct params for single variant', async () => {
            class StoreService {
                static serviceName = 'StoreService';

                observeStore() {}

                get state() {
                    return {
                        configurator: {
                            configuration: {
                                designdata: {
                                    area1: {
                                        canvasData: {
                                            objects: [{ type: 'Text', content: 'hello' }],
                                        },
                                    },
                                },
                                item: {
                                    identifier: 'rootItem',
                                },
                            },
                        },
                        designArea: {
                            filteredDesignAreas: [
                                {
                                    identifier: 'area1',
                                    designProductionMethods: [],
                                    designView: {
                                        identifier: 'view1',
                                    },
                                },
                            ],
                        },
                        designView: {
                            designViews: [
                                {
                                    identifier: 'view1',
                                    designAreas: [
                                        {
                                            identifier: 'area1',
                                        },
                                    ],
                                },
                            ],
                        },
                    };
                }

                dispatch = jest.fn();
            }
            const storeSrvc = new StoreService();
            ServiceLocator.provide(storeSrvc);

            const visualizationSrvc = new (class VisualizationService {
                static serviceName = 'VisualizationService';
                getScreenshots = jest.fn(() => ({
                    rootItem: {
                        area1: 'theImageDataURLForRootVariant',
                    },
                }));
            })();
            ServiceLocator.provide(visualizationSrvc);

            const designDataSrvc = new (class DesignDataService {
                static serviceName = 'DesignDataService';
                createHighlightMemo = () => ({ restore() {} });
                closeDesignAreaEditor = () => null;
                clearDesignAreaSelection = () => null;
                updateVisualization = () => null;
            })();
            ServiceLocator.provide(designDataSrvc);

            const configuratorSrv = new ConfiguratorService();
            const screenshots = await configuratorSrv.getPreviewImagesForVariants();
            expect(visualizationSrvc.getScreenshots).toHaveBeenCalledWith({
                areaList: ['area1'],
                viewList: ['default'],
                variantList: ['rootItem'],
                size: { width: 100, height: 100 },
            });
            expect(screenshots).toEqual({
                rootItem: 'theImageDataURLForRootVariant',
            });
        });

        it(
            'calls VisualizationService.getScreenshots with the correct params for single variant and no design areas, ' +
                'also with the provided size option',
            async () => {
                class StoreService {
                    static serviceName = 'StoreService';

                    observeStore() {}

                    get state() {
                        return {
                            configurator: {
                                configuration: {
                                    designdata: null,
                                    item: {
                                        identifier: 'rootItem',
                                    },
                                },
                            },
                            designArea: {
                                filteredDesignAreas: [],
                            },
                            designView: {
                                designViews: [
                                    {
                                        identifier: 'view1',
                                    },
                                ],
                            },
                        };
                    }

                    dispatch = jest.fn();
                }
                const storeSrvc = new StoreService();
                ServiceLocator.provide(storeSrvc);

                const visualizationSrvc = new (class VisualizationService {
                    static serviceName = 'VisualizationService';
                    getScreenshots = jest.fn(() => ({
                        rootItem: {
                            view1: 'theImageDataURLForRootVariant',
                        },
                    }));
                })();
                ServiceLocator.provide(visualizationSrvc);

                const designDataSrvc = new (class DesignDataService {
                    static serviceName = 'DesignDataService';
                    createHighlightMemo = () => ({ restore() {} });
                    closeDesignAreaEditor = () => null;
                    clearDesignAreaSelection = () => null;
                    updateVisualization = () => null;
                })();
                ServiceLocator.provide(designDataSrvc);

                const configuratorSrv = new ConfiguratorService();
                const screenshots = await configuratorSrv.getPreviewImagesForVariants({
                    size: { width: 200, height: 200 },
                });
                expect(visualizationSrvc.getScreenshots).toHaveBeenCalledWith({
                    areaList: [],
                    viewList: ['default'],
                    variantList: ['rootItem'],
                    size: { width: 200, height: 200 },
                });
                expect(screenshots).toEqual({
                    rootItem: 'theImageDataURLForRootVariant',
                });
            }
        );
    });
});
