import policyVehicles from '@/layouts/policy-vehicles/policy-vehicles.vue';
import settleAllPromises from '@/helpers/layout-helper';
import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper';
import { useMainStore } from '@/store';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import navigationScenarios from '@/router/router-constants/navigation-scenarios';
import baseMixin from '@/mixins/base-mixin';
import { getRandomString, getRandomInt } from '@/helpers/data-generation';
import endorsementOptions from '@/constants/endorsement-options';
import { createTestingPinia } from '@pinia/testing';
import vehicleSelectionOptions from '@/constants/vehicle-selection-options';
import bailoutCode from '@/constants/bailoutCode';
import bailoutMessage from '@/constants/bailoutMessage';
import issPageValues from '@/router/router-constants/issPage-values';

// Mock fetchCmsContentForPage
jest.mock('@/helpers/cms-content-helper', () => ({
    fetchCmsContentForPage: jest.fn(),
    doesCopyContainRouterLink: jest.fn(),
    splitCopyOnCMSPlaceHolder: jest.fn().mockImplementation(() => 'test'),
    getRouterLinkRouteFromCopy: jest.fn(),
    getRouterLinkDisplayTextFromCopy: jest.fn(),
    splitCMSCopyOnParagraphTag: jest.fn()
}));

// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => jest.fn());

const mockMixin = {
    methods: {
        getCmsContent: jest.fn()
    },
    computed: {
        dynamicStrings() {
            return { ROUTER_LINK: 'routerLink:' };
        }
    }
};

/** @ignore */
function setupMocks() {
    const mountOptions = getMountOptions({
        router: {
            navigate: jest.fn()
        },
        mixins: [mockMixin],
        global: {
            mocks: {
                $route: {
                    params: {
                        id: 1
                    }
                },
                $router: {
                    navigate: jest.fn()
                }
            },
            plugins: [createTestingPinia()]
        }
    });

    const apiResponses = {
        cmsContent: {}
    };

    settleAllPromises.mockImplementation(() => apiResponses);
    fetchCmsContentForPage.mockImplementation(() => Promise.resolve());

    const wrapper = shallowMount(policyVehicles, mountOptions);
    wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => '');
    wrapper.vm.setCmsContent = jest.fn();
    wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
    wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn();
    wrapper.vm.$refs.siteFooter.disableForwardButton = jest.fn();
    return { wrapper };
}

beforeEach(() => {
    useMainStore().applicationUser.pageData[issPageValues.BAILOUT_PAGE] = null;
});

describe('policy-vehicles.vue', () => {
    test('Should navigate to CLICKED_BACK if backButtonAction is run', async () => {
        // Arrange
        const { wrapper } = setupMocks({});

        // Act
        await wrapper.vm.backButtonAction();

        // Assert
        expect(wrapper.vm.$router.navigate).toBeCalled();
    });

    describe('forwardButtonAction', () => {
        // eslint-disable-next-line max-len
        test(
            // eslint-disable-next-line max-len
            'Selected VIN matches vehicle listed in system => update vehicle and navigate forward with CLICKED_FORWARD_LISTED_VEHICLE scenario.',
            async () => {
            // Arrange
                const { wrapper } = setupMocks({});

                const vin = getRandomString(17, 17);
                const policyVehicle = { vin };
                await wrapper.setData({
                    selectedVehicleVin: vin,
                    selectedPolicyVehicle: policyVehicle,
                    policyVehicles: [
                        policyVehicle
                    ],
                    policyVinFound: true
                });

                const year = getRandomInt(1998, 2023);
                const lookupVehicleResponse = {
                    data: {
                        year
                    }
                };
                const store = useMainStore();
                store.lookupVehicleByVin.mockReturnValue(Promise.resolve(lookupVehicleResponse));

                const updateVehicleInput = {
                    year,
                    vin
                };

                const updateVehicleCoverage = {
                    noCoverage: true,
                    deductible: 0,
                    repairWaived: false,
                    endorsements: []
                };

                // Act
                await wrapper.vm.forwardButtonAction();

                // Assert
                expect(wrapper.vm.bailout).toBeFalsy();
                expect(wrapper.vm.policyVinFound).toBeTruthy();
                expect(store.updateVehicle).toHaveBeenCalledWith(updateVehicleInput);
                expect(store.updateVehicleCoverage).toHaveBeenCalledWith(updateVehicleCoverage);
                expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
                    navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE,
                    undefined,
                    {},
                    {}
                );
            }
        );

        test(
            // eslint-disable-next-line max-len
            'Selected VIN matches vehicle listed in system and policy vehicle has Educator endorsement => update vehicle and navigate forward with CLICKED_FORWARD_WITH_ENDORSEMENTS scenario.',
            async () => {
            // Arrange
                const { wrapper } = setupMocks({});

                const vin = getRandomString(17, 17);
                const endorsements = ['Educator'];
                await wrapper.setData({
                    selectedVehicleVin: vin,
                    policyVehicles: [
                        {
                            vin,
                            endorsements
                        }
                    ]
                });

                const year = getRandomInt(1998, 2023);
                const lookupVehicleResponse = {
                    data: {
                        year
                    }
                };
                const store = useMainStore();
                store.lookupVehicleByVin.mockReturnValue(Promise.resolve(lookupVehicleResponse));

                const updateVehicleInput = {
                    year,
                    vin
                };

                const updateVehicleCoverage = {
                    noCoverage: true,
                    deductible: 0,
                    repairWaived: false,
                    endorsements
                };

                // Act
                await wrapper.vm.forwardButtonAction();

                // Assert
                expect(store.updateVehicle).toHaveBeenCalledWith(updateVehicleInput);
                expect(store.updateVehicleCoverage).toHaveBeenCalledWith(updateVehicleCoverage);
                expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
                    navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS,
                    undefined
                );
            }
        );

        test(
            // eslint-disable-next-line max-len
            'Selected VIN matches vehicle listed in system and policy vehicle has Parking Guard endorsement => update vehicle and navigate forward with CLICKED_FORWARD_WITH_ENDORSEMENTS scenario.',
            async () => {
            // Arrange
                const { wrapper } = setupMocks({});

                const vin = getRandomString(17, 17);
                const endorsements = ['Parking Guard'];
                const policyVehicle = {
                    vin,
                    endorsements
                };
                await wrapper.setData({
                    selectedVehicleVin: vin,
                    selectedPolicyVehicle: policyVehicle,
                    policyVehicles: [
                        policyVehicle
                    ]
                });

                const year = getRandomInt(1998, 2023);
                const lookupVehicleResponse = {
                    data: {
                        year
                    }
                };
                const store = useMainStore();
                store.lookupVehicleByVin.mockReturnValue(Promise.resolve(lookupVehicleResponse));

                const updateVehicleInput = {
                    year,
                    vin
                };

                const updateVehicleCoverage = {
                    noCoverage: true,
                    deductible: 0,
                    repairWaived: false,
                    endorsements
                };

                // Act
                await wrapper.vm.forwardButtonAction();

                // Assert
                expect(store.updateVehicle).toHaveBeenCalledWith(updateVehicleInput);
                expect(store.updateVehicleCoverage).toHaveBeenCalledWith(updateVehicleCoverage);
                expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
                    navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS,
                    undefined
                );
            }
        );

        test(
            'Error in lookupVehicleByVin call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario.',
            async () => {
            // Arrange
                const { wrapper } = setupMocks({});
                const lookupReturnValue = { error: true, status: 500, data: 'error' };
                wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(lookupReturnValue);

                const vin = getRandomString(17, 17);
                await wrapper.setData({
                    selectedVehicleVin: vin,
                    policyVehicles: [
                        {
                            vin
                        }
                    ]
                });

                // Act
                wrapper.vm.mainStore.applicationUser.pageData[issPageValues.BAILOUT_PAGE] = {
                    'bailout-page': {
                        bailoutCode: bailoutCode.VehicleVinLookupError
                    }
                };
                await wrapper.vm.forwardButtonAction();

                // Assert
                // eslint-disable-next-line max-len
                expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(bailoutMessage.vehicleVinLookupError(vin, lookupReturnValue.data));
                expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
                    navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
                    undefined,
                    {},
                    {}
                );
            }
        );

        test(
            // eslint-disable-next-line max-len
            'Vehicle not found in lookupVehicleByVin call => policyVinFound false and navigate forward with CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND scenario.',
            async () => {
                // Arrange
                const { wrapper } = setupMocks({});
                wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 404 });

                const vin = getRandomString(17, 17);
                await wrapper.setData({
                    selectedVehicleVin: vin,
                    policyVinFound: true,
                    policyVehicles: [{
                        vin,
                        vehicleYear: getRandomInt(2000, 2100),
                        vehicleMake: getRandomString(5, 10),
                        vehicleMode: getRandomString(5, 10),
                        vehicleStyle: getRandomString(5, 10)
                    }]
                });

                // Act
                await wrapper.vm.forwardButtonAction();

                // Assert
                expect(wrapper.vm.bailout).toBeFalsy();
                expect(wrapper.vm.policyVinFound).toBeFalsy();
                expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
                    navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
                    undefined,
                    {},
                    {}
                );
            }
        );

        test('vehicle not listed => navigate forward with CLICKED_FORWARD_NON_LISTED_VEHICLE scenario.', async () => {
            // Arrange
            const { wrapper } = setupMocks({});

            await wrapper.setData({
                selectedVehicleVin: vehicleSelectionOptions.VEHICLE_NOT_LISTED
            });

            // Act
            await wrapper.vm.forwardButtonAction();

            // Assert
            expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
                navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE,
                undefined,
                {},
                {}
            );
        });
    });

    describe('noCoverageForSelectedVehicle computed property', () => {
        it('No vehicle match => returns true', async () => {
            // Arrange
            const selectedVin = getRandomString(17, 17);
            const otherVin = getRandomString(17, 17);
            const policyVehicle = {
                vin: otherVin
            };
            const testValues = {
                selectedVehicleVin: selectedVin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.noCoverageForSelectedVehicle.call(testValues);

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

        it('Coverages list empty => true', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const policyVehicle = {
                vin,
                coverages: []
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.noCoverageForSelectedVehicle.call(testValues);

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

        it('Coverages list non-empty => false', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const policyVehicle = {
                vin,
                coverages: [
                    {
                        deductible: 0
                    }
                ]
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.noCoverageForSelectedVehicle.call(testValues);

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

    describe('deductibleForSelectedVehicle computed property', () => {
        it('No vehicle match => undefined returned', () => {
            // Arrange
            const selectedVin = getRandomString(17, 17);
            const otherVin = getRandomString(17, 17);
            const testValues = {
                selectedVehicleVin: selectedVin,
                selectedPolicyVehicle: null,
                policyVehicles: [
                    {
                        vin: otherVin
                    }
                ]
            };

            // Act
            const result = policyVehicles.computed.deductibleForSelectedVehicle.call(testValues);

            // Assert
            expect(result).toBe(undefined);
        });

        it('Vehicle match with empty coverages list => 0 returned', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const policyVehicle = {
                vin,
                coverages: []
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.deductibleForSelectedVehicle.call(testValues);

            // Assert
            expect(result).toBe(0);
        });

        it('Coverages list non empty => deductible from first coverage returned', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const firstDeductible = getRandomInt(1, 1000);
            const secondDeductible = getRandomInt(1, 1000);
            const policyVehicle = {
                vin,
                coverages: [
                    {
                        deductible: firstDeductible
                    },
                    {
                        deductible: secondDeductible
                    }
                ]
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.deductibleForSelectedVehicle.call(testValues);

            // Assert
            expect(result).toBe(firstDeductible);
        });
    });

    describe('repairWaivedForSelectedVehicle computed property', () => {
        it('No vehicle match => false returned', () => {
            // Arrange
            const selectedVin = getRandomString(17, 17);
            const otherVin = getRandomString(17, 17);
            const testValues = {
                selectedVehicleVin: selectedVin,
                selectedPolicyVehicle: null,
                policyVehicles: [
                    {
                        vin: otherVin
                    }
                ]
            };

            // Act
            const result = policyVehicles.computed.repairWaivedForSelectedVehicle.call(testValues);

            // Assert
            expect(result).toBe(false);
        });

        it('Endorsements list empty => false returned', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const policyVehicle = {
                vin,
                endorsements: []
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.repairWaivedForSelectedVehicle.call(testValues);

            // Assert
            expect(result).toBe(false);
        });

        it('Endorsements list non-empty, not containing repair waived => false returned', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const policyVehicle = {
                vin,
                endorsements: [endorsementOptions.EDUCATOR, endorsementOptions.PARKING_GUARD]
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.repairWaivedForSelectedVehicle.call(testValues);

            // Assert
            expect(result).toBe(false);
        });

        it('Endorsements list contains repair waived => true returned', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const policyVehicle = {
                vin,
                endorsements: [
                    endorsementOptions.EDUCATOR,
                    endorsementOptions.REPAIR_WAIVED,
                    endorsementOptions.PARKING_GUARD
                ]
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.repairWaivedForSelectedVehicle.call(testValues);

            // Assert
            expect(result).toBe(true);
        });
    });

    describe('endorsementsForSelectedVehicle computed property', () => {
        it('policyVehicles is null => empty endorsements array returned', () => {
            // Arrange
            const testValues = {
                selectedPolicyVehicle: null,
                policyVehicles: null
            };

            // Act
            const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues);
            const expected = [];

            // Assert
            expect(result).toStrictEqual(expected);
        });

        it('policyVehicles is empty => empty endorsements array returned', () => {
            // Arrange
            const testValues = {
                selectedPolicyVehicle: null,
                policyVehicles: []
            };

            // Act
            const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues);
            const expected = [];

            // Assert
            expect(result).toStrictEqual(expected);
        });

        it('selectedVehicleVin does not match any vin in policyVehicles => empty endorsements array returned', () => {
            // Arrange
            const selectedVin = getRandomString(17, 17);
            const otherVin = getRandomString(17, 17);
            const testValues = {
                selectedVehicleVin: selectedVin,
                selectedPolicyVehicle: null,
                policyVehicles: [
                    {
                        vin: otherVin
                    }
                ]
            };

            // Act
            const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues);
            const expected = [];

            // Assert
            expect(result).toStrictEqual(expected);
        });

        it('vehicle VIN match with endorsements null => empty endorsements array returned', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const policyVehicle = {
                vin,
                endorsements: null
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues);
            const expected = [];

            // Assert
            expect(result).toStrictEqual(expected);
        });

        it('vehicle VIN match with empty endorsements => empty endorsements array returned', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const policyVehicle = {
                vin,
                endorsements: []
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues);
            const expected = [];

            // Assert
            expect(result).toStrictEqual(expected);
        });

        it('vehicle VIN match with endorsements => endorsements array returned', () => {
            // Arrange
            const vin = getRandomString(17, 17);
            const endorsements = [getRandomString(10, 20)];
            const policyVehicle = {
                vin,
                endorsements
            };
            const testValues = {
                selectedVehicleVin: vin,
                selectedPolicyVehicle: policyVehicle,
                policyVehicles: [
                    policyVehicle
                ]
            };

            // Act
            const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues);

            // Assert
            expect(result).toStrictEqual(endorsements);
        });
    });

    test('first vehicle is auto-selected if only one vehicle on policy', async () => {
        // Arrange
        const vin = getRandomString(17, 17);
        useMainStore().order = {
            policy: {
                vehicles: [{ vin }]
            },
            vehicle: {
                vin: null
            }
        };

        const { wrapper } = setupMocks();

        // Act
        await wrapper.vm.$nextTick();

        // Assert
        expect(wrapper.vm.selectedVehicleVin).toBe(vin);
    });

    test('selected vehicle is a non-serviceable big truck, display no service alert', async () => {
        // Arrange
        const vin = getRandomString(17, 17);
        const policyVehicle = {
            vin,
            isBigTruck: true,
            canSafeliteService: false
        };

        const { wrapper } = setupMocks({});

        await wrapper.setData({
            selectedVehicleVin: vin,
            selectedPolicyVehicle: policyVehicle,
            policyVehicles: [policyVehicle],
            policyVinFound: true
        });

        wrapper.vm.displayNoServiceAlert = true;
        await wrapper.vm.$nextTick();

        // Assert
        expect(wrapper.findComponent({ ref: 'AlertNoService' }).isVisible()).toBe(true);
    });

    test('selected vehicle is a serviceable big truck, do not display no service alert', async () => {
        // Arrange
        const vin = getRandomString(17, 17);
        const policyVehicle = {
            vin,
            isBigTruck: true,
            canSafeliteService: true
        };

        const { wrapper } = setupMocks({});

        await wrapper.setData({
            selectedVehicleVin: vin,
            selectedPolicyVehicle: policyVehicle,
            policyVehicles: [policyVehicle],
            policyVinFound: true
        });

        wrapper.vm.displayNoServiceAlert = false;
        await wrapper.vm.$nextTick();

        // Assert
        expect(wrapper.findComponent({ ref: 'AlertNoService' }).exists()).toBe(false);
    });
});
