import * as navigationHelper from "@/helpers/heritage-integration/navigation-helper";
import * as orderHelper from "@/helpers/heritage-integration/order-helper";
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
import { storeActions } from "@/constants/store-actions";
import { storeMutations } from "@/constants/store-mutations";
import { setupMocksForJsFiles, getMockOrderInfo } from "@/helpers/unit-test-helper.js";
import { externalUrls } from "@/router/router-constants/externalUrl-values";
import { queryStrings } from "@/constants/query-strings";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";

import { damageLocationsSelected as glassLocations } from "@/constants/damage-locations-selected";

import store from "@/store";
import router from "@/router";

const getPageToRouteExistingOrderTo = navigationHelper.getPageToRouteExistingOrderTo;
const navigateToHeritageFunnel = navigationHelper.navigateToHeritageFunnel;

// Mock Lazy Load
jest.mock("@/router/dynamic-routing/component-loader.js", () => ({
    lazyLoadComponent: jest.fn(),
}));

describe("getPageToRouteExistingOrderTo", () => {
    describe("Explicit vs. Implicit Routing", () => {
        test("Should call explicit routing if `fmgPage` parameter is present in query", async () => {
            // Arrange
            const toRoute = {
                query: {
                    fmgPage: "schedule",
                },
            };

            mockLazyLoadComponentReturnValues();

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            // Assert
            // If direct, should be "schedule".
            // Otherwise, not.
            expect(result).toBe("schedule");
        });

        test("Should call implicit routing if `fmgPage` parameter is NOT present in query", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            mockLazyLoadComponentReturnValues();

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            // Assert
            // If direct, should be "schedule".
            // Otherwise, not.
            expect(result).not.toBe("schedule");
        });
    });

    describe("Implicit Routing", () => {
        test("should return vehicle", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: false,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.VEHICLE);
        });

        test("should return vehicle-damage", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: false,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.VEHICLE_DAMAGE);
        });

        test("user has YMMS and no vehicle questions > should return estimate", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            const mockExperimentsList = [
                {
                    universeName: "ConceptFunnel",
                    settings: {
                        SuppressVinCapture: false,
                    },
                },
            ];
            store.commit(storeMutations.UPDATE_EXPERIMENTS, mockExperimentsList);

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: true,
                [fmgPageValues.CAPABILITY_QUESTIONS]: false,
                [fmgPageValues.MOLDING_QUESTIONS]: false,
                [fmgPageValues.VEHICLE_PARTS]: false,
                [fmgPageValues.PART_QUESTIONS]: false,
                [fmgPageValues.VIN_LOOKUP]: true,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.ESTIMATE);
        });

        test("user has YMMS but no questions or carId > should return estimate", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: true,
                [fmgPageValues.CAPABILITY_QUESTIONS]: false,
                [fmgPageValues.MOLDING_QUESTIONS]: false,
                [fmgPageValues.VEHICLE_PARTS]: false,
                [fmgPageValues.PART_QUESTIONS]: false,
                [fmgPageValues.VIN_LOOKUP]: false,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.ESTIMATE);
        });

        test("user has capability questions and molding questions > should return capability questions", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: true,
                [fmgPageValues.CAPABILITY_QUESTIONS]: true,
                [fmgPageValues.MOLDING_QUESTIONS]: true,
                [fmgPageValues.VEHICLE_PARTS]: false,
                [fmgPageValues.PART_QUESTIONS]: false,
                [fmgPageValues.VIN_LOOKUP]: false,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.CAPABILITY_QUESTIONS);
        });

        test("user has molding questions and part questions > should return molding questions", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: true,
                [fmgPageValues.CAPABILITY_QUESTIONS]: false,
                [fmgPageValues.MOLDING_QUESTIONS]: true,
                [fmgPageValues.VEHICLE_PARTS]: false,
                [fmgPageValues.PART_QUESTIONS]: true,
                [fmgPageValues.VIN_LOOKUP]: false,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.MOLDING_QUESTIONS);
        });

        test("user has vehicle parts questions > should return vehicle-parts", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: true,
                [fmgPageValues.CAPABILITY_QUESTIONS]: false,
                [fmgPageValues.MOLDING_QUESTIONS]: false,
                [fmgPageValues.VEHICLE_PARTS]: true,
                [fmgPageValues.PART_QUESTIONS]: true,
                [fmgPageValues.VIN_LOOKUP]: false,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.VEHICLE_PARTS);
        });

        test("user has part questions > should return part-questions", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: true,
                [fmgPageValues.CAPABILITY_QUESTIONS]: false,
                [fmgPageValues.MOLDING_QUESTIONS]: false,
                [fmgPageValues.VEHICLE_PARTS]: false,
                [fmgPageValues.PART_QUESTIONS]: true,
                [fmgPageValues.VIN_LOOKUP]: false,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.PART_QUESTIONS);
        });

        test("should return quote, if not verified insurance", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: true,
                [fmgPageValues.CAPABILITY_QUESTIONS]: false,
                [fmgPageValues.MOLDING_QUESTIONS]: false,
                [fmgPageValues.VEHICLE_PARTS]: false,
                [fmgPageValues.PART_QUESTIONS]: true,
                [fmgPageValues.VIN_LOOKUP]: false,
                [fmgPageValues.QUOTE]: true,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.QUOTE);
        });

        test("should not return pages beyond quote even if prerequisites are met", async () => {
            // Arrange
            const toRoute = {
                query: {},
            };

            store.commit(storeMutations.UPDATE_IS_REPAIR, false);
            store.commit(storeMutations.UPDATE_MAKE, "acura");

            // Mock out the lazy load calls for all components.
            mockLazyLoadComponentReturnValues({
                [fmgPageValues.VEHICLE]: true,
                [fmgPageValues.VEHICLE_DAMAGE]: true,
                [fmgPageValues.ESTIMATE]: true,
                [fmgPageValues.CAPABILITY_QUESTIONS]: false,
                [fmgPageValues.MOLDING_QUESTIONS]: false,
                [fmgPageValues.VEHICLE_PARTS]: false,
                [fmgPageValues.PART_QUESTIONS]: true,
                [fmgPageValues.VIN_LOOKUP]: false,
                [fmgPageValues.QUOTE]: true,
                [fmgPageValues.SERVICE_LOCATION]: true,
                [fmgPageValues.SCHEDULE]: true,
            });

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            //Assert
            expect(result).toBe(fmgPageValues.QUOTE);
        });
    });

    describe("Explicit Routing", () => {
        test("Returns the value passed in in nominal case", async () => {
            // Arrange
            const toRoute = {
                query: {
                    fmgPage: "testValue",
                },
            };

            // Act
            const result = await getPageToRouteExistingOrderTo(toRoute);

            // Assert
            expect(result).toBe("testValue");
        });

        describe("Verified users", () => {
            test("Replace navigation to `vehicle` with `vehicle-damage`", async () => {
                // Arrange
                const toRoute = {
                    query: {
                        fmgPage: fmgPageValues.VEHICLE,
                    },
                };

                store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
                store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666");

                // Act
                const result = await getPageToRouteExistingOrderTo(toRoute);

                // Assert
                expect(result).toBe(fmgPageValues.VEHICLE_DAMAGE);
            });

            describe("Vin-Exempt", () => {
                test("Replace navigation to `quote` with `estimate`", async () => {
                    // Arrange
                    const toRoute = {
                        query: {
                            fmgPage: fmgPageValues.QUOTE,
                        },
                    };

                    store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
                    store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666");
                    store.commit(storeMutations.UPDATE_IS_REPAIR, true);
                    store.commit(storeMutations.UPDATE_MAKE, "acura");

                    // Act
                    const result = await getPageToRouteExistingOrderTo(toRoute);

                    // Assert
                    expect(result).toBe(fmgPageValues.ESTIMATE);
                });
            });

            describe("Vin-Required", () => {
                test("Replace navigation to `quote` with `vin-lookup`", async () => {
                    // Arrange
                    const toRoute = {
                        query: {
                            fmgPage: fmgPageValues.QUOTE,
                        },
                    };

                    store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
                    store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666");
                    store.commit(storeMutations.UPDATE_IS_REPAIR, false);
                    store.commit(storeMutations.UPDATE_MAKE, "acura");
                    store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [
                        { glassLocation: glassLocations.WINDSHIELD },
                    ]);
                    store.commit(storeMutations.UPDATE_VEHICLE_VIN, "abcd1234");

                    // Act
                    const result = await getPageToRouteExistingOrderTo(toRoute);

                    // Assert
                    expect(result).toBe(fmgPageValues.VIN_LOOKUP);
                });
            });
        });

        describe("Non-verified users", () => {
            test("Don't replace navigation to `vehicle` with `vehicle-damage`", async () => {
                // Arrange
                const toRoute = {
                    query: {
                        fmgPage: fmgPageValues.VEHICLE,
                    },
                };

                store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false);
                store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555");

                // Act
                const result = await getPageToRouteExistingOrderTo(toRoute);

                // Assert
                expect(result).toBe(fmgPageValues.VEHICLE);
            });

            test("Don't replace navigation to `quote` with `estimate` or `vin-lookup`", async () => {
                // Arrange
                const toRoute = {
                    query: {
                        fmgPage: fmgPageValues.QUOTE,
                    },
                };

                store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false);
                store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555");

                // Act
                const result = await getPageToRouteExistingOrderTo(toRoute);

                // Assert
                expect(result).toBe(fmgPageValues.QUOTE);
            });

            test("Don't replace navigation to `insurance-company` with `estimate` or `vin-lookup`", async () => {
                // Arrange
                const toRoute = {
                    query: {
                        fmgPage: fmgPageValues.INSURANCE_COMPANY,
                    },
                };

                store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false);
                store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "55555");

                // Act
                const result = await getPageToRouteExistingOrderTo(toRoute);

                // Assert
                expect(result).toBe(fmgPageValues.INSURANCE_COMPANY);
            });
        });
    });
});

describe("navigateToHeritageFunnel", () => {
    test("should save session", async () => {
        // Arrange
        const mockReferralNumber = "2";
        const mockCorrelationId = "55";
        const mockReferralDate = "2022";
        const mockParentAccountNumber = "167132";
        const mockSavedSessionId = "xxx-xxx-xxx";
        const mockCrmCustomerId = "xxx-xxx-xxx";

        const mockOrderInfo = getMockOrderInfo(
            mockReferralNumber,
            mockCorrelationId,
            mockReferralDate,
            mockParentAccountNumber,
            mockSavedSessionId,
            mockCrmCustomerId
        );

        const mockData = {
            actionList: [
                {
                    actionName: storeActions.SAVE_SESSION,
                    data: mockOrderInfo,
                },
            ],
        };

        setupMocksForJsFiles(mockData);
        const saveSessionFunction = jest.spyOn(orderHelper, "saveSession");
        router.navigateToExternalUrl = jest.fn();

        // Act
        await navigateToHeritageFunnel({ shouldSaveSession: true });

        // Assert
        expect(saveSessionFunction).toHaveBeenCalled();

        // Should save before we navigate to heritage by default
        const saveSessionFunctionCallOrder = saveSessionFunction.mock.invocationCallOrder[0];
        const routerNavigateFunctionCallOrder =
            router.navigateToExternalUrl.mock.invocationCallOrder[0];
        expect(saveSessionFunctionCallOrder).toBeLessThan(routerNavigateFunctionCallOrder);
        saveSessionFunction.mockRestore();
    });

    test("should go to heritage funnel", async () => {
        // Arrange
        const mockReferralNumber = "2";
        const mockCorrelationId = "55";
        const mockReferralDate = "2022";
        const mockParentAccountNumber = "167132";
        const mockSavedSessionId = "xxx-xxx-xxx";
        const mockCrmCustomerId = "xxx-xxx-xxx";

        const mockOrderInfo = getMockOrderInfo(
            mockReferralNumber,
            mockCorrelationId,
            mockReferralDate,
            mockParentAccountNumber,
            mockSavedSessionId,
            mockCrmCustomerId
        );

        const mockData = {
            actionList: [
                {
                    actionName: storeActions.SAVE_SESSION,
                    data: mockOrderInfo,
                },
            ],
        };

        setupMocksForJsFiles(mockData);

        store.getters.order.referralCorrelationId = mockCorrelationId;

        router.navigateToExternalUrl = jest.fn();

        // Act
        await navigateToHeritageFunnel({});

        // Assert
        expect(router.navigateToExternalUrl).toHaveBeenCalled();
        expect(router.navigateToExternalUrl).toHaveBeenCalledWith(
            externalUrls.HERITAGE_FUNNEL,
            expect.objectContaining({
                corid: mockCorrelationId,
            })
        );
    });

    test("shouldSaveSession is false => should not save session, but should still navigate", async () => {
        // Arrange
        const mockReferralNumber = "2";
        const mockCorrelationId = "55";
        const mockReferralDate = "2022";

        const mockOrderInfo = getMockOrderInfo(
            mockReferralNumber,
            mockCorrelationId,
            mockReferralDate
        );

        const mockData = {
            actionList: [
                {
                    actionName: storeActions.SAVE_SESSION,
                    data: mockOrderInfo,
                },
            ],
        };

        setupMocksForJsFiles(mockData);
        const saveSessionFunction = jest.spyOn(orderHelper, "saveSession");
        router.navigateToExternalUrl = jest.fn();

        // Act
        await navigateToHeritageFunnel({ shouldSaveSession: false });

        // Assert
        expect(saveSessionFunction).not.toHaveBeenCalled();
        expect(router.navigateToExternalUrl).toHaveBeenCalled();
        saveSessionFunction.mockRestore();
    });
});

/**
 * `arePagePrerequisitesValidObject` is an object where the keys are fmgPageValue names and the values are booleans that indicate
 * whether arePagePrerequisitesValid is true or false
 */
function mockLazyLoadComponentReturnValues(arePagePrerequisitesValidObject = {}) {
    lazyLoadComponent.mockImplementation((pageName) => {
        return async () => {
            return Promise.resolve({
                default: {
                    methods: {
                        arePagePrerequisitesValid: jest
                            .fn()
                            .mockReturnValueOnce(arePagePrerequisitesValidObject[pageName]),
                    },
                },
            });
        };
    });
}
