// Components
import payment from "@/layouts/payment/payment";

// Supporting Files
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper";
import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import { paymentMethods } from "@/constants/payment-method-constants";

jest.mock("@/helpers/pricing-helper.js", () => ({
    getAmountDue: jest.fn(),
    getDisplayAmountDue: jest.fn(),
}));

// Constants
const parts = {
    windshield: {
        name: "windshield",
        canSafeliteRecalibrate: true,
        childParts: [],
        color: "Green Tint",
        description:
            "solar, soundproofing, lane keep assist, lane departure warning system, w/adaptive cruise control",
        id: "db22fd44-10dd-456f-979b-ff88cf68cca6",
        partNumber: "FW04896GTYN",
        partType: "WINDSHIELD",
        recalibrationType: "STATIC",
        requiresCapabilityQuestions: false,
        requiresRecalibration: true,
        salesTax: 63.86,
        sellingPrice: 791.46,
    },
    frontWipers: {
        name: "front wipers",
        partNumber: "SBB16",
        description: "SAFELITE BEAM BLADE 16",
        partType: "FRONT WIPER",
        price: 32.64,
    },
    rearWipers: {
        name: "rear wipers",
        partNumber: "SBBR12A",
        description: "SAFELITE REAR BLADE 12A",
        partType: "REAR WIPER",
        price: 24.48,
    },
    rainDefense: {
        name: "rain defense",
        partNumber: "RAIN DEFENSE",
        description: null,
        partType: "RAIN DEFENSE",
        price: 35.5,
    },
};

const pricedParts = {
    windshield: {
        ...parts.windshield,
        kitPrice: 0,
        laborAmount: 60,
        salesTax: 0,
        sellingPrice: 791.46,
    },
    frontWipers: {
        ...parts.frontWipers,
        kitPrice: 0,
        laborAmount: 20,
        salesTax: 0,
        sellingPrice: 32.64,
    },
    rearWipers: {
        ...parts.rearWipers,
        kitPrice: 10,
        laborAmount: 10,
        salesTax: 0,
        sellingPrice: 24.48,
    },
    rainDefense: {
        ...parts.rainDefense,
        kitPrice: 20,
        laborAmount: 0,
        salesTax: 0,
        sellingPrice: 35.5,
    },
};

const taxedParts = {
    windshield: {
        ...pricedParts.windshield,
        salesTax: 63.86,
        subTotal: 100,
    },
    frontWipers: {
        ...pricedParts.frontWipers,
        salesTax: 2.36,
        subTotal: 100,
    },
    rearWipers: {
        ...pricedParts.rearWipers,
        salesTax: 6.0,
        subTotal: 100,
    },
    rainDefense: {
        ...pricedParts.rainDefense,
        salesTax: 1.0,
        subTotal: 100,
    },
};

// Setup global mocks
let mockCmsContent = {};

let mockStoreActionData = {};

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

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

jest.mock("@/helpers/promotions-helper", () => ({
    revalidatePromosAndValidateQueryStringPromo: async () => ({}),
    getVapsThatNeedToBeAddedToSatisfyPromos: () => [],
}));

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

describe("payment.vue", () => {
    beforeEach(() => {
        store.getters = {
            order: {
                vehicle: {
                    year: "2020",
                    make: "acura",
                    model: "mdx",
                    style: "4-door sedan",
                    carId: "dummyCarId",
                    category: "dummyCategory",
                    vin: "dummyVin",
                },
                serviceLocation: {
                    address: "add1",
                    address2: "add2",
                    city: "city",
                    state: "state",
                    zipCode: "zip",
                    zipCodeCtu: "zipCtu",
                    appointmentType: "IN_SHOP",
                    isVehicleProtected: true,
                    provider: {
                        providerNumber: 2,
                        address: {
                            streetAddress: "add3",
                            city: "city2",
                            state: "state2",
                            zipCode: "zip2",
                            zipCodeCtu: "zipCtu2",
                        },
                    },
                    techNotes: "",
                },
                customer: {
                    firstName: "first",
                    lastName: "last",
                    emailAddress: "builddigitaltest@safelite.com",
                    phoneNumber: "555-555-5555",
                    isSmsOptIn: false,
                },
                damage: {
                    isRepair: false,
                    numberOfChips: null,
                    glassToReplace: [{ location: "windshield" }],
                },
                lineItems: {
                    glassParts: [parts.windshield],
                    supportingItems: [],
                    vaps: [parts.frontWipers],
                    promos: [],
                },
                payment: {
                    isInsurance: false,
                    insuranceCoverage: {
                        isVerified: null,
                        coverageStatus: null,
                        coverageVerificationType: null,
                    },
                    isPia: true,
                    piaType: "Afterpay",
                    inactivePromos: [],
                },
                schedule: {
                    date: "date",
                    startTime: "start",
                    endTime: "end",
                    jobMinMinutes: "30",
                    jobMaxMinutes: "45",
                },
            },
            applicationUser: {
                loggingOption: false,
            },
            policy: {
                currentDeductible: 1,
            },
            get payment() {
                return this.order.payment;
            },
            get vehicle() {
                return this.order.vehicle;
            },
        };

        mockCmsContent = {};

        mockStoreActionData = {
            [storeActions.GET_SIGNATURE]: {
                token: "token",
                signature: "signature",
                startDate: "startDate",
            },
            [storeActions.GET_WIPERS]: [parts.frontWipers, parts.rearWipers],
            [storeActions.GET_RAIN_DEFENSE]: parts.rainDefense,
            [storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA]: [
                pricedParts.windshield,
                pricedParts.frontWipers,
            ],
            [storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA]: [
                taxedParts.windshield,
                taxedParts.frontWipers,
            ],
        };
    });

    afterEach(() => {
        jest.clearAllMocks();
    });

    describe("arePagePrerequisitesValid", () => {
        test("Returns true in nominal conditions", () => {
            // Arrange
            // Store defaults are valid
            const wrapper = setupMocks({});

            // Act
            const result = wrapper.vm.arePagePrerequisitesValid();

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

        describe("Payment method cases", () => {
            test("Returns false if pia options are not valid", () => {
                // Arrange
                store.getters.order.payment.isPia = null;
                store.getters.order.payment.piaType = null;
                const wrapper = setupMocks({});

                // Act
                const result = wrapper.vm.arePagePrerequisitesValid();

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

            test("Returns false if pay at time of service", () => {
                // Arrange
                store.getters.order.payment.isPia = false;
                store.getters.order.payment.piaType = null;
                const wrapper = setupMocks({});

                // Act
                const result = wrapper.vm.arePagePrerequisitesValid();

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

    describe("beforeRouteEnter", () => {
        test("Properly initializes fields and kicks off hop", async () => {
            // Arrange
            const vmMock = {
                setCmsContent: jest.fn(),
                $refs: {
                    cart: {
                        cartItems: [],
                    },
                },
                getPiaLineItems: jest.fn(),
                fetchSignatureInfo: jest.fn(),
                setIFrameListener: jest.fn(),

                $nextTick: (f) => {
                    f();
                },
            };

            const nextF = (f) => {
                f(vmMock);
            };

            // Act
            await payment.beforeRouteEnter.call(
                vmMock,
                { query: { fmgPage: "payment" } },
                undefined,
                nextF
            );

            // Assert
            expect(vmMock.availableVaps).not.toBeUndefined();
            expect(vmMock.lineItems).not.toBeUndefined();

            expect(vmMock.setCmsContent).toBeCalled();
            expect(vmMock.getPiaLineItems).toBeCalled();
            expect(vmMock.fetchSignatureInfo).toBeCalled();
            expect(vmMock.setIFrameListener).toBeCalled();
        });
    });

    describe("payment type mapping", () => {
        describe("getPaymentType", () => {
            test("Maps AFTERPAY -> ap", () => {
                // Arrange
                store.getters.order.payment.piaType = paymentMethods.AFTERPAY;
                const wrapper = setupMocks({});

                // Act
                const mapped = wrapper.vm.getPaymentType();

                // Assert
                expect(mapped).toBe("ap");
            });

            test("Maps CREDIT_CARD -> cc", () => {
                // Arrange
                store.getters.order.payment.piaType = paymentMethods.CREDIT_CARD;
                const wrapper = setupMocks({});

                // Act
                const mapped = wrapper.vm.getPaymentType();

                // Assert
                expect(mapped).toBe("cc");
            });

            test("Maps PAYPAL -> pp", () => {
                // Arrange
                store.getters.order.payment.piaType = paymentMethods.PAYPAL;
                const wrapper = setupMocks({});

                // Act
                const mapped = wrapper.vm.getPaymentType();

                // Assert
                expect(mapped).toBe("pp");
            });

            test("Maps other values to self", () => {
                // Arrange
                store.getters.order.payment.piaType = "SomeOtherText";
                const wrapper = setupMocks({});

                // Act
                const mapped = wrapper.vm.getPaymentType();

                // Assert
                expect(mapped).toBe("SomeOtherText");
            });
        });

        describe("isPaypal", () => {
            test("Is responsive if initial data changes", async () => {
                // Arrange
                // note: begins with payment-type = afterpay
                const wrapper = setupMocks({});

                // Act
                const preVal = wrapper.vm.isPaypal;
                wrapper.vm.paymentType = "pp";
                await wrapper.vm.$nextTick();

                const postVal = wrapper.vm.isPaypal;

                // Assert
                expect(preVal).toBe(false);
                expect(postVal).toBe(true);
            });
        });
    });

    describe("getPiaLineItems", () => {
        test("Properly formatted output", () => {
            // Arrange
            const wrapper = setupMocks({});
            const cartItems = [
                taxedParts.windshield,
                taxedParts.frontWipers,
                taxedParts.rainDefense,
            ];

            // Act
            wrapper.vm.getPiaLineItems(cartItems);
            const result = wrapper.vm.piaLineItems;

            // Assert
            expect(result).toMatch(/^[\w ]+\|[\d]+(.\d\d)?\|1(\|\|[\w ]+\|[\d]+(.\d\d)?\|1)*$/);
        });

        test("Includes repair supply for non-glass orders", () => {
            // Arrange
            store.getters.order.lineItems.glassParts = null;

            const wrapper = setupMocks({});
            const cartItems = [taxedParts.frontWipers, taxedParts.rainDefense];

            // Act
            wrapper.vm.getPiaLineItems(cartItems);
            const result = wrapper.vm.piaLineItems;

            // Assert
            expect(result).toMatch(/Repair supplies/);
        });

        test("Does not include promos", () => {
            // Arrange
            store.getters.order.lineItems.promos = [
                {
                    name: "PROMO",
                    salesTax: 10,
                    subTotal: 20,
                },
            ];

            const wrapper = setupMocks({});
            const cartItems = [
                taxedParts.windshield,
                taxedParts.frontWipers,
                taxedParts.rainDefense,
            ];

            // Act
            wrapper.vm.getPiaLineItems(cartItems);
            const result = wrapper.vm.piaLineItems;

            // Assert
            expect(result).not.toMatch(/PROMO/);
        });
    });

    describe("hop form", () => {
        describe("fetchSignatureInfo", () => {
            test("Sets auth fields", async () => {
                // Arrange
                const signature = {
                    token: "TOKEN",
                    signature: "SIGNATURE",
                    startDate: "STARTDATE",
                };
                const wrapper = setupMocks({});

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

                // Act
                await wrapper.vm.fetchSignatureInfo(signature);

                // Assert
                expect(wrapper.vm.authToken).toBe(signature.token);
                expect(wrapper.vm.authSignature).toBe(signature.signature);
                expect(wrapper.vm.authSignatureStart).toBe(signature.startDate);

                expect(wrapper.vm.submitHopForm).toBeCalled();
            });

            test("If paypal, show the loading modal", async () => {
                // Arrange
                const signature = {
                    token: "TOKEN",
                    signature: "SIGNATURE",
                    startDate: "STARTDATE",
                };
                store.getters.order.payment.piaType = "Paypal";
                const wrapper = setupMocks({});

                wrapper.vm.submitHopForm = jest.fn();
                wrapper.vm.$refs.loadingModal.showModal = jest.fn();

                // Act
                await wrapper.vm.fetchSignatureInfo(signature);

                // Assert
                expect(wrapper.vm.$refs.loadingModal.showModal).toBeCalled();
            });
        });

        describe("submitHopForm", () => {
            test("Submits form", async () => {
                // Arrange
                const wrapper = setupMocks({});

                wrapper.vm.$refs.hopForm.submit = jest.fn();
                wrapper.vm.$refs.paymentFrame = null;

                // Act
                wrapper.vm.submitHopForm();

                await wrapper.vm.$nextTick();

                // Assert
                expect(wrapper.vm.$refs.hopForm.submit).toBeCalled();
            });
        });

        describe("handleIFrameContentWindowMessage", () => {
            test("Navigates back if afterpay is closed", () => {
                // Arrange
                const event = {
                    data: "afterpayClosed",
                };

                const wrapper = setupMocks({});

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

                // Act
                wrapper.vm.handleIFrameContentWindowMessage(event);

                // Assert
                expect(wrapper.vm.backButtonAction).toBeCalled();
            });

            test("Blocks UI interaction if credit card is submitted", () => {
                // Arrange
                const event = {
                    data: "creditCardSubmit",
                };

                const wrapper = setupMocks({});

                // Act
                wrapper.vm.handleIFrameContentWindowMessage(event);

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

    describe("UI Blocking", () => {
        test("UI block appears when toggled", async () => {
            // Arrange
            const wrapper = setupMocks({});

            // Act
            wrapper.vm.setUIBlock(true);

            await wrapper.vm.$nextTick();

            const uiBlockElements = wrapper.findAll(".ui-block");

            // Assert
            expect(uiBlockElements.length).toBeGreaterThan(0);
        });

        test("Don't propogate clicks from UI block", async () => {
            // Arrange
            const wrapper = setupMocks({});

            const outerDiv = wrapper.find(".container-fluid");
            const clickFn = jest.fn();
            outerDiv.element.addEventListener("click", clickFn);

            // Act
            wrapper.vm.setUIBlock(true);

            await wrapper.vm.$nextTick();

            const uiBlockElement = wrapper.find(".ui-block");

            await uiBlockElement.trigger("click");

            // Assert
            expect(clickFn).not.toBeCalled();
        });
    });
});

function setupMocks({ customMountOptions }) {
    const mountOptions = getMountOptions({
        ...customMountOptions,
        route: { query: { fmgPage: "page-name" }, params: {} },
    });

    // set all mock stuff
    mountOptions.global.mocks["$store"] = store;
    mountOptions.mixins = [
        {
            methods: {
                getCmsContent: jest.fn().mockImplementation((widgetName, fieldName) => {
                    if (mockCmsContent[widgetName] && mockCmsContent[widgetName][fieldName])
                        return mockCmsContent[widgetName][fieldName];
                }),
                setCmsContent: jest.fn(),
            },
        },
    ];

    const wrapper = shallowMount(payment, mountOptions);

    // act on vm

    return wrapper;
}
