// Components
import licensePlateLookup from "@/layouts/license-plate-lookup/license-plate-lookup.vue";

// Supporting Files
import { settleAllPromises } from "@/helpers/layout-helper.js";
import * as navigateToHeritage from "@/helpers/heritage-integration/navigation-helper";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
import { storeActions } from "@/constants/store-actions";
import { storeMutations } from "@/constants/store-mutations";
import store from "@/store";
import { experimentSettings } from "@/constants/experiments";

jest.mock("@/assets/img/loader.gif", () => "loader.gif");
jest.mock("@/assets/img/windshield.png", () => "windshield.png");

// Mock our module for promises.
jest.mock("@/helpers/layout-helper.js", () => ({
    settleAllPromises: jest.fn(),
}));

// Mock fetchCmsContentForPage
jest.mock("@/helpers/cms-content-helper", () => ({
    fetchCmsContentForPage: jest.fn(),
}));

// Mock damage helper
jest.mock("@/helpers/damage-helper", () => ({
    isGlassAvailableForCarId: () => {
        return false;
    },
    getDamageString: () => {
        return "damage string";
    },
}));

describe("license-plate-lookup.vue", () => {
    describe("get values from store", () => {
        test("getLicensePlateFromStore returns store license plate", async () => {
            // Arrange
            const licensePlateInput = "TEST129";
            const { wrapper } = setupMocks({ licensePlate: licensePlateInput });

            // Act
            const licensePlate = wrapper.vm.getLicensePlateFromStore();

            // Assert
            expect(licensePlate).toEqual(licensePlateInput);
        });

        test("getRegistrationZipFromStore returns store registration zip", async () => {
            // Arrange
            const mockRegistrationZip = "77777";
            const { wrapper } = setupMocks({ registrationZipCode: mockRegistrationZip });

            // ACT
            const registrationZip = wrapper.vm.getRegistrationZipFromStore();

            // Assert
            expect(registrationZip).toEqual(mockRegistrationZip);
        });

        test("getEmailFromStore returns store customer email", async () => {
            // Arrange
            const mockEmail = "test12345@test.com";
            const { wrapper } = setupMocks({ emailAddress: mockEmail });

            // ACT
            const customerEmail = wrapper.vm.getEmailOrSmsFromStore();

            // Assert
            expect(customerEmail).toEqual(mockEmail);
        });

        test("getServiceZipFromStore returns store service zip", async () => {
            // Arrange
            const { wrapper } = setupMocks({ serviceLocationZipCode: "98765" });

            // ACT
            const serviceZip = wrapper.vm.getServiceZipFromStore();

            // Assert
            expect(serviceZip).toEqual("98765");
        });
    });

    describe("navigation", () => {
        test("BackButtonAction triggers a router.navigateWithoutSaving change", async () => {
            //Arrange
            const { wrapper } = setupMocks({});

            //Act
            licensePlateLookup.beforeRouteEnter.call(
                wrapper.vm,
                { query: { fmgPage: "license-plate-lookup" } },
                undefined,
                (c) => c(wrapper.vm)
            );

            wrapper.vm.backButtonAction();

            //Assert
            expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled();
        });

        describe("on forwardButtonAction click", () => {
            test("Navigate forward should be called and isCarIdDifferent should be set to false when data entered matches store data on forwardButtonAction click", async () => {
                // Arrange
                const mockCarId = "TESTID";
                const { wrapper } = setupMocks({
                    carId: mockCarId,
                    validateZipResponse: {
                        isServiceable: true,
                        isValid: true,
                        zipCodeCtu: "01820",
                        state: "OH",
                    },
                });

                wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
                wrapper.vm.navigateForward = jest.fn();
                wrapper.vm.dispatchStoreActionWithLogging = jest.fn().mockImplementation(() =>
                    Promise.resolve({
                        data: {
                            vehicle: {
                                carId: mockCarId,
                            },
                        },
                    })
                );

                //Act
                licensePlateLookup.beforeRouteEnter.call(
                    wrapper.vm,
                    { query: { fmgPage: "license-plate-lookup" } },
                    undefined,
                    (c) => c(wrapper.vm)
                );

                await wrapper.vm.forwardButtonAction();

                //Assert
                expect(wrapper.vm.isCarIdDifferent).toEqual(false);
                expect(wrapper.vm.navigateForward).toHaveBeenCalled();
            });

            test("Function should stop and datam isCarIdDifferent should be set to true when carId entered doesn't match store carId or previously entered carId on forwardButtonAction click", async () => {
                // Arrange

                // Setup state data / return data.
                const { wrapper } = setupMocks({ carId: "C111111", isServiceable: true });
                store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, []);

                wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
                    return "";
                });

                // Mock store action call
                wrapper.vm.dispatchStoreActionWithLogging = jest.fn().mockImplementation(() =>
                    Promise.resolve({
                        data: {
                            vehicle: {
                                carId: "C00000", // Make sure carId returned from call does not match carId in state.
                            },
                        },
                    })
                );

                //Act
                licensePlateLookup.beforeRouteEnter.call(
                    wrapper.vm,
                    { query: { fmgPage: "license-plate-lookup" } },
                    undefined,
                    (c) => c(wrapper.vm)
                );

                await wrapper.vm.forwardButtonAction();

                //Assert
                expect(wrapper.vm.isCarIdDifferent).toEqual(true);
            });

            test("Navigate forward should be called and isCarId should be set to true when carId entered matches previously entered carId and rest of data entered matches store data on forwardButtonAction click", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    carId: "C10000",
                    validateZipResponse: {
                        isServiceable: true,
                        isValid: true,
                        zipCodeCtu: "01820",
                        state: "OH",
                    },
                });

                wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
                    return "";
                });

                wrapper.vm.previouslyEnteredCarId = "C00000";
                wrapper.vm.navigateForward = jest.fn();

                // Mock store action call
                wrapper.vm.dispatchStoreActionWithLogging = jest.fn().mockImplementation(() =>
                    Promise.resolve({
                        data: {
                            vehicle: {
                                carId: "C00000", // Make sure carId returned from call does not match carId in state.
                            },
                        },
                    })
                );

                //Act
                licensePlateLookup.beforeRouteEnter.call(
                    wrapper.vm,
                    { query: { fmgPage: "license-plate-lookup" } },
                    undefined,
                    (c) => c(wrapper.vm)
                );

                await wrapper.vm.forwardButtonAction();

                //Assert
                expect(wrapper.vm.navigateForward).toHaveBeenCalled();
                expect(wrapper.vm.isCarIdDifferent).toEqual(true);
            });
        });

        describe("navigateForward", () => {
            test("navigateWithSaving should be called if isCarIdDifferent is true and isSelectedGlassAvailableForVehicle is false when navigateForward is called", async () => {
                // Arrange
                const { wrapper } = setupMocks({});

                //Act
                await wrapper.setData({
                    isCarIdDifferent: true,
                    isSelectedGlassAvailableForVehicle: false,
                });

                wrapper.vm.$router.navigate = jest.fn();
                wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
                    return "";
                });
                wrapper.vm.dispatchStoreActionWithLogging = jest.fn();

                await wrapper.vm.navigateForward();

                //Assert
                expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
            });

            test("navigateForwardWithSingleCarMatch should be called if isCarIdDifferent is false or isSelectedGlassAvailableForVehicle is true when navigateForward is called", async () => {
                // Arrange
                const { wrapper } = setupMocks({});

                //Act
                await wrapper.setData({
                    isCarIdDifferent: false,
                });
                wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
                    return "";
                });
                wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
                await wrapper.vm.navigateForward();

                //Assert
                expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalled();
            });

            test("carId matches returned vehicle => navigateForwardWithSingleCarMatch", async () => {
                // Arrange
                const { wrapper } = setupMocks({});
                await wrapper.setData({
                    isCarIdDifferent: false,
                });

                wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();

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

                //Assert
                expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1);
            });

            test("selected glass is available for returned vehicle => navigateForwardWithSingleCarMatch", async () => {
                // Arrange
                const { wrapper } = setupMocks({});
                await wrapper.setData({
                    isSelectedGlassAvailableForVehicle: true,
                });

                wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();

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

                //Assert
                expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1);
            });
        });
    });

    describe("button text", () => {
        test("Button Text should revert to initial value when licensePlate textfield has new text", async () => {
            // Arrange
            const { wrapper } = setupMocks({});

            //Act
            wrapper.setData({
                licensePlate: "NEWPLATE",
            });
            wrapper.vm.getCmsContent = jest.fn();
            await wrapper.vm.$nextTick();

            //Assert
            expect(wrapper.vm.$refs.navbar.updateButtonText).toHaveBeenCalled();
        });

        test("Button Text should revert to initial value when registrationZip textfield has new text", async () => {
            // Arrange
            const { wrapper } = setupMocks({});

            //Act
            await wrapper.setData({
                registrationZipCode: "55555",
            });
            wrapper.vm.getCmsContent = jest.fn();
            await wrapper.vm.$nextTick();

            //Assert
            expect(wrapper.vm.$refs.navbar.updateButtonText).toHaveBeenCalled();
        });

        test("Button Text should revert to initial value when serviceZip textfield has new text", async () => {
            // Arrange
            const { wrapper } = setupMocks({});

            //Act
            await wrapper.setData({
                serviceZipCode: "55555",
            });
            wrapper.vm.getCmsContent = jest.fn();
            await wrapper.vm.$nextTick();

            //Assert
            expect(wrapper.vm.$refs.navbar.updateButtonText).toHaveBeenCalled();
        });
    });

    describe("saving registrationZip and serviceZip on continue", () => {
        test("registrationZip is serviceable and vehicle match is found => sets service zip/state to registration zip/state", async () => {
            // NEEDS FIX - Unsure what this test should be doing but it seems incorrect or at least very confusing. If you
            //   comment out the "Act" entirely, then the test passes which seems to say the test isn't testing anything

            // Arrange
            const { wrapper } = setupMocks({
                validateZipResponse: {
                    isServiceable: true,
                    isValid: true,
                    zipCodeCtu: "01820",
                    state: "OH",
                },
                serviceLocationZipCode: "12345",
                registrationZipCode: "12345",
            });

            wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");

            await wrapper.setData({ registrationZipCode: "00000" });
            navigateToHeritage.navigateToHeritageFunnel = jest.fn();

            wrapper.vm.lookupVinByPlate = jest.fn(() => {
                return { data: { vehicle: { carId: "C00000" } } };
            });

            storeMutations.lookupVinByPlate = jest.fn().mockImplementation(() => {
                return { data: { vehicle: { carId: "C00000" } } };
            });

            wrapper.vm.dispatchStoreActionWithLogging = jest.fn().mockImplementation(() =>
                Promise.resolve({
                    data: {
                        vehicle: {
                            carId: "C00000",
                        },
                    },
                })
            );

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

            // Assert
            expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual(
                wrapper.vm.$store.getters.vehicle.registration.zipCode
            );

            expect(wrapper.vm.$store.getters.vehicle.registration.zipCode).toEqual("12345");
            expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual("12345");
        });

        test("vehicle match is found but registrationZip is not serviceable => shows service zip/state field", async () => {
            // Arrange
            // TODO FIX - seems like shouldn't have to set serviceLocationZipCode or registrationZipCode in state from the start
            const { wrapper } = setupMocks({
                validateZipResponse: { isServiceable: false, isValid: true },
                serviceLocationZipCode: "12345",
                registrationZipCode: "12345",
            });
            // TODO FIX - test succeeds even if you comment this line out, is it doing anything?
            wrapper.vm.validateZip = jest.fn().mockImplementation(() => {
                return { data: { isServiceable: false, state: "XX" } };
            });

            await wrapper.setData({ registrationZip: "00000" });
            navigateToHeritage.navigateToHeritageFunnel = jest.fn();

            wrapper.vm.dispatchStoreActionWithLogging = jest.fn().mockImplementation(() =>
                Promise.resolve({
                    data: {
                        vehicle: {
                            carId: "C00000",
                        },
                    },
                })
            );

            //TODO FIX - test succeeds even if comment out the Act section
            // Act
            await wrapper.vm.forwardButtonAction();

            // Assert
            const serviceZipField = wrapper.findComponent(
                "[cmsWidgetName='ServiceZipQuestionWidget']"
            );
            expect(serviceZipField.exists()).toBe(true);
            expect(serviceZipField.isVisible()).toBe(true);
        });

        test("registrationZip is not serviceable so serviceZip field is shown, continue clicked => user cannot continue", async () => {
            // Arrange
            const { wrapper } = setupMocks({});
            wrapper.vm.validateZip = jest.fn().mockImplementation(() => {
                return { data: { isServiceable: false, state: "XX" } };
            });

            await wrapper.setData({ registrationZipCode: "00000" });
            navigateToHeritage.navigateToHeritageFunnel = jest.fn();

            wrapper.vm.dispatchStoreActionWithLogging = jest.fn().mockImplementation(() =>
                Promise.resolve({
                    data: {
                        vehicle: {
                            carId: "C00000",
                        },
                    },
                })
            );

            await wrapper.vm.forwardButtonAction();
            wrapper.vm.$router.navigate = jest.fn();
            // At this point, serviceZip field is shown

            // Act
            // Continue without entering anything into service zip field
            await wrapper.vm.forwardButtonAction();

            // Assert
            const serviceZipField = wrapper.findComponent(
                "[cmsWidgetName='ServiceZipQuestionWidget']"
            );
            expect(serviceZipField.exists()).toBe(true);
            expect(serviceZipField.isVisible()).toBe(true);
            3;
            expect(navigateToHeritage.navigateToHeritageFunnel).not.toHaveBeenCalled();
            expect(wrapper.vm.$router.navigate).not.toHaveBeenCalled();
        });

        test("registrationZip is not serviceable so serviceZip field is shown, user enters serviceZip => user can continue", async () => {
            // Arrange
            const { wrapper } = setupMocks({
                validateZipResponse: { isServiceable: false, isValid: true },
            });
            const registrationZip = "00000";
            const serviceZip = "99999";

            wrapper.vm.navigateForward = jest.fn();

            const vinLookup = { data: { vehicle: { carId: "TESTID1" } } };
            wrapper.vm.lookupVin = jest.fn().mockImplementation(() => {
                return new Promise((resolve) => resolve(vinLookup));
            });

            await wrapper.setData({ registrationZipCode: registrationZip });
            await wrapper.vm.forwardButtonAction();

            // At this point, serviceZip field is shown
            await wrapper.setData({ serviceZipCode: serviceZip });

            // Act

            // Continue after entering input into service zip field
            await wrapper.vm.forwardButtonAction();

            // Assert
            const serviceZipField = wrapper.findComponent(
                "[cmsWidgetName='ServiceZipQuestionWidget']"
            );
            expect(serviceZipField.exists()).toBe(true);
            expect(serviceZipField.isVisible()).toBe(true);
        });

        test("registrationZip is not serviceable so serviceZip field is shown, user enters serviceZip => service and registration zips/states saved", async () => {
            // Arrange

            // TODO FIX - This test only works because 12345 is set in the state store when setupMocks is called. If u change registrationZip or serviceZip
            //  to any other value, then the test fails. Driving this home is you can comment out the entire "Act" portion (and the expect navigate forward be called)'
            //  and the test still succeeds so the checking ZIP fields part doesn't work.
            const { wrapper } = setupMocks({
                validateZipResponse: { isServiceable: false, isValid: true },
                //serviceLocationZipCode: "12345",
                //registrationZipCode: "12345"
            });
            const registrationZip = "12345";
            const serviceZip = "12345";

            wrapper.vm.navigateForward = jest.fn();
            const vinLookup = { data: { vehicle: { carId: "TESTID1" } } };
            wrapper.vm.lookupVin = jest.fn().mockImplementation(() => {
                return new Promise((resolve) => resolve(vinLookup));
            });

            await wrapper.setData({ registrationZipCode: registrationZip });
            await wrapper.vm.forwardButtonAction();

            // At this point, serviceZip field is shown
            await wrapper.setData({ serviceZip: serviceZip });

            const apiResponses = {
                serviceZipValidationResponse: {
                    isValid: true,
                    isServiceable: true,
                    state: "OH",
                    zipCodeCtu: "01820",
                },
                registrationZipValidationResponse: {
                    isValid: true,
                    isServiceable: false,
                },
            };

            const apiPromise = Promise.resolve(apiResponses);

            settleAllPromises.mockImplementation(() => apiPromise);

            // Act

            // Continue after entering value into service zip field
            await wrapper.vm.forwardButtonAction();

            // Assert
            // TODO - these two values have a value in pages "data" but not setting it in store; need to figure out why or mock it or something
            //expect(wrapper.vm.$store.vehicle.registration.zipCode).toEqual(registrationZip);
            //expect(wrapper.vm.$store.order.serviceLocation.zipCode).toEqual(serviceZip);

            //Verify we only navigateForward one time despite our calling forwardButtonAction twice
            expect(wrapper.vm.navigateForward).toBeCalledTimes(1);
        });
    });

    describe("miscellaneous", () => {
        test("CarId set, arePagePrerequisitesValid should be true ", async () => {
            //Arrange
            const { wrapper } = setupMocks({});
            store.commit(storeMutations.UPDATE_CAR_ID, "TESTCARID");

            //Act
            licensePlateLookup.beforeRouteEnter.call(
                wrapper.vm,
                { query: { fmgPage: "license-plate-lookup" } },
                undefined,
                (c) => c(wrapper.vm)
            );

            let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
            await nextTick();

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

        test("CarId not set, arePagePrerequisitesValid should be false ", async () => {
            //Arrange
            const { wrapper } = setupMocks({});

            //Act
            licensePlateLookup.beforeRouteEnter.call(
                wrapper.vm,
                { query: { fmgPage: "license-plate-lookup" } },
                undefined,
                (c) => c(wrapper.vm)
            );

            let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
            await nextTick();

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

function setupMocks({
    pageHeaderWidgetHeaderText = {},
    mountOptionsMockData = {},
    partsOrQuestions = [],
    validateZipResponse = { zipCodeCtu: null, isServiceable: false, isValid: true, state: null },
    //Values to set in the state store
    carId = null,
    serviceLocationZipCode = null,
    registrationZipCode = null,
    licensePlate = null, //"TESTPLATE",
    emailAddress = null, //"test@test.com"
}) {
    store.commit(storeMutations.RESET_STATE);
    //Mock api responses
    const apiResponses = {
        cmsContent: {
            FunnelSubHeaderWidget: pageHeaderWidgetHeaderText,
            VehicleBannerWidget: {
                GenericVehicleImage:
                    "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
            },
            FunnelHeaderWidget: {
                LogoImage:
                    "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
            },
        },
        serviceZipValidationResponse: {
            isValid: validateZipResponse.isValid,
            isServiceable: validateZipResponse.isServiceable,
            state: validateZipResponse.state,
            zipCodeCtu: validateZipResponse.zipCodeCtu,
        },
        registrationZipValidationResponse: {
            isValid: validateZipResponse.isValid,
            isServiceable: validateZipResponse.isServiceable,
            state: validateZipResponse.state,
            zipCodeCtu: validateZipResponse.zipCodeCtu,
        },
    };

    mountOptionsMockData = {
        ...mountOptionsMockData,
        router: {
            navigate: jest.fn(),
            navigateWithoutSaving: jest.fn(),
            navigateWithSaving: jest.fn(),
        },
        route: {
            query: {},
        },
        store: {
            getters: {
                emailOrSms: emailAddress,
                vehicle: {
                    registration: {
                        licensePlate: licensePlate,
                        zipCode: registrationZipCode,
                    },
                    carId: carId,
                },
                order: {
                    customer: {
                        emailAddress: emailAddress,
                    },
                    serviceLocation: {
                        zipCode: serviceLocationZipCode,
                    },
                },
            },
        },
        actionList: [
            {
                actionName: storeActions.GET_PARTS_OR_QUESTIONS,
                data: {
                    partsOrQuestions: partsOrQuestions,
                },
            },
        ],
    };

    const apiPromise = Promise.resolve(apiResponses);
    const mockMixin = {
        methods: {
            getSettingValue: jest.fn((settingName) => {
                if (settingName === experimentSettings.IS_EMAIL_OPTIONAL) {
                    return "true";
                }
                return "false";
            }),
        },
    };
    settleAllPromises.mockImplementation(() => apiPromise);
    fetchCmsContentForPage.mockImplementation(() => Promise.resolve());

    const mountOptions = getMountOptions({ ...mountOptionsMockData, mixins: [mockMixin] });
    mountOptions["attachTo"] = document.body; // append wrapper to document.body to test DOM methods

    const wrapper = shallowMount(licensePlateLookup, mountOptions);

    wrapper.vm.setCmsContent = jest.fn();
    wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
    wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
    wrapper.vm.$refs.navbar.removeLoader = jest.fn();
    return { wrapper, apiPromise };
}
