import { mount, shallowMount } from "@vue/test-utils";
import promoModalQuestion from "./promo-modal-question";
import { storeActions } from "@/constants/store-actions";
import baseMixin from "@/mixins/base-mixin.js";
import {
    promoErrorCodes,
    getPromoCodesFromPromoObjectsWithoutDuplicates,
    getPromoCodeWithoutBundleIdentifier,
} from "@/helpers/promotions-helper";
import { cartItemCategories } from "@/constants/cart-item-categories";

let mockReturnsForStoreActions = {};

jest.mock("@/mixins/base-mixin", () => ({
    ...jest.requireActual("@/mixins/base-mixin"),
    methods: {
        dispatchStoreActionWithLogging: jest.fn(
            (action, { promoCode, addableVaps }, pageNameToLog, someBool) => {
                return mockReturnsForStoreActions[action];
            }
        ),
        dispatchStoreAction: jest.fn(
            (action, { promoCode, addableVaps }, pageNameToLog, someBool) => {
                return mockReturnsForStoreActions[action];
            }
        ),
    },
}));

afterEach(() => {
    // reset store action returns
    mockReturnsForStoreActions = {};
});

jest.mock("@/digital-components/textbox-question/textbox-question", () => ({
    getCmsContent: jest.fn((widgetName, cmsFieldName) => {
        return widgetName[cmsFieldName];
    }),
}));

jest.mock("@/digital-components/modal/modal", () => ({
    methods: {
        closeModal: jest.fn(),
        resetButtonStyle: jest.fn(),
        resetForm: jest.fn(),
    },
}));

const modalWidgetName = "modalWidgetName";

const mockModalCmsContent = {
    FooterText: "Sample modal footer text here.",
};

const mockMixin = {
    methods: {
        getCmsContent: jest.fn((widgetName, cmsFieldName) => {
            if (widgetName === modalWidgetName) {
                return mockModalCmsContent[cmsFieldName];
            }

            return null;
        }),
    },
};

describe("promo-modal-question.vue", () => {
    it("Should reset all alerts on onModalClosed", async () => {
        // Arrange

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });

        // Act
        wrapper.vm.onModalClosed();

        // Assert
        expect(wrapper.vm.displayInvalidPromoAlert).toBe(false);
        expect(wrapper.vm.displayStackingPromoAlert).toBe(false);
        expect(wrapper.vm.displayInShopPromoAlert).toBe(false);
        expect(wrapper.vm.displaySimilarPromoAlert).toBe(false);
    });
    it("Should emit update:modelValue on Modal closed", async () => {
        // Arrange
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });

        // Act

        await wrapper.vm.onModalClosed();

        // Assert
        expect(wrapper.emitted("update:modelValue")).toEqual([[lineItems]]);
    });
    it("focuses on the input element when focusOnPromoInput is called", async () => {
        // Arrange
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });
        const focusMock = jest.fn();
        const inputMock = { focus: focusMock };

        // Mock document.getElementById to return the inputMock
        jest.spyOn(document, "getElementById").mockReturnValue(inputMock);

        //Act

        wrapper.vm.focusOnPromoInput();
        await wrapper.vm.$nextTick();

        //Assert
        expect(focusMock).toHaveBeenCalled();
    });
    it("returns a validate response on applying promo", async () => {
        // Arrange
        const newPromo = "testPromo";
        const pageNameToLog = "testPage";
        const validateResponse = { orderPromos: [] };
        const addableVaps = [];
        mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] =
            validateResponse;
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });
        // Act

        wrapper.vm.getPromoCodeData();

        const promoValidationResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
            storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA,
            {
                newPromo,
                lineItems,
                addableVaps,
            },
            pageNameToLog,
            false
        );

        // Assert
        expect(promoValidationResponse).toEqual(validateResponse);
    });
    test("If promocode is valid return taxed lineItems", async () => {
        //Arrange
        const taxedlineItems = {};

        mockReturnsForStoreActions[storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA] =
            taxedlineItems;

        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        let promoCode = "1wiper0";
        const pricedLineItemsToTax = [];

        pricedLineItemsToTax.push(promoCode);
        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],

            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },

            attachTo: document.body,
        });
        wrapper.vm.addPromoCode();

        const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging(
            storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
            {
                billToAccountNumber: "87291",
                providerNumber: 2,
                appointmentType: "IN_SHOP",
                serviceLocationCity: "city",
                serviceLocationState: "state",
                serviceLocationZipCode: "12345",
                pricedLineItems: pricedLineItemsToTax,
            },

            "payment-method",

            false
        );

        // Assert

        expect(taxedLineItems).toEqual(taxedlineItems);
    });
    test("Get promoCode list will return the applied promos", async () => {
        // Arrange
        const promos = [
            { promoCode: "duplicatePromo" },
            { promoCode: "duplicatePromo" },
            { promoCode: "bundlePromo/400" },
            { promoCode: "bundlePromo/401" },
        ];
        const appliedPromos = ["duplicatePromo", "bundlePromo"];
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });

        // Act
        wrapper.vm.getPromoCodeList();
        const promoCodeToDisplay = getPromoCodesFromPromoObjectsWithoutDuplicates(promos);

        // Assert
        expect(promoCodeToDisplay).toEqual(appliedPromos);
    });
    test("On clicking remove the promo gets removed from the lineItems", async () => {
        //Arrange
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [{ promoCode: "duplicatePromo" }, { promoCode: "duplicatePromo" }],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });
        const promo = ["bundlePromo"];
        //Act
        wrapper.vm.removeItem(promo);
        const existingPromo = (lineItems[cartItemCategories.PROMOS] = lineItems[
            cartItemCategories.PROMOS
        ].filter(
            (lineItemsToKeep) =>
                getPromoCodeWithoutBundleIdentifier(lineItemsToKeep.promoCode) != promo
        ));
        await baseMixin.methods.dispatchStoreAction(
            storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
            {
                activePromos: lineItems.promos,
            },
            false
        );

        //Assert
        expect(existingPromo).toEqual(lineItems.promos);
    });
    test("Getting stacking alert on stack promo error code", async () => {
        // Arrange
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });
        const additionalInfo = ["testAdditionalInfo"];

        const stackingErrorCode = promoErrorCodes.PROMO_STACKING_NOT_ALLOWED;

        // Act
        wrapper.vm.getErrorMessage(stackingErrorCode, additionalInfo);

        // Assert
        expect(wrapper.vm.displayStackingPromoAlert).toBe(true);
    });
    test("Getting invalid promo alert on invalid promoCode", async () => {
        // Arrange
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });
        const additionalInfo = ["testAdditionalInfo"];

        const invalidErrorCode = promoErrorCodes.INVALID_PROMO_ON_ORDER;

        // Act
        wrapper.vm.getErrorMessage(invalidErrorCode, additionalInfo);

        // Assert
        expect(wrapper.vm.displayInvalidPromoAlert).toBe(true);
    });
    test("Getting invalid promo alert on invalid promoCode", async () => {
        // Arrange
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });
        const additionalInfo = ["APPOINTMENT_TYPE"];

        const invalidErrorCode = promoErrorCodes.INVALID_PROMO_ON_ORDER;

        // Act
        wrapper.vm.getErrorMessage(invalidErrorCode, additionalInfo);

        // Assert
        expect(wrapper.vm.displayInShopPromoAlert).toBe(true);
    });
    test("Get conflicting promoCodes on applying more than one promo", async () => {
        // Arrange
        const lineItems = {
            glassParts: [],
            supportingItems: [],
            vaps: [],
            promos: [],
        };

        const wrapper = mount(promoModalQuestion, {
            mixins: [mockMixin],
            props: {
                modelValue: lineItems,
                modalWidgetName: modalWidgetName,
            },
            attachTo: document.body,
        });
        const additionalInfo = ["1wiper0", "Glass30"];
        const conflictingCodes = ["1wiper0", "Glass30"];

        //Act
        wrapper.vm.getConflictingPromoCode(additionalInfo);

        //Assert
        expect(conflictingCodes).toEqual(additionalInfo);
    });
});
