import './searchbar.js';

describe('Component vnSearchbar', () => {
    let controller;
    let $element;
    let $state;
    let $params;
    let $scope;
    const filter = {id: 1, search: 'needle'};

    beforeEach(ngModule('vnCore', $stateProvider => {
        $stateProvider
            .state('foo', {
                abstract: true
            })
            .state('foo.index', {
                url: '/foo/index'
            })
            .state('foo.card', {
                abstract: true
            })
            .state('foo.card.summary', {
                url: '/foo/:id/summary'
            })
            .state('foo.card.bar', {
                url: '/foo/:id/bar'
            })
            .state('foo.card.baz', {
                abstract: true
            })
            .state('foo.card.baz.index', {
                url: '/foo/:id/baz/index'
            })
            .state('foo.card.baz.edit', {
                url: '/foo/:id/baz/:bazId/edit'
            });
    }));

    beforeEach(inject(($componentController, $rootScope, _$state_) => {
        $scope = $rootScope.$new();
        $state = _$state_;
        $params = $state.params;

        $params.q = JSON.stringify(filter);

        $element = angular.element(`<div></div>`);
        controller = $componentController('vnSearchbar', {$element, $scope});
    }));

    describe('$postLink()', () => {
        it(`should fetch the filter from the state if it's in the filter state`, () => {
            jest.spyOn(controller, 'doSearch');

            controller.autoState = false;
            controller.$postLink();

            expect(controller.doSearch).toHaveBeenCalledWith(filter, 'state');
        });

        it(`should not fetch the filter from the state if not in the filter state`, () => {
            jest.spyOn(controller, 'doSearch');

            controller.autoState = false;
            controller.searchState = 'other.state';
            controller.$postLink();

            expect(controller.doSearch).toHaveBeenCalledWith(null, 'state');
        });
    });

    describe('filter() setter', () => {
        it(`should update the bar params and search`, () => {
            const withoutHours = new Date(2000, 1, 1);
            const withHours = new Date(withoutHours.getTime());
            withHours.setHours(12, 30, 15, 10);

            controller.filter = {
                search: 'needle',
                withHours: withHours.toJSON(),
                withoutHours: withoutHours.toJSON(),
                boolean: true,
                negated: false,
                myObjectProp: {myProp: 1}
            };

            const chips = {};
            for (const param of controller.params || [])
                chips[param.key] = param.chip;

            expect(controller.searchString).toBe('needle');
            expect(chips.withHours).toBe('withHours: 2000-02-01 12:30');
            expect(chips.withoutHours).toBe('withoutHours: 2000-02-01');
            expect(chips.boolean).toBe('boolean');
            expect(chips.negated).toBe('not negated');
            expect(chips.myObjectProp).toBe('myObjectProp');
        });

        it(`should clear the filter when null`, () => {
            controller.filter = null;

            expect(controller.filter).toBeNull();
            expect(controller.searchString).toBeNull();
            expect(controller.params.length).toBe(0);
        });
    });

    describe('shownFilter() getter', () => {
        it(`should return the _filter if not null`, () => {
            controller.filter = filter;
            controller.suggestedFilter = {sugestedParam: 'suggestedValue'};

            expect(controller.shownFilter).toEqual(filter);
        });

        it(`should return the suggested filter if filter is null`, () => {
            controller.filter = null;
            controller.suggestedFilter = {sugestedParam: 'suggestedValue'};

            expect(controller.shownFilter).toEqual(controller.suggestedFilter);
        });
    });

    describe('searchString() setter', () => {
        it(`should clear the whole filter when it's null`, () => {
            controller.filter = filter;
            controller.searchString = null;

            expect(controller.searchString).toBeNull();
            expect(controller.params.length).toBe(0);
        });
    });

    describe('onPanelSubmit()', () => {
        it(`should compact and define the filter`, () => {
            controller.$.popover = {hide: jasmine.createSpy('hide')};
            jest.spyOn(controller, 'doSearch');

            const filter = {
                id: 1,
                thisKeyShouldBePurged: null,
                alsoThis: [],
                andThis: {emptyProp: undefined, nullProp: null},
                dontForgetThis: [null, undefined],
                myObject: {keepThis: true, butNotThis: null},
                myArray: [null, undefined, true]
            };
            controller.onPanelSubmit(filter);

            expect(controller.doSearch).toHaveBeenCalledWith({
                id: 1,
                myObject: {keepThis: true},
                myArray: [true]
            }, 'panel');
        });
    });

    describe('onSubmit()', () => {
        it(`should define the filter`, () => {
            jest.spyOn(controller, 'doSearch');

            controller.filter = filter;
            controller.searchString = 'mySearch';
            controller.onSubmit();

            expect(controller.doSearch).toHaveBeenCalledWith({
                id: 1,
                search: 'mySearch'
            }, 'bar');
        });
    });

    describe('removeParam()', () => {
        it(`should remove the parameter from the filter`, () => {
            jest.spyOn(controller, 'doSearch');
            controller.model = {
                refresh: jest.fn(),
                applyFilter: jest.fn().mockReturnValue(Promise.resolve()),
                userParams: {
                    id: 1
                }
            };

            controller.filter = filter;
            controller.removeParam(0);

            expect(controller.doSearch).toHaveBeenCalledWith({
                search: 'needle'
            }, 'removeBar');
        });
    });

    describe('doSearch()', () => {
        it(`should do the filter`, () => {
            jest.spyOn(controller, 'onSearch');
            jest.spyOn(controller, 'onFilter');

            controller.doSearch(filter, 'any');
            $scope.$apply();

            expect(controller.onSearch).toHaveBeenCalledWith({$params: filter}, 'any');
            expect(controller.onFilter).toHaveBeenCalledWith(filter, 'any', undefined);
        });
    });

    describe('onFilter()', () => {
        it(`should go to the summary state when one result`, () => {
            jest.spyOn($state, 'go');

            const data = [{id: 1}];

            controller.baseState = 'foo';
            controller.onFilter(filter, 'any', data);
            $scope.$apply();

            expect($state.go).toHaveBeenCalledWith('foo.card.summary', {id: 1}, undefined);
            expect(controller.filter).toEqual(null);
        });

        it(`should keep the same card state when one result and it's already inside any card state`, () => {
            $state.go('foo.card.bar', {id: 1});
            $scope.$apply();

            jest.spyOn($state, 'go');
            const data = [{id: 1}];

            controller.baseState = 'foo';
            controller.onFilter(filter, 'any', data);
            $scope.$apply();

            expect($state.go).toHaveBeenCalledWith('foo.card.bar', {id: 1}, undefined);
            expect(controller.filter).toEqual(null);
        });

        it(`should keep the same card state but index when one result and it's already in card state but inside more than three-nested states`, () => {
            $state.go('foo.card.baz.edit', {id: 1, bazId: 1});
            $scope.$apply();

            jest.spyOn($state, 'go');
            const data = [{id: 1}];

            controller.baseState = 'foo';
            controller.onFilter(filter, 'any', data);
            $scope.$apply();

            expect($state.go).toHaveBeenCalledWith('foo.card.baz.index', {id: 1}, undefined);
            expect(controller.filter).toEqual(null);
        });

        it(`should go to the search state when multiple results and pass the filter as query param`, () => {
            jest.spyOn($state, 'go');

            controller.autoState = false;
            controller.searchState = 'search.state';
            controller.onFilter(filter, 'any');
            $scope.$apply();

            const queryParams = {q: JSON.stringify(filter)};

            expect($state.go).toHaveBeenCalledWith('search.state', queryParams, undefined);
            expect(controller.filter).toEqual(filter);
        });
    });
});