import ProductVariant, { itemGroupMap, createPropsFromState } from './ProductVariant';
import mountWithState from 'Utils/Test/mountWithState';
import ServiceLocator from 'App/ServiceLocator';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import { ProductVariantContainer } from './ProductVariant';

describe('Shared/Containers/ProductVariant', () => {
    // mock state data
    const item = {
        identifier: 'hoodie',
        title: 'hoodie',
        itemGroup: {
            identifier: 'color',
            title: 'Farbe',
            children: [
                {
                    identifier: 'hoodie_blue_s',
                    itemgroupentryIdentifier: 'red',
                    itemgroupentryTitle: 'Rot',
                    title: '{hoodie_red_s}',
                    thumbnail: '/images/item/thumb/hoodie_blue_s.jpg',
                    itemGroup: {
                        identifier: 'size',
                        title: 'Größe',
                        children: [
                            {
                                identifier: 'hoodie_blue_s',
                                itemgroupentryIdentifier: 's',
                                itemgroupentryTitle: 'S',
                                title: '{hoodie_blue_s}',
                            },
                        ],
                    },
                },
                {
                    identifier: 'hoodie_red_s',
                    itemgroupentryIdentifier: 'red',
                    itemgroupentryTitle: 'Rot',
                    title: '{hoodie_red_s}',
                    thumbnail: '/images/item/thumb/hoodie_red_s.jpg',
                    itemGroup: {
                        identifier: 'size',
                        title: 'Größe',
                        children: [
                            {
                                identifier: 'hoodie_red_s',
                                itemgroupentryIdentifier: 's',
                                itemgroupentryTitle: 'S',
                                title: '{hoodie_red_s}',
                            },
                        ],
                    },
                },
            ],
        },
    };
    const configurationWithItemGroupsLevelOne = {
        configuration: {
            item: {
                identifier: 'hoodie',
                title: 'hoodie',
                itemGroup: {
                    identifier: 'color',
                    title: 'Farbe',
                    children: [
                        {
                            identifier: 'hoodie_blue_s',
                            itemgroupentryIdentifier: 'blue',
                            itemgroupentryTitle: 'Blau',
                            title: '{hoodie_blue_s}',
                            thumbnail: '/images/item/thumb/hoodie_blue_s.jpg',
                        },
                        {
                            identifier: 'hoodie_red_s',
                            itemgroupentryIdentifier: 'red',
                            itemgroupentryTitle: 'Rot',
                            title: '{hoodie_red_s}',
                            thumbnail: '/images/item/thumb/hoodie_red_s.jpg',
                        },
                    ],
                },
            },
            customdata: {
                selectedAmounts: {
                    hoodie_blue_s: 2,
                    hoodie_red_s: 5,
                },
            },
        },
    };
    const configurationWithItemGroupsLevelTwo = {
        configuration: {
            item: {
                identifier: 'hoodie',
                title: 'hoodie',
                itemGroup: {
                    identifier: 'color',
                    title: 'Farbe',
                    children: [
                        {
                            identifier: 'hoodie_blue_s',
                            itemgroupentryIdentifier: 'blue',
                            itemgroupentryTitle: 'Blau',
                            title: '{hoodie_blue_s}',
                            thumbnail: '/images/item/thumb/hoodie_blue_s.jpg',
                            itemGroup: {
                                identifier: 'size',
                                title: 'Größe',
                                children: [
                                    {
                                        identifier: 'hoodie_blue_s',
                                        itemgroupentryIdentifier: 's',
                                        itemgroupentryTitle: 'S',
                                        title: '{hoodie_blue_s}',
                                        thumbnail: '/images/item/thumb/hoodie_blue_s.jpg',
                                    },
                                ],
                            },
                        },
                        {
                            identifier: 'hoodie_red_s',
                            itemgroupentryIdentifier: 'red',
                            itemgroupentryTitle: 'Rot',
                            title: '{hoodie_red_s}',
                            thumbnail: '/images/item/thumb/hoodie_red_s.jpg',
                            itemGroup: {
                                identifier: 'size',
                                title: 'Größe',
                                children: [
                                    {
                                        identifier: 'hoodie_red_s',
                                        itemgroupentryIdentifier: 's',
                                        itemgroupentryTitle: 'S',
                                        title: '{hoodie_red_s}',
                                        thumbnail: '/images/item/thumb/hoodie_red_s.jpg',
                                    },
                                ],
                            },
                        },
                    ],
                },
            },
            customdata: {
                selectedAmounts: {
                    hoodie_blue_s: 2,
                    hoodie_red_s: 5,
                },
            },
        },
    };
    const configurationWithItemGroupsLevelTwoNotSet = {
        configuration: {
            item: {
                identifier: 'hoodie',
                title: 'hoodie',
                itemGroup: {
                    identifier: 'color',
                    title: 'Farbe',
                    children: [
                        {
                            identifier: 'hoodie_blue_s',
                            itemgroupentryIdentifier: 'blue',
                            itemgroupentryTitle: 'Blau',
                            title: '{hoodie_blue_s}',
                            thumbnail: '/images/item/thumb/hoodie_blue_s.jpg',
                            itemGroup: {
                                identifier: 'size',
                                title: 'Größe',
                                children: [
                                    {
                                        identifier: 'hoodie_blue_s',
                                        itemgroupentryIdentifier: 's',
                                        itemgroupentryTitle: 'S',
                                        title: '{hoodie_blue_s}',
                                        thumbnail: '/images/item/thumb/hoodie_blue_s.jpg',
                                    },
                                ],
                            },
                        },
                        {
                            identifier: 'hoodie_red_s',
                            itemgroupentryIdentifier: 'red',
                            itemgroupentryTitle: 'Rot',
                            title: '{hoodie_red_s}',
                            thumbnail: '/images/item/thumb/hoodie_red_s.jpg',
                            itemGroup: {
                                identifier: 'size',
                                title: 'Größe',
                                children: [
                                    {
                                        identifier: 'hoodie_red_s',
                                        itemgroupentryIdentifier: 's',
                                        itemgroupentryTitle: 'S',
                                        title: '{hoodie_red_s}',
                                        thumbnail: '/images/item/thumb/hoodie_red_s.jpg',
                                    },
                                ],
                            },
                        },
                    ],
                },
            },
            customdata: {
                selectedAmounts: {
                    hoodie_red_s: 1,
                },
            },
        },
    };
    const stateWithNoGroup = {
        configuration: {
            item: {
                identifier: 'hoodie',
                title: 'hoodie',
                itemGroup: {},
                thumbnail: '/images/item/thumb/hoodie.jpg',
            },
        },
    };
    // expected results
    const amountDataLevelOne = [
        {
            title: 'Blau',
            identifier: 'hoodie_blue_s',
            amount: 2,
            isDisabled: false,
            sizes: [],
            bulkNames: [],
            url: '/images/item/thumb/hoodie_blue_s.jpg',
        },
        {
            title: 'Rot',
            identifier: 'hoodie_red_s',
            amount: 5,
            isDisabled: false,
            sizes: [],
            bulkNames: [],
            url: '/images/item/thumb/hoodie_red_s.jpg',
        },
    ];
    const amountDataLevelTwo = [
        {
            title: 'Blau',
            url: '/images/item/thumb/hoodie_blue_s.jpg',
            identifier: 'hoodie_blue_s',
            amount: 2,
            bulkNames: [],
            isDisabled: false,
            sizes: [
                {
                    size: 'S',
                    amount: 2,
                    label: 'S',
                    bulkNames: [],
                    key: 'hoodie_blue_s',
                    identifier: 'hoodie_blue_s',
                    url: '/images/item/thumb/hoodie_blue_s.jpg',
                },
            ],
        },
        {
            title: 'Rot',
            identifier: 'hoodie_red_s',
            url: '/images/item/thumb/hoodie_red_s.jpg',
            amount: 5,
            bulkNames: [],
            isDisabled: false,
            sizes: [
                {
                    size: 'S',
                    amount: 5,
                    label: 'S',
                    bulkNames: [],
                    key: 'hoodie_red_s',
                    identifier: 'hoodie_red_s',
                    url: '/images/item/thumb/hoodie_red_s.jpg',
                },
            ],
        },
    ];
    const amountDataLevelTwoNotSet = [
        {
            title: 'Blau',
            url: '/images/item/thumb/hoodie_blue_s.jpg',
            identifier: 'hoodie_blue_s',
            amount: 0,
            bulkNames: [],
            isDisabled: false,
            sizes: [
                {
                    size: 'S',
                    amount: 0,
                    label: 'S',
                    bulkNames: [],
                    key: 'hoodie_blue_s',
                    identifier: 'hoodie_blue_s',
                    url: '/images/item/thumb/hoodie_blue_s.jpg',
                },
            ],
        },
        {
            title: 'Rot',
            identifier: 'hoodie_red_s',
            url: '/images/item/thumb/hoodie_red_s.jpg',
            amount: 1,
            bulkNames: [],
            isDisabled: false,
            sizes: [
                {
                    size: 'S',
                    amount: 1,
                    label: 'S',
                    bulkNames: [],
                    key: 'hoodie_red_s',
                    identifier: 'hoodie_red_s',
                    url: '/images/item/thumb/hoodie_red_s.jpg',
                },
            ],
        },
    ];

    test('itemGroupMap throws error when the second argument is not a function', () => {
        expect(() => itemGroupMap(undefined, undefined)).toThrowError();
    });
    test('itemGroupMap does not fail on undefined input array and returns empty array', () => {
        const fn = obj => obj.identifier;
        expect(itemGroupMap(undefined, fn)).toEqual([]);
    });
    test('itemGroupMap runs the map function on all the items in the itemGroup of the given root item', () => {
        const fn = obj => obj.identifier;
        expect(itemGroupMap(item, fn)).toEqual(['hoodie_blue_s', 'hoodie_red_s']);
    });
    test('createPropsFromState creates the amountData prop from the state - single item, no group', () => {
        expect(createPropsFromState(stateWithNoGroup)).toEqual({
            amountData: [
                {
                    amount: 0,
                    bulkNames: [],
                    identifier: 'hoodie',
                    isDisabled: false,
                    sizes: [],
                    title: 'hoodie',
                    url: '/images/item/thumb/hoodie.jpg',
                },
            ],
            amountMap: {},
            error: null,
        });
    });
    test('createPropsFromState creates the amountData prop from the state - one level grouping', () => {
        expect(createPropsFromState(configurationWithItemGroupsLevelOne)).toEqual({
            amountData: amountDataLevelOne,
            amountMap: {
                hoodie_blue_s: 2,
                hoodie_red_s: 5,
            },
            error: null,
        });
    });
    test('createPropsFromState creates the amountData prop from the state - two level grouping', () => {
        expect(createPropsFromState(configurationWithItemGroupsLevelTwo)).toEqual({
            amountData: amountDataLevelTwo,
            amountMap: {
                hoodie_blue_s: 2,
                hoodie_red_s: 5,
            },
            error: null,
        });
    });
    test('createPropsFromState creates the amountData prop from the state with the correct defaults - two level grouping', () => {
        expect(createPropsFromState(configurationWithItemGroupsLevelTwoNotSet)).toEqual({
            amountData: amountDataLevelTwoNotSet,
            amountMap: {
                hoodie_red_s: 1,
            },
            error: null,
        });
    });

    // integration tests
    describe('Connected component', () => {
        const configuratorSrvc = new (class ConfiguratorService {
            static serviceName = 'ConfiguratorService';
            setSelectedAmount = jest.fn();
            getPreviewImagesForVariants = () => null;
        })();
        ServiceLocator.provide(configuratorSrvc);
        test('the connected component gets the correct props for empty initial state', () => {
            const { wrapper } = mountWithState(ProductVariant, {
                configurator: { configuration: { item: {} } },
            });
            const props = wrapper.find('ProductVariant').props();
            expect(props.amountData).toEqual([]);
        });
        test('the connected component gets the correct props from the state with one grouping level', () => {
            const { wrapper } = mountWithState(ProductVariant, {
                configurator: configurationWithItemGroupsLevelOne,
            });
            const props = wrapper.find('ProductVariant').props();
            expect(props.amountData).toEqual(amountDataLevelOne);
        });
        test('the connected component gets the correct props from the state with two grouping levels', () => {
            const { wrapper } = mountWithState(ProductVariant, {
                configurator: configurationWithItemGroupsLevelTwo,
            });
            const props = wrapper.find('ProductVariant').props();
            expect(props.amountData).toEqual(amountDataLevelTwo);
        });
        test('handleChange calls the ConfiguratorService.setSelectedAmount method with the correct arguments', () => {
            const { wrapper } = mountWithState(ProductVariant, {
                configurator: { configuration: { item: {} } },
            });
            const props = wrapper.find('ProductVariant').props();
            props.handleChange({ identifier: 'hoodie_blue_s' }, { identifier: 'hoodie_blue_m' }, '9');
            expect(configuratorSrvc.setSelectedAmount).toBeCalledWith('hoodie_blue_m', 9, true);
            props.handleChange({ identifier: 'hoodie_blue_s' }, undefined, '6');
            expect(configuratorSrvc.setSelectedAmount).toBeCalledWith('hoodie_blue_s', 6, true);
        });
    });

    describe('ProductVariantContainer provides preview images to the component', () => {
        it('calls getPreviewImages if renderPreviewImagesForCheckout is true', async () => {
            const getPreviewImages = jest.fn(() =>
                Promise.resolve({
                    variant1: 'imageDataURLForVariant1',
                    variant2: 'imageDataURLForVariant2',
                })
            );
            // eslint-disable-next-line react/prop-types
            const ProductVariant = ({ variantListLoaderElement }) => <div>{variantListLoaderElement}</div>;
            const Progress = jest.fn(() => <div />);
            let wrapper;
            await act(async () => {
                wrapper = mount(
                    <ProductVariantContainer
                        renderPreviewImagesForCheckout={true}
                        getPreviewImages={getPreviewImages}
                        ProductVariantComponent={ProductVariant}
                        ProgressComponent={Progress}
                    />
                );
            });
            expect(getPreviewImages).toHaveBeenCalledTimes(1); // loads the preview images
            // sets the loading state of the Progress component correctly
            expect(Progress).toHaveBeenNthCalledWith(1, { loading: false }, {});
            expect(Progress).toHaveBeenCalledWith({ loading: true }, {});
            expect(Progress).toHaveBeenLastCalledWith({ loading: false }, {});
            // provides the previewImages to the ProductVariant component
            wrapper.update();
            expect(wrapper.find(ProductVariant).props().previewImages).toEqual({
                variant1: 'imageDataURLForVariant1',
                variant2: 'imageDataURLForVariant2',
            });
        });

        it('does not call getPreviewImages if renderPreviewImagesForCheckout is false', async () => {
            const getPreviewImages = jest.fn(() => null);
            const ProductVariant = () => <div />;
            const Progress = () => <div />;
            await act(async () => {
                mount(
                    <ProductVariantContainer
                        renderPreviewImagesForCheckout={false}
                        getPreviewImages={getPreviewImages}
                        ProductVariantComponent={ProductVariant}
                        ProgressComponent={Progress}
                    />
                );
            });
            expect(getPreviewImages).not.toHaveBeenCalled();
        });
    });

    describe('ProductVariantContainer moves the items with amount to the top', () => {
        it('works with one item amount', () => {
            const ProductVariant = () => <div />;
            const Progress = () => <div />;
            const amountDataLevelOne = [
                {
                    title: 'Blau',
                    identifier: 'hoodie_blue_s',
                    amount: 0,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_blue_s.jpg',
                },
                {
                    title: 'Rot',
                    identifier: 'hoodie_red_s',
                    amount: 5,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_red_s.jpg',
                },
            ];
            const wrapper = mount(
                <ProductVariantContainer
                    renderPreviewImagesForCheckout={false}
                    ProductVariantComponent={ProductVariant}
                    ProgressComponent={Progress}
                    amountData={amountDataLevelOne}
                    getPreviewImages={() => null}
                />
            );
            expect(wrapper.find(ProductVariant).props().amountData).toEqual([
                {
                    title: 'Rot',
                    identifier: 'hoodie_red_s',
                    amount: 5,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_red_s.jpg',
                },
                {
                    title: 'Blau',
                    identifier: 'hoodie_blue_s',
                    amount: 0,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_blue_s.jpg',
                },
            ]);
        });

        it('works with more item amounts', () => {
            const ProductVariant = () => <div />;
            const Progress = () => <div />;
            const amountDataLevelOne = [
                {
                    title: 'Blau',
                    identifier: 'hoodie_blue_s',
                    amount: 0,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_blue_s.jpg',
                },
                {
                    title: 'Rot',
                    identifier: 'hoodie_red_s',
                    amount: 5,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_red_s.jpg',
                },
                {
                    title: 'Yellow',
                    identifier: 'hoodie_yellow_s',
                    amount: 0,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_yellow_s.jpg',
                },
                {
                    title: 'White',
                    identifier: 'hoodie_white_s',
                    amount: 2,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_white_s.jpg',
                },
            ];
            const wrapper = mount(
                <ProductVariantContainer
                    renderPreviewImagesForCheckout={false}
                    ProductVariantComponent={ProductVariant}
                    ProgressComponent={Progress}
                    amountData={amountDataLevelOne}
                    getPreviewImages={() => null}
                />
            );
            expect(wrapper.find(ProductVariant).props().amountData).toEqual([
                {
                    title: 'Rot',
                    identifier: 'hoodie_red_s',
                    amount: 5,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_red_s.jpg',
                },
                {
                    title: 'White',
                    identifier: 'hoodie_white_s',
                    amount: 2,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_white_s.jpg',
                },
                {
                    title: 'Blau',
                    identifier: 'hoodie_blue_s',
                    amount: 0,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_blue_s.jpg',
                },
                {
                    title: 'Yellow',
                    identifier: 'hoodie_yellow_s',
                    amount: 0,
                    isDisabled: false,
                    sizes: [],
                    bulkNames: [],
                    url: '/images/item/thumb/hoodie_yellow_s.jpg',
                },
            ]);
        });

        it('works with two levels', () => {
            const ProductVariant = () => <div />;
            const Progress = () => <div />;
            const amountDataLevelTwo = [
                {
                    title: 'Blau',
                    url: '/images/item/thumb/hoodie_blue_s.jpg',
                    identifier: 'hoodie_blue_s',
                    amount: 0,
                    bulkNames: [],
                    isDisabled: false,
                    sizes: [
                        {
                            size: 'S',
                            amount: 0,
                            label: 'S',
                            bulkNames: [],
                            key: 'hoodie_blue_s',
                            identifier: 'hoodie_blue_s',
                            url: '/images/item/thumb/hoodie_blue_s.jpg',
                        },
                    ],
                },
                {
                    title: 'Rot',
                    identifier: 'hoodie_red_s',
                    url: '/images/item/thumb/hoodie_red_s.jpg',
                    amount: 0,
                    bulkNames: [],
                    isDisabled: false,
                    sizes: [
                        {
                            size: 'S',
                            amount: 5,
                            label: 'S',
                            bulkNames: [],
                            key: 'hoodie_red_s',
                            identifier: 'hoodie_red_s',
                            url: '/images/item/thumb/hoodie_red_s.jpg',
                        },
                    ],
                },
            ];
            const wrapper = mount(
                <ProductVariantContainer
                    renderPreviewImagesForCheckout={false}
                    ProductVariantComponent={ProductVariant}
                    ProgressComponent={Progress}
                    amountData={amountDataLevelTwo}
                    getPreviewImages={() => null}
                />
            );
            expect(wrapper.find(ProductVariant).props().amountData).toEqual([
                {
                    title: 'Rot',
                    identifier: 'hoodie_red_s',
                    url: '/images/item/thumb/hoodie_red_s.jpg',
                    amount: 0,
                    bulkNames: [],
                    isDisabled: false,
                    sizes: [
                        {
                            size: 'S',
                            amount: 5,
                            label: 'S',
                            bulkNames: [],
                            key: 'hoodie_red_s',
                            identifier: 'hoodie_red_s',
                            url: '/images/item/thumb/hoodie_red_s.jpg',
                        },
                    ],
                },
                {
                    title: 'Blau',
                    url: '/images/item/thumb/hoodie_blue_s.jpg',
                    identifier: 'hoodie_blue_s',
                    amount: 0,
                    bulkNames: [],
                    isDisabled: false,
                    sizes: [
                        {
                            size: 'S',
                            amount: 0,
                            label: 'S',
                            bulkNames: [],
                            key: 'hoodie_blue_s',
                            identifier: 'hoodie_blue_s',
                            url: '/images/item/thumb/hoodie_blue_s.jpg',
                        },
                    ],
                },
            ]);
        });
    });
});
