// Components
import payment from '@/layouts/payment-page/payment-page.vue';

// Supporting Files
import { shallowMount } from '@vue/test-utils';
import { getEnumName, getMountOptions } from '@/helpers/unit-test-helper';
import { useMainStore } from '@/store';
import { paymentMethods } from '@/constants/payment-method-constants.js';
import queryStrings from '@/constants/query-strings';
import { createTestingPinia } from '@pinia/testing';
import settleAllPromises from '@/helpers/layout-helper';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import baseMixin from '@/mixins/base-mixin';

// 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 repel',
        partNumber: 'RAIN REPEL',
        description: null,
        partType: 'RAIN REPEL',
        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
// Mock fetchCmsContentForPage
jest.mock('@/helpers/cms-content-helper', () => ({
    fetchCmsContentForPage: jest.fn(),
    doesCopyContainRouterLink: jest.fn()
}));

// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => jest.fn());

function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRunAfterInitializingStore = () => {}, queryString = {}) {
    const mountOptions = getMountOptions({
        router: {
            navigate: jest.fn()
        },
        route: { query: { issPage: 'page-name', ...queryString }, params: {} }
    });

    const testingPinia = createTestingPinia({
        initialState: {
            main: mainInitialState
        }
    });
    useMainStore(testingPinia);
    methodToRunAfterInitializingStore();

    mountOptions.global.plugins = [testingPinia];
    mountOptions.data = () => (initialData);

    const apiResponses = { cmsContent: {} };

    settleAllPromises.mockImplementation(() => apiResponses);
    fetchCmsContentForPage.mockImplementation(() => Promise.resolve());

    const wrapper = shallowMount(payment, mountOptions);
    wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {});
    wrapper.vm.setCmsContent = jest.fn();
    wrapper.vm.$router.navigateWithSpinner = jest.fn();
    wrapper.vm.navigateBack = baseMixin.methods.navigateBack;

    return { wrapper };
}

const defaultOrder = {
    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: ''
        },
        contactInfo: {
            firstName: 'first',
            lastName: 'last',
            emailAddress: 'builddigitaltest@safelite.com',
            servicePhone: '555-555-5555'
        },
        damage: {
            isRepair: false,
            numberOfChips: null,
            glassToReplace: [{ location: 'windshield' }]
        },
        lineItems: {
            glassParts: [parts.windshield],
            supportingItems: [],
            vaps: [parts.frontWipers],
            promos: []
        },
        insuranceCoverage: {
            coverageStatus: null,
            coverageType: null,
            claimNumber: null
        },
        payment: {
            isInsurance: false,
            paymentMethod: paymentMethods.AFTERPAY,
            inactivePromos: []
        },
        schedule: {
            date: 'date',
            startTime: 'start',
            endTime: 'end',
            jobMinMinutes: '30',
            jobMaxMinutes: '45'
        }
    }
};

describe('payment-page.vue', () => {
    describe('arePagePrerequisitesValid', () => {
        test('Returns true in nominal conditions', () => {
            // Arrange
            // Store defaults are valid
            const { wrapper } = getMountedComponent(defaultOrder);

            // 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
                const { wrapper } = getMountedComponent(defaultOrder);
                useMainStore().order.payment.paymentMethod = null;

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

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

            test('Returns false if pay at time of service', () => {
                // Arrange
                const { wrapper } = getMountedComponent(defaultOrder);
                useMainStore().order.payment.paymentMethod = paymentMethods.PAY_AT_TIME_OF_SERVICE;

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

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

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

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

                const postVal = wrapper.vm.isPaypal;

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

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

                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();
            });
        });

        describe('submitHopForm', () => {
            test('Submits form', async () => {
                // Arrange
                const { wrapper } = getMountedComponent(defaultOrder);

                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 } = getMountedComponent(defaultOrder);

                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 } = getMountedComponent(defaultOrder);
                useMainStore().order.payment.paymentMethod = paymentMethods.PayNow;

                // 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 } = getMountedComponent(defaultOrder);

            // 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 } = getMountedComponent(defaultOrder);

            const outerDiv = wrapper.find('.fade-on-route-transition');
            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();
        });
    });

    describe('Credit Card Error Alert', () => {
        test('show credit card error alert', () => {
            // Arrange
            const queryString = {
                [queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]: paymentMethods.CREDIT_CARD
            };
            const { wrapper } = getMountedComponent(defaultOrder, {}, () => {}, queryString);

            // Act

            // Assert
            const creditCardErrorAlert = wrapper.find('[name="payInAdvanceCreditCardErrorAlert"]');
            expect(creditCardErrorAlert.exists()).toBeTruthy();
            expect(creditCardErrorAlert.isVisible()).toBeTruthy();
        });
    });

    describe('AfterPay Error Alert', () => {
        test('show after pay error alert', () => {
            // Arrange
            const queryString = {
                [queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]: paymentMethods.AFTERPAY
            };
            const { wrapper } = getMountedComponent(defaultOrder, {}, () => {}, queryString);

            // Act

            // Assert
            const afterPayErrorAlert = wrapper.find('[name="payInAdvanceAfterPayErrorAlert"]');
            expect(afterPayErrorAlert.exists()).toBeTruthy();
            expect(afterPayErrorAlert.isVisible()).toBeTruthy();
        });
    });

    describe.each([
        [paymentMethods.CREDIT_CARD, { data: 'creditCardSubmit' }],
        [paymentMethods.AFTERPAY, { data: 'afterpayPaymentReceived' }],
        [paymentMethods.APPLEPAY, { data: 'applepayOpened' }],
        [paymentMethods.PAYPAL, { data: 'showLoaderForPaypal' }]
    ])('handleIFrameContentWindowMessage', (expected, event) => {
        test(`sets paymentMethod to ${getEnumName(paymentMethods, expected)} when data is ${event.data}`, () => {
            // Arrange
            const { wrapper } = getMountedComponent(defaultOrder);
            useMainStore().order.payment.paymentMethod = paymentMethods.PayNow;

            useMainStore().savePaymentMethodChoice = jest.fn().mockImplementation((value) => { useMainStore().order.payment.paymentMethod = value; });

            // Act
            wrapper.vm.handleIFrameContentWindowMessage(event);
            const result = useMainStore().order.payment.paymentMethod;

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