// Components
import serviceZip from "@/layouts/service-zip/service-zip";

// Supporting Files
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper";
import baseMixin from "@/mixins/base-mixin";
import store from "@/store";
import router from "@/router";
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { experimentSettings } from "../../constants/experiments";

// Constants

// Setup global mocks
let mockCmsContent = {
    AlertNonServiceableZipWidget: {
        HeadlineText: "Test",
    },
};

let mockStoreActionData = {};

let mockStoreData = {};
let mockExperimentSettings = {
    experiments: [
        {
            universeName: "ConceptFunnel",
            settings: {
                SuppressVinCapture: false,
            },
        },
    ],
};

function resetMockStoreData() {
    mockStoreData = {
        vehicle: {
            year: "2000",
            make: "TestMake",
            model: "TestModel",
            style: "TestStyle",
            carId: "TestID",
            vin: null,
            registration: {
                licensePlate: null,
            },
            isServiceable: true,
        },
        serviceLocation: {
            address: null,
            address2: null,
            city: null,
            state: null,
            zipCode: null,
            zipCodeCtu: null,
            appointmentType: null,
            isVehicleProtected: null,
            provider: {
                providerNumber: null,
                address: {
                    streetAddress: null,
                    city: null,
                    state: null,
                    zipCode: null,
                    zipCodeCtu: null,
                },
            },
            techNotes: null,
        },
        customer: {
            firstName: null,
            lastName: null,
            emailAddress: null,
            phoneNumber: null,
            isSmsOptIn: null,
        },
        damage: {
            isRepair: false,
            numberOfChips: null,
            glassToReplace: [{ glassLocation: "Windshield", glassName: "windshield" }],
            partQuestionAnswers: null,
            moldingQuestionAnswers: null,
            capabilityQuestionAnswers: null,
            dateOfLoss: null,
            damageCause: null,
        },
        lineItems: {
            glassParts: [
                {
                    canSafeliteRecalibrate: true,
                    childParts: [
                        {
                            kitPrice: 0,
                            laborAmount: 23.55,
                            partNumber: "GGG FW4896",
                            salesTax: 1.77,
                            sellingPrice: 0,
                        },
                    ],
                    color: "Green Tint",
                    description:
                        "solar, soundproofing, lane keep assist, lane departure warning system, w/adaptive cruise control",
                    id: "db22fd44-10dd-456f-979b-ff88cf68cca6",
                    kitPrice: 0,
                    laborAmount: 60,
                    partNumber: "FW04896GTYN",
                    partType: "WINDSHIELD",
                    recalibrationType: "STATIC",
                    requiresCapabilityQuestions: false,
                    requiresRecalibration: true,
                    salesTax: 63.86,
                    sellingPrice: 791.46,
                },
            ],
            supportingItems: null,
            vaps: null,
            serverData: null,
            promos: null,
        },
        payment: {
            isInsurance: null,
            insuranceCoverage: {
                isVerified: null,
                coverageStatus: null,
                coverageType: null,
                coverageVerificationType: null,
            },
            parentAccountNumber: 0,
            billToAccountNumber: null,
            isPia: null,
            piaType: null,
            inactivePromos: null,
            paypalToken: null,
            nextGenSettledAmount: 0,
            ccToken: {
                subscriptionId: null,
                expMonth: null,
                expYear: null,
                cardType: null,
                billToPostalCode: null,
                billToFirstName: null,
                billToLastName: null,
                referenceNumber: null,
                authCode: null,
                transactionId: null,
                transReferenceNumber: null,
                lastFour: null,
            },
        },
        policy: {
            currentDeductible: 0,
            policyNumber: null,
            isItac: false,
            additionalAuthFlag: null,
            isNoComp: false,
            insuranceCompanyName: null,
        },
        schedule: {
            date: null,
            startTime: null,
            endTime: null,
            routeCode: null,
            jobMaxMinutes: null,
            jobMinMinutes: null,
        },
        externalParameterServiceZip: {
            zipCode: null,
            emailAddress: null,
        },
        externalParameterState: { isExternalParameter: false },
    };
}

const applicationUser = {
    experiments: [],
};

function applyMockStoreDataToGetters() {
    store.getters = {
        experimentSettings: mockExperimentSettings,
        applicationUser: applicationUser,
        order: mockStoreData,
        damage: mockStoreData.damage,
        payment: mockStoreData.payment,
        policy: mockStoreData.policy,
        externalParameterServiceZip: mockStoreData.externalParameterServiceZip,
        emailOrSms: mockStoreData.customer.emailAddress,
        vehicle: mockStoreData.vehicle,
    };
    store.state.order = mockStoreData;
    store.state.applicationUser.experiments = mockExperimentSettings;
}

async function mockDispatchStoreAction(actionName) {
    return mockStoreActionData[actionName];
}

jest.mock("@/mixins/base-mixin.js", () => ({
    methods: {
        dispatchStoreAction: jest.fn().mockImplementation(mockDispatchStoreAction),
        dispatchStoreActionWithLogging: jest.fn().mockImplementation(mockDispatchStoreAction),
    },
}));

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

jest.mock("@/helpers/heritage-integration/order-helper.js", () => ({
    saveSession: jest.fn(),
}));

router.navigateWithoutSaving = jest.fn();
router.navigateWithSaving = jest.fn();

// Tests
describe("service-zip.vue", () => {
    beforeEach(() => {
        resetMockStoreData();
        jest.clearAllMocks();
    });

    describe("Pre-existing fields", () => {
        test("No existing fields -> serviceZip & emailAdress undefined", () => {
            // Arrange
            // no changes to store
            applyMockStoreDataToGetters();

            // Act
            const wrapper = setupMocks({});

            // Assert
            expect(wrapper.vm.serviceZipCode).toBeFalsy();
            expect(wrapper.vm.emailAddress).toBeFalsy();
        });

        test("Fields in store -> autofilled to data", () => {
            // Arrange
            mockStoreData.serviceLocation.zipCode = "11111";
            mockStoreData.customer.emailAddress = "builddigitaltest@safelite.com";

            applyMockStoreDataToGetters();

            // Act
            const wrapper = setupMocks({});

            // Assert
            expect(wrapper.vm.serviceZipCode).toEqual("11111");
            expect(wrapper.vm.emailOrSms).toEqual("builddigitaltest@safelite.com");
        });

        test("Zip in querystring -> pushed to data", () => {
            // Arrange
            // no changes to store
            applyMockStoreDataToGetters();

            // Act
            const wrapper = setupMocks({
                customZipQuery: "11111",
            });

            // Assert
            expect(wrapper.vm.serviceZipCode).toEqual("11111");
        });
    });

    describe("Navigation", () => {
        describe("Back Nav", () => {
            test("If skipvin eligable, go back to vehicle-damage", async () => {
                // Arrange
                mockStoreData.damage.isRepair = true;
                mockStoreData.damage.numberOfChips = 1;
                mockStoreData.damage.glassToReplace = null;
                mockStoreData.lineItems.glassParts = null;

                applyMockStoreDataToGetters();

                // Act
                const wrapper = setupMocks({});
                await wrapper.vm.backButtonAction();

                // Assert
                expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith(
                    navigationScenarios.CLICKED_BACK_WITH_SKIP_VIN,
                    wrapper.vm.$route
                );
            });

            test("If not skipvin eligable, go back to estimate", async () => {
                // Arrange
                // no changes to store
                applyMockStoreDataToGetters();

                // Act
                const wrapper = setupMocks({});
                await wrapper.vm.backButtonAction();

                // Assert
                expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith(
                    navigationScenarios.CLICKED_BACK,
                    wrapper.vm.$route
                );
            });
        });

        describe("Forward Nav", () => {
            test("Repair skips questions flow", async () => {
                // Arrange
                mockStoreData.damage.isRepair = true;
                mockStoreData.damage.numberOfChips = 1;
                mockStoreData.damage.glassToReplace = null;
                mockStoreData.lineItems.glassParts = null;

                applyMockStoreDataToGetters();

                // Act
                const wrapper = setupMocks({});
                wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
                jest.spyOn(wrapper.vm, "navigateForwardWithSingleCarMatch").mockImplementation();

                await wrapper.vm.forwardButtonAction();

                // Assert
                expect(wrapper.vm.$router.navigateWithSaving).toBeCalledWith(
                    navigationScenarios.CLICKED_FORWARD_WITH_NO_QUESTIONS,
                    wrapper.vm.$route
                );
                expect(wrapper.vm.navigateForwardWithSingleCarMatch).not.toBeCalled();
            });

            test("Non-repair enters questions flow", async () => {
                // Arrange
                mockStoreData.damage.isRepair = false;
                applyMockStoreDataToGetters();

                // Act
                const wrapper = setupMocks({});
                wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
                jest.spyOn(wrapper.vm, "navigateForwardWithSingleCarMatch").mockImplementation();

                await wrapper.vm.forwardButtonAction();

                // Assert
                expect(wrapper.vm.$router.navigateWithSaving).not.toBeCalled();
                expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalled();
            });
        });
    });

    describe("Alerts", () => {
        test("Invalid zip alert shown if indicated by endpoint", async () => {
            // Arrange
            // no changes to store
            applyMockStoreDataToGetters();
            const invalidZipResponse = {
                state: "OH",
                zipCodeCtu: "TESTCTU",
                isValid: false,
                isServiceable: true,
            };

            // Act
            const wrapper = setupMocks({ customZipDataResponse: invalidZipResponse });

            wrapper.vm.$refs.navbar.removeLoader = jest.fn();

            await wrapper.vm.forwardButtonAction();

            // Assert
            expect(wrapper.vm.displayInvalidZipAlert).toBe(true);
        });

        test("Non-serviceable zip alert shown if indicated by endpoint", async () => {
            // Arrange
            // no changes to store
            applyMockStoreDataToGetters();
            const invalidZipResponse = {
                state: "OH",
                zipCodeCtu: "TESTCTU",
                isValid: true,
                isServiceable: false,
            };

            // Act
            const wrapper = setupMocks({ customZipDataResponse: invalidZipResponse });

            wrapper.vm.$refs.navbar.removeLoader = jest.fn();

            await wrapper.vm.forwardButtonAction();

            // Assert
            expect(wrapper.vm.displayNonServiceableZipAlert).toBe(true);
        });

        test("Alerts cleared when valid zip is submitted", async () => {
            // Arrange
            // no changes to store
            applyMockStoreDataToGetters();

            // Act
            const wrapper = setupMocks({});

            wrapper.vm.displayInvalidZipAlert = true;
            wrapper.vm.displayNonServiceableZipAlert = true;

            wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
            jest.spyOn(wrapper.vm, "navigateForwardWithSingleCarMatch").mockImplementation();

            await wrapper.vm.forwardButtonAction();

            // Assert
            expect(wrapper.vm.displayInvalidZipAlert).toBe(false);
            expect(wrapper.vm.displayNonServiceableZipAlert).toBe(false);
        });
    });
});

describe("service-zip.vue", () => {
    test("should call forwardButtonAction if isExternalParameter is true and form is valid", async () => {
        // Set up the store with isExternalParameter as true
        store.getters.externalParameterState = { isExternalParameter: true };

        // Set up the component
        const wrapper = setupMocks({});
        wrapper.vm.forwardButtonAction = jest.fn();
        const nextFunction = jest.fn((c) => {
            c(wrapper.vm);
        });

        // Mock the isFormValid method
        baseMixin.methods.isFormValid = jest.fn().mockReturnValue(true);

        // Call the method that contains the if-else logic
        await serviceZip.beforeRouteEnter.call(
            wrapper.vm,
            { query: { fmgPage: "service-zip" } },
            undefined,
            nextFunction
        );
        expect(nextFunction).toHaveBeenCalled();
        expect(wrapper.vm.forwardButtonAction).toHaveBeenCalled();
    });
    test("should not call forwardButtonAction if isExternalParameter is true and form is invalid", async () => {
        // Set up the store with isExternalParameter as true
        store.getters.externalParameterState = { isExternalParameter: true };

        // Set up the component
        const wrapper = setupMocks({});
        wrapper.vm.forwardButtonAction = jest.fn();
        const nextFunction = jest.fn((c) => {
            c(wrapper.vm);
        });

        // Mock the isFormValid method
        baseMixin.methods.isFormValid = jest.fn().mockImplementation(() => {
            return false;
        });

        // Call the method that contains the if-else logic
        await serviceZip.beforeRouteEnter.call(
            wrapper.vm,
            { query: { fmgPage: "service-zip" } },
            undefined,
            nextFunction
        );
        expect(wrapper.vm.forwardButtonAction).not.toHaveBeenCalled();
    });
});

function setupMocks({ customMountOptions, customZipQuery, customZipDataResponse }) {
    const route = { query: { fmgPage: "service-zip" }, params: {} };
    if (customZipQuery) {
        route.query.zipcode = customZipQuery;
    }
    baseMixin.methods.ResetExternalParamsAndHideModal = jest.fn();

    const mountOptions = getMountOptions({
        ...customMountOptions,
        route: route,
    });

    mountOptions.global.mocks["$store"] = store;
    mountOptions.global.mocks["$router"] = router;
    mountOptions.mixins = [
        {
            methods: {
                getCmsContent: jest.fn().mockImplementation((widgetName, fieldName) => {
                    if (mockCmsContent[widgetName] && mockCmsContent[widgetName][fieldName])
                        return mockCmsContent[widgetName][fieldName];
                }),
                setCmsContent: jest.fn(),
                navigateForwardWithSingleCarMatch: jest.fn(),
                getAndSaveBillToAccountNumber: jest.fn(),
                getZipCodeData: jest.fn().mockImplementation(() => {
                    if (customZipDataResponse) {
                        return customZipDataResponse;
                    } else {
                        return {
                            state: "OH",
                            zipCodeCtu: "TESTCTU",
                            isValid: true,
                            isServiceable: true,
                        };
                    }
                }),
            },
        },
    ];

    const wrapper = shallowMount(serviceZip, mountOptions);
    return wrapper;
}
