import './index.js';
import crudModel from 'core/mocks/crud-model';

describe('Order', () => {
    describe('Component vnOrderCatalog', () => {
        let $scope;
        let $state;
        let controller;
        let $httpBackend;

        beforeEach(ngModule('order'));

        beforeEach(inject(($componentController, _$state_, _$httpBackend_, $rootScope) => {
            $httpBackend = _$httpBackend_;
            $scope = $rootScope.$new();
            $scope.model = crudModel;
            $scope.search = {};
            $scope.itemId = {};
            $state = _$state_;
            $state.current.name = 'my.current.state';
            const $element = angular.element('<vn-order-catalog></vn-order-catalog>');
            controller = $componentController('vnOrderCatalog', {$element, $scope});
            controller._order = {id: 4};
            controller.$params = {
                categoryId: 1,
                typeId: 2,
                id: 4
            };
        }));

        describe('getData()', () => {
            it(`should make a query an fetch the order data`, () => {
                controller._order = null;

                $httpBackend.expect('GET', `Orders/4`).respond(200, {id: 4, isConfirmed: true});
                $httpBackend.expect('GET', `Orders/4/getItemTypeAvailable?itemCategoryId=1`).respond();
                controller.getData();
                $httpBackend.flush();

                const order = controller.order;

                expect(order.id).toEqual(4);
                expect(order.isConfirmed).toBeTruthy();
            });
        });

        describe('order() setter', () => {
            it(`should call scope $applyAsync() method and apply filters from state params`, () => {
                $httpBackend.expect('GET', `Orders/4/getItemTypeAvailable?itemCategoryId=1`).respond();
                controller.order = {id: 4};

                $scope.$apply();

                expect(controller.categoryId).toEqual(1);
                expect(controller.typeId).toEqual(2);
            });
        });

        describe('items() setter', () => {
            it(`should return an object with order params`, () => {
                jest.spyOn(controller, 'fetchResultTags');
                jest.spyOn(controller, 'buildOrderFilter');

                const expectedResult = [{field: 'showOrder, price', name: 'Color and price', priority: 999}];
                const items = [{id: 1, name: 'My Item', tags: [
                    {tagFk: 4, name: 'Length'},
                    {tagFk: 5, name: 'Color'}
                ]}];
                controller.items = items;

                expect(controller.orderFields.length).toEqual(6);
                expect(controller.orderFields).toEqual(jasmine.arrayContaining(expectedResult));
                expect(controller.fetchResultTags).toHaveBeenCalledWith(items);
                expect(controller.buildOrderFilter).toHaveBeenCalledWith();
            });
        });

        describe('categoryId() setter', () => {
            it(`should set category property to null, call updateStateParams() method and not call applyFilters()`, () => {
                jest.spyOn(controller, 'updateStateParams');

                controller.categoryId = null;

                expect(controller.updateStateParams).toHaveBeenCalledWith();
            });

            it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
                jest.spyOn(controller, 'updateStateParams');

                controller.categoryId = 2;

                expect(controller.updateStateParams).toHaveBeenCalledWith();
            });
        });

        describe('changeCategory()', () => {
            it(`should set categoryId property to null if the new value equals to the old one`, () => {
                controller.categoryId = 2;
                controller.changeCategory(2);

                expect(controller.categoryId).toBeNull();
            });

            it(`should set categoryId property`, () => {
                controller.categoryId = 2;
                controller.changeCategory(1);

                expect(controller.categoryId).toEqual(1);
            });
        });

        describe('typeId() setter', () => {
            it(`should set type property to null, call updateStateParams() method and not call applyFilters()`, () => {
                jest.spyOn(controller, 'updateStateParams');
                jest.spyOn(controller, 'applyFilters');

                controller.typeId = null;

                expect(controller.updateStateParams).toHaveBeenCalledWith();
                expect(controller.applyFilters).not.toHaveBeenCalledWith();
            });

            it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
                jest.spyOn(controller, 'updateStateParams');
                jest.spyOn(controller, 'applyFilters');

                controller.typeId = 2;

                expect(controller.updateStateParams).toHaveBeenCalledWith();
                expect(controller.applyFilters).toHaveBeenCalledWith();
            });
        });

        describe('tagGroups() setter', () => {
            it(`should set tagGroups property and then call updateStateParams() and applyFilters() methods`, () => {
                jest.spyOn(controller, 'updateStateParams');
                jest.spyOn(controller, 'applyFilters');

                controller.tagGroups = [{tagFk: 11, values: [{value: 'Brown'}]}];

                expect(controller.updateStateParams).toHaveBeenCalledWith();
                expect(controller.applyFilters).toHaveBeenCalledWith();
            });
        });

        describe('onSearchByTag()', () => {
            it(`should not add a new tag if the event key code doesn't equals to 'Enter'`, () => {
                jest.spyOn(controller, 'applyFilters');

                controller.order = {id: 4};
                controller.$.search.value = 'Brown';
                controller.onSearchByTag({key: 'Tab'});

                expect(controller.applyFilters).not.toHaveBeenCalledWith();
            });

            it(`should add a new tag if the event key code equals to 'Enter' an then call applyFilters()`, () => {
                jest.spyOn(controller, 'applyFilters');

                controller.order = {id: 4};
                controller.$.search.value = 'Brown';
                controller.onSearchByTag({key: 'Enter'});

                expect(controller.applyFilters).toHaveBeenCalledWith();
            });
        });

        describe('onSearch()', () => {
            it(`should apply a filter by item id an then call the applyFilters method`, () => {
                jest.spyOn(controller, 'applyFilters');

                const itemId = 1;
                controller.onSearch({search: itemId});

                expect(controller.applyFilters).toHaveBeenCalledWith({
                    'i.id': itemId
                });
            });

            it(`should apply a filter by item name an then call the applyFilters method`, () => {
                jest.spyOn(controller, 'applyFilters');

                const itemName = 'Bow';
                controller.onSearch({search: itemName});

                expect(controller.applyFilters).toHaveBeenCalledWith({
                    'i.name': {like: `%${itemName}%`}
                });
            });
        });

        describe('applyFilters()', () => {
            it(`should call model applyFilter() method with a new filter`, () => {
                jest.spyOn(controller.$.model, 'applyFilter');

                controller._categoryId = 2;
                controller._typeId = 4;

                controller.applyFilters();

                expect(controller.$.model.applyFilter).toHaveBeenCalledWith(
                    {where: {categoryFk: 2, typeFk: 4}},
                    {orderFk: 4, orderBy: controller.getOrderBy(), tagGroups: []});
            });
        });

        describe('remove()', () => {
            it(`should remove a tag from tags property`, () => {
                jest.spyOn(controller, 'applyFilters');

                controller.tagGroups = [
                    {tagFk: 1, values: [{value: 'Brown'}]},
                    {tagFk: 67, values: [{value: 'Concussion'}]}
                ];
                controller.remove(0);

                const firstTag = controller.tagGroups[0];

                expect(controller.tagGroups.length).toEqual(1);
                expect(firstTag.tagFk).toEqual(67);
                expect(controller.applyFilters).toHaveBeenCalledWith();
            });

            it(`should remove a tag from tags property and call applyFilters() if there's no more tags`, () => {
                jest.spyOn(controller, 'applyFilters');

                controller._categoryId = 1;
                controller._typeId = 1;
                controller.tagGroups = [{tagFk: 1, values: [{value: 'Blue'}]}];
                controller.remove(0);

                expect(controller.tagGroups.length).toEqual(0);
                expect(controller.applyFilters).toHaveBeenCalledWith();
            });
        });

        describe('updateStateParams()', () => {
            it(`should call state go() method passing category and type state params`, () => {
                jest.spyOn(controller.$state, 'go');

                controller._categoryId = 2;
                controller._typeId = 4;
                controller._tagGroups = [
                    {tagFk: 67, values: [{value: 'Concussion'}], tagSelection: {name: 'Category'}}
                ];
                const tagGroups = JSON.stringify([
                    {values: [{value: 'Concussion'}], tagFk: 67, tagSelection: {name: 'Category'}}
                ]);
                const expectedResult = {categoryId: 2, typeId: 4, tagGroups: tagGroups};
                controller.updateStateParams();

                expect(controller.$state.go).toHaveBeenCalledWith('my.current.state', expectedResult);
            });
        });

        describe('getOrderBy()', () => {
            it(`should return an object with order params`, () => {
                controller.orderField = 'relevancy DESC, name';
                controller.orderWay = 'DESC';
                let expectedResult = {
                    field: 'relevancy DESC, name',
                    way: 'DESC',
                    isTag: false
                };
                let result = controller.getOrderBy();

                expect(result).toEqual(expectedResult);
            });
        });

        describe('applyOrder()', () => {
            it(`should apply order param to model calling getOrderBy()`, () => {
                jest.spyOn(controller, 'getOrderBy');
                jest.spyOn(controller.$.model, 'addFilter');

                controller.field = 'relevancy DESC, name';
                controller.way = 'ASC';
                controller._categoryId = 1;
                controller._typeId = 1;
                let expectedOrder = {orderBy: controller.getOrderBy()};

                controller.applyOrder();

                expect(controller.getOrderBy).toHaveBeenCalledWith();
                expect(controller.$.model.addFilter).toHaveBeenCalledWith(null, expectedOrder);
            });
        });

        describe('fetchResultTags()', () => {
            it(`should create an array of non repeated tags then set the resultTags property`, () => {
                const items = [
                    {
                        id: 1, name: 'My Item 1', tags: [
                            {tagFk: 4, name: 'Length', value: 1},
                            {tagFk: 5, name: 'Color', value: 'red'}
                        ]
                    },
                    {
                        id: 2, name: 'My Item 2', tags: [
                            {tagFk: 4, name: 'Length', value: 1},
                            {tagFk: 5, name: 'Color', value: 'blue'}
                        ]
                    }];
                controller.fetchResultTags(items);

                expect(controller.resultTags.length).toEqual(2);
            });
        });

        describe('buildOrderFilter()', () => {
            it(`should create an array of non repeated tags plus default filters and then set the orderFields property`, () => {
                const items = [
                    {
                        id: 1, name: 'My Item 1', tags: [
                            {tagFk: 4, name: 'Length'},
                            {tagFk: 5, name: 'Color'}
                        ]
                    },
                    {
                        id: 2, name: 'My Item 2', tags: [
                            {tagFk: 5, name: 'Color'},
                            {tagFk: 6, name: 'Relevancy'}
                        ]
                    }];

                controller.fetchResultTags(items);
                controller.buildOrderFilter();

                expect(controller.orderFields.length).toEqual(7);
            });
        });

        describe('formatTooltip()', () => {
            it(`should return a formatted text with the tag name and values`, () => {
                const tagGroup = {
                    values: [{value: 'Silver'}, {value: 'Brown'}],
                    tagFk: 1,
                    tagSelection: {
                        name: 'Color'
                    }
                };

                const result = controller.formatTooltip(tagGroup);

                expect(result).toEqual(`Color: "Silver", "Brown"`);
            });

            it(`should return a formatted text with the tag value`, () => {
                const tagGroup = {
                    values: [{value: 'Silver'}]
                };

                const result = controller.formatTooltip(tagGroup);

                expect(result).toEqual(`"Silver"`);
            });
        });

        describe('sanitizedTagGroupParam()', () => {
            it(`should return an array of tags`, () => {
                const dirtyTagGroups = [{
                    values: [{value: 'Silver'}, {value: 'Brown'}],
                    tagFk: 1,
                    tagSelection: {
                        name: 'Color',
                        $orgRow: {name: 'Color'}
                    },
                    $orgIndex: 1
                }];
                controller.tagGroups = dirtyTagGroups;

                const expectedResult = [{
                    values: [{value: 'Silver'}, {value: 'Brown'}],
                    tagFk: 1,
                    tagSelection: {
                        name: 'Color'
                    }
                }];
                const result = controller.sanitizedTagGroupParam();

                expect(result).toEqual(expect.objectContaining(expectedResult));
            });
        });
    });
});