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

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

        beforeEach(ngModule('ticket'));

        beforeEach(inject(($componentController, $rootScope, _$state_, _$httpBackend_) => {
            const ticket = {
                id: 1,
                clientFk: 1101,
                shipped: Date.vnNew(),
                landed: Date.vnNew(),
                created: Date.vnNew(),
                client: {salesPersonFk: 1},
                address: {mobile: 111111111}
            };
            const sales = [
                {
                    id: 1,
                    concept: 'Item 1',
                    quantity: 5,
                    price: 23.5,
                    discount: 0,
                }, {
                    id: 4,
                    concept: 'Item 2',
                    quantity: 20,
                    price: 5.5,
                    discount: 0,
                }
            ];

            $state = _$state_;
            $scope = $rootScope.$new();
            $scope.watcher = watcher;
            $scope.sms = {open: () => {}};
            $scope.ticket = ticket;
            $scope.model = crudModel;
            $scope.editDiscount = {relocate: () => {}, hide: () => {}};
            $scope.editPricePopover = {relocate: () => {}};
            $scope.claimConfirm = {show: () => {}};
            $httpBackend = _$httpBackend_;
            Object.defineProperties($state.params, {
                id: {
                    value: ticket.id,
                    writable: true
                },
                go: {
                    value: () => {},
                    writable: false
                }
            });
            const $element = angular.element('<vn-ticket-sale></vn-ticket-sale>');
            controller = $componentController('vnTicketSale', {$element, $scope});
            controller.card = {reload: () => {}};
            controller._ticket = ticket;
            controller._sales = sales;
            controller.ticketConfig = [
                {daysForWarningClaim: 1}
            ];
            $httpBackend.expect('GET', 'TicketConfigs').respond(200);
        }));

        describe('ticket() setter', () => {
            it('should set the ticket data an then call the isTicketEditable() and isTicketLocked() methods', () => {
                jest.spyOn(controller, 'isTicketEditable').mockReturnThis();
                jest.spyOn(controller, 'isTicketLocked').mockReturnThis();

                controller.ticket = {id: 1};

                expect(controller.isTicketEditable).toHaveBeenCalledWith();
                expect(controller.isTicketLocked).toHaveBeenCalledWith();
            });
        });

        describe('isClaimable() getter', () => {
            it('should return true for a ticket delivered less than seven days ago', () => {
                const result = controller.isClaimable;

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

            it('should return false for a ticket delivered more than seven days ago', () => {
                const ticket = controller.ticket;
                const landedMinusEightDays = new Date(ticket.landed);
                landedMinusEightDays.setDate(landedMinusEightDays.getDate() - 8);
                ticket.landed = landedMinusEightDays;

                const result = controller.isClaimable;

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

        describe('getSaleTotal()', () => {
            it('should return the sale total amount', () => {
                const sale = {
                    quantity: 10,
                    price: 2,
                    discount: 10
                };

                const expectedAmount = 18;
                const result = controller.getSaleTotal(sale);

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

        describe('getMana()', () => {
            it('should make an HTTP GET query and return the worker mana', () => {
                controller.edit = {};
                const expectedAmount = 250;
                $httpBackend.expect('GET', 'Tickets/1/getSalesPersonMana').respond(200, expectedAmount);
                $httpBackend.expect('GET', 'Sales/usesMana').respond(200);
                controller.getMana();
                $httpBackend.flush();

                expect(controller.edit.mana).toEqual(expectedAmount);
            });
        });

        describe('selectedSales()', () => {
            it('should return a list of selected sales', () => {
                controller.sales[1].checked = true;

                const expectedSaleId = 4;
                const result = controller.selectedSales();
                const firstSelectedSale = result[0];

                expect(result.length).toEqual(1);
                expect(firstSelectedSale.id).toEqual(expectedSaleId);
            });
        });

        describe('selectedValidSales()', () => {
            it('should return a list of selected sales having a sale id', () => {
                const newEmptySale = {quantity: 10, checked: true};
                controller.sales.push(newEmptySale);
                controller.sales[0].checked = true;

                const expectedSaleId = 1;
                const result = controller.selectedValidSales();
                const firstSelectedSale = result[0];

                expect(result.length).toEqual(1);
                expect(firstSelectedSale.id).toEqual(expectedSaleId);
            });
        });

        describe('selectedSalesCount()', () => {
            it('should return the number of selected sales', () => {
                controller.sales[0].checked = true;

                const result = controller.selectedSalesCount();

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

        describe('hasSelectedSales()', () => {
            it('should return truthy if atleast one sale is selected', () => {
                controller.sales[0].checked = true;

                const result = controller.hasSelectedSales();

                expect(result).toBeTruthy();
            });
        });

        describe('hasOneSaleSelected()', () => {
            it('should return truthy if just one sale is selected', () => {
                controller.sales[0].checked = true;

                const result = controller.hasOneSaleSelected();

                expect(result).toBeTruthy();
            });

            it('should return falsy if more than one sale is selected', () => {
                controller.sales[0].checked = true;
                controller.sales[1].checked = true;

                const result = controller.hasOneSaleSelected();

                expect(result).toBeFalsy();
            });
        });

        describe('newInstances()', () => {
            it(`should return a list of sales that doesn't have an id`, () => {
                const newEmptySale = {quantity: 10, checked: true};
                controller.sales.push(newEmptySale);

                const result = controller.newInstances();
                const firstNewSale = result[0];

                expect(result.length).toEqual(1);
                expect(firstNewSale.id).toBeUndefined();
                expect(firstNewSale.quantity).toEqual(10);
            });
        });

        describe('resetChanges()', () => {
            it(`should not call the watcher updateOriginalData() method`, () => {
                jest.spyOn(controller.$.watcher, 'updateOriginalData').mockReturnThis();

                const newEmptySale = {quantity: 10};
                controller.sales.push(newEmptySale);
                controller.resetChanges();

                expect(controller.$.watcher.updateOriginalData).not.toHaveBeenCalledWith();
            });

            it(`should call the watcher updateOriginalData() method`, () => {
                jest.spyOn(controller.$.watcher, 'updateOriginalData').mockReturnThis();

                controller.resetChanges();

                expect(controller.$.watcher.updateOriginalData).toHaveBeenCalledWith();
            });
        });

        describe('state()', () => {
            it('should make an HTTP post query, then call the showSuccess(),' +
                ' reload() and resetChanges() methods', () => {
                jest.spyOn(controller.card, 'reload').mockReturnThis();
                jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
                jest.spyOn(controller, 'resetChanges').mockReturnThis();

                const expectedParams = {ticketFk: 1, code: 'OK'};
                $httpBackend.expect('POST', `Tickets/state`, expectedParams).respond(200);
                controller.state('OK');
                $httpBackend.flush();

                expect(controller.card.reload).toHaveBeenCalledWith();
                expect(controller.vnApp.showSuccess).toHaveBeenCalled();
                expect(controller.resetChanges).toHaveBeenCalledWith();
            });
        });

        describe('removeSales()', () => {
            it('should make an HTTP post query, then call the showSuccess(),' +
                ' removeSelectedSales() and resetChanges() methods', () => {
                jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
                jest.spyOn(controller, 'removeSelectedSales').mockReturnThis();
                jest.spyOn(controller, 'resetChanges').mockReturnThis();

                const firstSale = controller.sales[0];
                firstSale.checked = true;
                const expectedParams = {sales: [firstSale], ticketId: 1};
                $httpBackend.expect('POST', `Sales/deleteSales`, expectedParams).respond(200);
                controller.removeSales();
                $httpBackend.flush();

                expect(controller.removeSelectedSales).toHaveBeenCalledWith();
                expect(controller.vnApp.showSuccess).toHaveBeenCalled();
                expect(controller.resetChanges).toHaveBeenCalledWith();
            });
        });

        describe('removeSelectedSales()', () => {
            it('should remove the selected sales from the controller sale property', () => {
                jest.spyOn(controller, 'resetChanges').mockReturnThis();

                const firstSale = controller.sales[0];
                firstSale.checked = true;

                controller.removeSelectedSales();

                const lastSale = controller.sales[0];

                expect(controller.sales.length).toEqual(1);
                expect(lastSale.id).toEqual(4);
            });
        });

        describe('createClaim()', () => {
            it('should call to the claimConfirm show() method', () => {
                jest.spyOn(controller.$.claimConfirm, 'show').mockReturnThis();

                controller.createClaim();

                expect(controller.$.claimConfirm.show).toHaveBeenCalledWith();
            });
        });

        describe('onCreateClaimAccepted()', () => {
            it('should perform a query and call window open', () => {
                jest.spyOn(controller, 'resetChanges').mockReturnThis();
                jest.spyOn(controller.vnApp, 'getUrl').mockReturnThis();
                Object.defineProperty(window, 'location', {
                    value: {
                        href: () => {}
                    },
                });
                jest.spyOn(controller.window.location, 'href');

                const newEmptySale = {quantity: 10};
                controller.sales.push(newEmptySale);
                const firstSale = controller.sales[0];
                const claimId = 1;
                firstSale.checked = true;

                const expectedParams = {ticketId: 1, sales: [firstSale]};
                $httpBackend.expect('POST', `Claims/createFromSales`, expectedParams).respond(200, {id: claimId});
                controller.onCreateClaimAccepted();
                $httpBackend.flush();

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

        describe('setTransferParams()', () => {
            it('should define the transfer object on the controller and its default properties', () => {
                const firstSale = controller.sales[0];
                firstSale.checked = true;
                const expectedResponse = [firstSale];

                $httpBackend.expect('GET', `clients/1101/lastActiveTickets?ticketId=1`).respond(expectedResponse);
                controller.setTransferParams();
                $httpBackend.flush();

                const lastActiveTickets = controller.transfer.lastActiveTickets;

                expect(controller.transfer).toBeDefined();
                expect(lastActiveTickets).toBeDefined();
                expect(lastActiveTickets[0].id).toEqual(1);
            });
        });

        describe('transferSales()', () => {
            it('should transfer sales to a ticket and then call to the $state go() method', () => {
                jest.spyOn(controller.$state, 'go').mockReturnThis();

                controller.transfer = {
                    sales: [{id: 1, itemFk: 1}, {id: 2, itemFk: 4}]
                };

                const ticketIdToTransfer = 13;
                const expectedResponse = {id: ticketIdToTransfer};
                const params = {
                    ticketId: 13,
                    sales: controller.transfer.sales
                };

                $httpBackend.expect('POST', `tickets/1/transferSales`, params).respond(expectedResponse);
                controller.transferSales(ticketIdToTransfer);
                $httpBackend.flush();

                expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.sale', {id: ticketIdToTransfer});
            });
        });

        describe('updatePrice()', () => {
            it('should make an HTTP POST query, update the sale price ' +
                'and then call to the resetChanges() method', () => {
                jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
                jest.spyOn(controller, 'resetChanges').mockReturnThis();

                const selectedSale = controller.sales[0];
                selectedSale.checked = true;
                controller.$.editPricePopover = {hide: jest.fn()};
                controller.edit = {
                    price: 2,
                    sale: selectedSale
                };
                const expectedParams = {newPrice: 2};
                $httpBackend.expect('POST', `Sales/1/updatePrice`, expectedParams).respond(200, {price: 2});
                controller.updatePrice();
                $httpBackend.flush();

                expect(selectedSale.price).toEqual(2);
                expect(controller.vnApp.showSuccess).toHaveBeenCalled();
                expect(controller.$.editPricePopover.hide).toHaveBeenCalledWith();
                expect(controller.resetChanges).toHaveBeenCalledWith();
            });
        });

        describe('changeDiscount()', () => {
            it('should call to the updateDiscount() method and then to the editDiscount hide() method', () => {
                jest.spyOn(controller, 'updateDiscount').mockReturnThis();

                const selectedSale = controller.sales[0];
                selectedSale.checked = true;
                const expectedSale = selectedSale;
                controller.$.editDiscount = {hide: jest.fn()};
                controller.edit = {
                    discount: 10,
                    sale: selectedSale
                };

                controller.changeDiscount();

                expect(controller.updateDiscount).toHaveBeenCalledWith([expectedSale]);
                expect(controller.$.editDiscount.hide).toHaveBeenCalledWith();
            });
        });

        describe('changeMultipleDiscount()', () => {
            it('should call to the updateDiscount() method and then to the editDiscountDialog hide() method', () => {
                jest.spyOn(controller, 'updateDiscount').mockReturnThis();

                const firstSelectedSale = controller.sales[0];
                firstSelectedSale.checked = true;

                const secondSelectedSale = controller.sales[1];
                secondSelectedSale.checked = true;

                const expectedSales = [firstSelectedSale, secondSelectedSale];
                controller.$.editDiscount = {hide: jest.fn()};
                controller.edit = {
                    discount: 10,
                    sales: expectedSales
                };

                controller.changeMultipleDiscount();

                expect(controller.updateDiscount).toHaveBeenCalledWith(expectedSales);
                expect(controller.$.editDiscount.hide).toHaveBeenCalledWith();
            });

            it('should not call to the updateDiscount() method and then' +
                ' to the editDiscountDialog hide() method', () => {
                jest.spyOn(controller, 'updateDiscount').mockReturnThis();

                const firstSelectedSale = controller.sales[0];
                firstSelectedSale.checked = true;
                firstSelectedSale.discount = 10;

                const secondSelectedSale = controller.sales[1];
                secondSelectedSale.checked = true;
                secondSelectedSale.discount = 10;

                const expectedSales = [firstSelectedSale, secondSelectedSale];
                controller.$.editDiscount = {hide: jest.fn()};
                controller.edit = {
                    discount: 10,
                    sales: expectedSales
                };

                controller.changeMultipleDiscount();

                expect(controller.updateDiscount).not.toHaveBeenCalledWith(expectedSales);
                expect(controller.$.editDiscount.hide).toHaveBeenCalledWith();
            });
        });

        describe('updateDiscount()', () => {
            it('should make an HTTP POST query, update the sales discount ' +
                'and then call to the resetChanges() method', () => {
                jest.spyOn(controller, 'resetChanges').mockReturnThis();
                jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();

                const expectedDiscount = 10;
                const firstSelectedSale = controller.sales[0];
                firstSelectedSale.checked = true;

                const secondSelectedSale = controller.sales[1];
                secondSelectedSale.checked = true;

                controller.edit = {
                    discount: expectedDiscount
                };

                const expectedSales = [firstSelectedSale, secondSelectedSale];
                const expectedSaleIds = [firstSelectedSale.id, secondSelectedSale.id];
                const expectedParams = {salesIds: expectedSaleIds, newDiscount: expectedDiscount, manaCode: 'mana'};
                $httpBackend.expect('POST', `Tickets/1/updateDiscount`, expectedParams).respond(200, {discount: 10});
                controller.updateDiscount(expectedSales);
                $httpBackend.flush();

                expect(firstSelectedSale.discount).toEqual(expectedDiscount);
                expect(secondSelectedSale.discount).toEqual(expectedDiscount);
                expect(controller.vnApp.showSuccess).toHaveBeenCalled();
                expect(controller.resetChanges).toHaveBeenCalledWith();
            });
        });

        describe('getNewPrice()', () => {
            it('should return the total price simulation from a price change', () => {
                const selectedSale = controller.sales[0];
                selectedSale.checked = true;
                controller.edit = {
                    price: 2,
                    sale: selectedSale
                };

                const expectedAmount = 10;
                const result = controller.getNewPrice();

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

            it('should return the total price simulation from a discount change', () => {
                const selectedSale = controller.sales[0];
                selectedSale.checked = true;
                controller.edit = {
                    discount: 10,
                    sale: selectedSale
                };

                const expectedAmount = 105.75;
                const result = controller.getNewPrice();

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

        describe('hasReserves()', () => {
            it('should return true for any sale marked as reserved', () => {
                const selectedSale = controller.sales[0];
                selectedSale.reserved = true;

                const result = controller.hasReserves();

                expect(result).toBeTruthy();
            });
        });

        describe('unmarkAsReserved()', () => {
            it('should call setReserved with false', () => {
                jest.spyOn(controller, 'setReserved');

                controller.unmarkAsReserved(false);

                expect(controller.setReserved).toHaveBeenCalledWith(false);
            });
        });

        describe('markAsReserved()', () => {
            it('should call setReserved with true', () => {
                jest.spyOn(controller, 'setReserved');

                controller.markAsReserved(true);

                expect(controller.setReserved).toHaveBeenCalledWith(true);
            });
        });

        describe('setReserved()', () => {
            it('should call getCheckedLines, $.index.accept and make a query ', () => {
                const selectedSale = controller.sales[0];
                selectedSale.checked = true;
                const expectedParams = {
                    sales: [selectedSale],
                    ticketId: 1,
                    reserved: false
                };

                $httpBackend.expectPOST(`Sales/reserve`, expectedParams).respond();
                controller.unmarkAsReserved(false);
                $httpBackend.flush();
            });
        });

        describe('newOrderFromTicket()', () => {
            it('should make an HTTP post query and then open the new order on a new tab', () => {
                const expectedParams = {ticketFk: 1};
                const expectedResponse = {id: 123};

                window.open = jasmine.createSpy('open');
                controller.$state.href = jasmine.createSpy('href')
                    .and.returnValue('/somePath');

                $httpBackend.expect('POST', `Orders/newFromTicket`, expectedParams).respond(expectedResponse);
                controller.newOrderFromTicket();
                $httpBackend.flush();

                expect(window.open).toHaveBeenCalledWith('/somePath', '_blank');
            });
        });

        describe('showSMSDialog()', () => {
            it('should open an SMS dialog with specified data', () => {
                jest.spyOn(controller.$.sms, 'open');

                const selectedSale = controller.sales[0];
                selectedSale.checked = true;
                controller.showSMSDialog();

                expect(controller.$.sms.open).toHaveBeenCalledWith();
                expect(controller.newSMS.destination).toEqual(111111111);
                expect(controller.newSMS.message).not.toEqual('');
            });
        });

        describe('changeQuantity()', () => {
            it('should not call addSale() or updateQuantity() methods', () => {
                jest.spyOn(controller, 'addSale');
                jest.spyOn(controller, 'updateQuantity');

                const sale = {itemFk: 4};
                controller.changeQuantity(sale);

                expect(controller.addSale).not.toHaveBeenCalled();
                expect(controller.updateQuantity).not.toHaveBeenCalled();
            });

            it('should call addSale() method', () => {
                jest.spyOn(controller, 'addSale');

                const sale = {itemFk: 4, quantity: 5};
                controller.changeQuantity(sale);

                expect(controller.addSale).toHaveBeenCalledWith(sale);
            });

            it('should call updateQuantity() method', () => {
                jest.spyOn(controller, 'addSale');
                jest.spyOn(controller, 'updateQuantity');

                const sale = {id: 1, itemFk: 4, quantity: 5};
                controller.changeQuantity(sale);

                expect(controller.addSale).not.toHaveBeenCalled();
                expect(controller.updateQuantity).toHaveBeenCalledWith(sale);
            });
        });

        describe('updateQuantity()', () => {
            it('should make a POST query saving sale quantity', () => {
                jest.spyOn(controller, 'resetChanges').mockReturnThis();

                const selectedSale = controller.sales[0];
                selectedSale.checked = true;
                selectedSale.quantity = 10;

                const expectedParams = {quantity: 10};
                $httpBackend.expect('POST', `Sales/1/updateQuantity`, expectedParams).respond();
                controller.updateQuantity(selectedSale);
                $httpBackend.flush();

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

        describe('updateConcept()', () => {
            it('should make a POST query saving sale concept', () => {
                jest.spyOn(controller, 'resetChanges').mockReturnThis();

                const selectedSale = controller.sales[0];
                selectedSale.checked = true;
                selectedSale.concept = 'My new weapon';

                const expectedParams = {newConcept: 'My new weapon'};
                $httpBackend.expect('POST', `Sales/1/updateConcept`, expectedParams).respond();
                controller.updateConcept(selectedSale);
                $httpBackend.flush();

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

        describe('addSale()', () => {
            it('should make a POST query adding a new sale', () => {
                jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
                jest.spyOn(controller, 'resetChanges').mockReturnThis();

                const newSale = {itemFk: 4, quantity: 10};
                const expectedParams = {barcode: 4, quantity: 10};
                const expectedResult = {
                    id: 30,
                    quantity: 10,
                    discount: 0,
                    price: 0,
                    itemFk: 4,
                    item: {
                        subName: 'Item subName',
                        image: '30.png'
                    }
                };

                $httpBackend.expect('POST', `tickets/1/addSale`, expectedParams).respond(expectedResult);
                controller.addSale(newSale);
                $httpBackend.flush();

                expect(controller.vnApp.showSuccess).toHaveBeenCalled();
                expect(controller.resetChanges).toHaveBeenCalledWith();
            });
        });

        describe('isTicketEditable()', () => {
            it('should make a HTTP GET query and set the isEditable property on the controller', () => {
                $httpBackend.expect('GET', `Tickets/1/isEditable`).respond(200, true);
                controller.isTicketEditable();
                $httpBackend.flush();

                expect(controller.isEditable).toBeDefined();
                expect(controller.isEditable).toBeTruthy();
            });
        });

        describe('isTicketLocked()', () => {
            it('should make a HTTP GET query and set the isLocked property on the controller', () => {
                $httpBackend.expect('GET', `Tickets/1/isLocked`).respond(200, false);
                controller.isTicketLocked();
                $httpBackend.flush();

                expect(controller.isLocked).toBeDefined();
                expect(controller.isLocked).toBeFalsy();
            });
        });

        describe('calculateSalePrice()', () => {
            it('should make an HTTP post query', () => {
                jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
                jest.spyOn(controller.$.model, 'refresh').mockReturnThis();

                controller.sales.forEach(sale => {
                    sale.checked = true;
                });

                $httpBackend.expect('POST', `Sales/recalculatePrice`).respond(200);
                controller.calculateSalePrice();
                $httpBackend.flush();

                expect(controller.vnApp.showSuccess).toHaveBeenCalled();
                expect(controller.$.model.refresh).toHaveBeenCalledWith();
            });
        });

        describe('createRefund()', () => {
            it('should make a query and then navigate to the created ticket sales section', () => {
                jest.spyOn(controller, 'selectedValidSales').mockReturnValue(controller.sales);
                jest.spyOn(controller.$state, 'go');
                const params = {
                    salesIds: [1, 4],
                    negative: true
                };
                const refundTicket = {id: 99};
                $httpBackend.expect('POST', 'Sales/clone', params).respond(200, [refundTicket]);
                controller.createRefund();
                $httpBackend.flush();

                expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.sale', refundTicket);
            });
        });

        describe('itemSearchFunc()', () => {
            it('should return the filter by id property for an input of a number', () => {
                const itemId = 1;
                const result = controller.itemSearchFunc(itemId);

                expect(result).toEqual({id: itemId});
            });

            it('should return the filter by bank property for an input of an string', () => {
                const itemName = 'Bow';
                const result = controller.itemSearchFunc(itemName);

                expect(result).toEqual({name: {like: '%' + itemName + '%'}});
            });
        });

        describe('cancel()', () => {
            it('should call hide()', () => {
                jest.spyOn(controller.$.editDiscount, 'hide').mockReturnThis();
                controller.cancel();

                expect(controller.$.editDiscount.hide).toHaveBeenCalledWith();
            });
        });

        describe('sortBy()', () => {
            it('should set reverse and propertyName properties', () => {
                const propertyName = 'id';
                controller.sortBy(propertyName);

                expect(controller.propertyName).toEqual(propertyName);
                expect(controller.reverse).toEqual(false);
            });
        });
    });
});