/* eslint-env jest */
import '@testing-library/jest-dom';
import { flushPromises } from '@vue/test-utils';
import { render, waitFor } from '@testing-library/vue';
import baseMixin from '@/mixins/base-mixin';
import { createTestingPinia } from '@pinia/testing';
import userEvent from '@testing-library/user-event';
import errorMessages from '@/constants/error-messages';
import issPageValues from '@/router/router-constants/issPage-values';
import queryStrings from '@/constants/query-strings';
import { GaActions } from '@/constants/analytics';
import navigationScenarios from '@/router/router-constants/navigation-scenarios';
import routerParams from '@/router/router-constants/router-params';
import { useMainStore } from '@/store';
import VinLookupComponent from '@/layouts/vin-lookup/vin-lookup.vue';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';

const continueButtonQuerySelector = '[data-test-id="site-footer-main-button"]';
const errorMessageWrapperElSelector = '#vin-question-wrapper .form-test-error';
const vinInputSelector = '#vin-question';
const mockValidVin = '12345678901234567';
const mockCarId = 'Mock Car Id';
const lookupVehicleByVin = {
    methodName: 'lookupVehicleByVin',
    mockResponse: {
        data: {
            carId: mockCarId,
            canSafeliteService: true
        }
    }
};
const lookupVehicleByVinCannotService = {
    methodName: 'lookupVehicleByVin',
    mockResponse: {
        data: {
            carId: mockCarId,
            isBigTruck: true,
            canSafeliteService: false
        }
    }
};
const lookupVehicleByVinError = {
    methodName: 'lookupVehicleByVin',
    mockResponse: {
        data: {
            error: true
        }
    }
};
const getPartsOrQuestions = {
    methodName: 'getPartsOrQuestions',
    mockResponse: null
};

const vehicleWithPartQuestionsMockResponse = {
    data: {
        partsOrQuestions: [
            {
                partQuestions: [
                    {
                        id: 1
                    }
                ]
            }
        ]
    }
};

const vehicleWithMultiplePartsMockResponse = {
    data: {
        partsOrQuestions: [
            {
                parts: [
                    {
                        id: 1
                    },
                    {
                        id: 2
                    }
                ]
            }
        ]
    }
};

const vehicleWithMoldingQuestionsMockResponse = {
    data: {
        partsOrQuestions: [
            {
                parts: [
                    {
                        childPartQuestions: [
                            {
                                id: 1
                            }
                        ]
                    }
                ]
            }
        ]
    }
};

const vehicleWithCapabilityQuestionsMockResponse = {
    data: {
        partsOrQuestions: [
            {
                parts: [
                    {
                        requiresCapabilityQuestions: true,
                        partNumber: 1
                    }
                ]
            }
        ]
    }
};

const vehicleWithNoAdditionalPartsOrQuestionsMockResponse = {
    data: {
        partsOrQuestions: [
            {
                parts: [
                    {
                        childPartQuestions: [],
                        requiresCapabilityQuestions: false
                    }
                ]
            }
        ],
        partQuestions: null
    }
};

const partsOrQuestionsErrorMockResponse = {
    error: 'Error getting parts'
};

jest.mock('bootstrap', () => ({
    getInstance: jest.fn(),
    getOrCreateInstance: jest.fn()
}));

const mockRoute = {
    query: {
    // Needed inside vehicle-questions-mixin
        issPage: issPageValues.VIN_LOOKUP
    }
};
const mockRouter = {
    navigate: jest.fn(),
    navigateWithSpinner: jest.fn()
};
const maska = jest.fn();
jest.mock('@/helpers/damage-helper', () => ({
    isGlassAvailableForCarId: jest.fn().mockResolvedValue(false)
}));
const mountOptions = {
    global: {
        directives: {
            maska
        },
        mixins: [
            {
                methods: {
                    getCmsContent: jest.fn(() => ''),
                    navigateBack: baseMixin.methods.navigateBack,
                    getFooterInfoBoxHeight: jest.fn(() => 80),
                    cssClassNameForCmsWidget: jest.fn(() => 'widget-name-mock-class'),
                    getPageNameByQueryString: jest.fn(() => ''),
                    pushEventToGA: jest.fn()
                },
                computed: {
                    GaActions() {
                        return GaActions;
                    },
                    navigationScenarios() {
                        return navigationScenarios;
                    },
                    queryStrings() {
                        return queryStrings;
                    }
                }
            }
        ],
        mocks: {
            $route: mockRoute,
            $router: mockRouter
        },
        plugins: [createTestingPinia({
            initialState: {
                main: {
                    order: {
                        vehicle: {
                            carId: mockCarId
                        }
                    }
                }
            },
            stubActions: false
        })],
        stubs: {
            siteHeader: true,
            siteSubHeader: true,
            vinLocationInformation: true,
            vinLookupAlerts: true,
            vinQuestion: true
        }
    }
};

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

describe('vin-lookup.vue', () => {
    describe('Client-side validation', () => {
        test.each([
            { vin: null },
            { vin: '' }
        ])('VIN Required error messsage is displayed.', async ({ vin }) => {
            const user = userEvent.setup();
            mountOptions.data = () => ({
                vin
            });
            mountOptions.global.stubs.vinQuestion = false;
            const { container } = render(VinLookupComponent, mountOptions);
            const continueButton = container.querySelector(continueButtonQuerySelector);

            user.click(continueButton);
            await flushPromises();
            await waitFor(() => {
                const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector);
                expect(errorMessageWrapperEl).toBeVisible();

                const errorMessageEl = errorMessageWrapperEl.querySelector('span');
                expect(errorMessageEl.textContent).toBe(errorMessages.VIN_REQUIRED);
            });
        });

        test.each([
            { vin: '11' },
            { vin: 'I1234567890123456' },
            { vin: 'O1234567890123456' },
            { vin: 'Q1234567890123456' }
        ])('VIN Invalid Format error messsage is displayed.', async ({ vin }) => {
            const user = userEvent.setup();
            mountOptions.data = () => ({
                vin
            });
            mountOptions.global.stubs.vinQuestion = false;
            const { container } = render(VinLookupComponent, mountOptions);
            const continueButton = container.querySelector(continueButtonQuerySelector);

            user.click(continueButton);
            await flushPromises();
            await waitFor(() => {
                const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector);
                expect(errorMessageWrapperEl).toBeVisible();

                const errorMessageEl = errorMessageWrapperEl.querySelector('span');
                expect(errorMessageEl.textContent).toBe(errorMessages.VIN_FORMAT);
            });
        });

        test('No error message is displayed when the vin is in the accepted format.', async () => {
            const user = userEvent.setup();
            mountOptions.data = () => ({
                vin: '12345678901234567'
            });
            mountOptions.global.stubs.vinQuestion = false;
            const { container } = render(VinLookupComponent, mountOptions);
            const continueButton = container.querySelector(continueButtonQuerySelector);

            user.click(continueButton);
            await flushPromises();
            await waitFor(() => {
                const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector);
                expect(errorMessageWrapperEl).not.toBeVisible();

                const errorMessageEl = errorMessageWrapperEl.querySelector('span');
                expect(errorMessageEl.textContent).toBeFalsy();
            });
        });
    });

    describe('Navigation', () => {
        test('Click "Back", execute navigate with navigationScenario.CLICKED_BACK.', async () => {
            const user = userEvent.setup();
            const { container } = render(VinLookupComponent, mountOptions);
            const backButton = container.querySelector('[data-test-id="site-footer-back-button"]');

            await user.click(backButton);

            expect(mockRouter.navigateWithSpinner).toHaveBeenCalledTimes(1);
            expect(mockRouter.navigateWithSpinner).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, mockRoute);
        });

        // eslint-disable-next-line max-len
        test('Different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page.', async () => {
            const user = userEvent.setup();
            mountOptions.global.stubs.vinQuestion = false;
            getPartsOrQuestions.mockResponse = vehicleWithPartQuestionsMockResponse;
            lookupVehicleByVin.mockResponse.data.carId = 'Different Car Id';

            jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                .mockResolvedValue(lookupVehicleByVin.mockResponse);
            jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                .mockResolvedValue(getPartsOrQuestions.mockResponse);

            mountOptions.data = () => ({
                activeVehicleLookupAlertType: null,
                needToLookupVehicle: false,
                vehicleFromLookup: {
                    carId: 'Different Car Id'
                },
                vin: mockValidVin
            });
            const { container } = render(VinLookupComponent, mountOptions);

            const continueButton = container.querySelector(continueButtonQuerySelector);
            await user.click(continueButton);

            await flushPromises();
            await waitFor(() => {
                expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
                expect(mockRouter.navigate).toHaveBeenCalledWith(
                    navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
                    mockRoute,
                    {},
                    { [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true }
                );
            });
        });

        describe('Succesful navigateForward', () => {
            // eslint-disable-next-line max-len
            test('Vehicle with Part Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_PART_QUESTIONS', async () => {
                const user = userEvent.setup();
                mountOptions.global.stubs.vinQuestion = false;
                getPartsOrQuestions.mockResponse = vehicleWithPartQuestionsMockResponse;
                jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                    .mockResolvedValue(lookupVehicleByVin.mockResponse);
                jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                    .mockResolvedValue(getPartsOrQuestions.mockResponse);

                const { container } = render(VinLookupComponent, mountOptions);

                const vinInput = container.querySelector(vinInputSelector);
                await user.type(vinInput, mockValidVin);

                const continueButton = container.querySelector(continueButtonQuerySelector);
                await user.click(continueButton);

                await flushPromises();
                await waitFor(() => {
                    expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
                    expect(mockRouter.navigate)
                        .toHaveBeenCalledWith(
                            navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
                            mockRoute,
                            {},
                            {},
                            { partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions }
                        );
                });
            });

            // eslint-disable-next-line max-len
            test('Vehicle with Multiple Parts. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE', async () => {
                const user = userEvent.setup();
                mountOptions.global.stubs.vinQuestion = false;
                getPartsOrQuestions.mockResponse = vehicleWithMultiplePartsMockResponse;
                jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                    .mockResolvedValue(lookupVehicleByVin.mockResponse);
                jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                    .mockResolvedValue(getPartsOrQuestions.mockResponse);

                const { container } = render(VinLookupComponent, mountOptions);

                const vinInput = container.querySelector(vinInputSelector);
                await user.type(vinInput, mockValidVin);

                const continueButton = container.querySelector(continueButtonQuerySelector);
                await user.click(continueButton);

                await flushPromises();
                await waitFor(() => {
                    expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
                    expect(mockRouter.navigate)
                        .toHaveBeenCalledWith(
                            navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
                            mockRoute,
                            {},
                            {},
                            { partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions }
                        );
                });
            });

            // eslint-disable-next-line max-len
            test('Vehicle with Molding Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS', async () => {
                const user = userEvent.setup();
                mountOptions.global.stubs.vinQuestion = false;
                getPartsOrQuestions.mockResponse = vehicleWithMoldingQuestionsMockResponse;
                jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                    .mockResolvedValue(lookupVehicleByVin.mockResponse);
                jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                    .mockResolvedValue(getPartsOrQuestions.mockResponse);

                const { container } = render(VinLookupComponent, mountOptions);

                const vinInput = container.querySelector(vinInputSelector);
                await user.type(vinInput, mockValidVin);

                const continueButton = container.querySelector(continueButtonQuerySelector);
                await user.click(continueButton);

                await flushPromises();
                await waitFor(() => {
                    expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
                    expect(mockRouter.navigate)
                        .toHaveBeenCalledWith(
                            navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
                            mockRoute,
                            {},
                            {},
                            { partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions }
                        );
                });
            });

            // eslint-disable-next-line max-len
            test('Vehicle with Capability Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS', async () => {
                const user = userEvent.setup();
                const store = useMainStore();
                mountOptions.global.stubs.vinQuestion = false;
                getPartsOrQuestions.mockResponse = vehicleWithCapabilityQuestionsMockResponse;

                jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                    .mockResolvedValue(lookupVehicleByVin.mockResponse);
                jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                    .mockResolvedValue(getPartsOrQuestions.mockResponse);
                store.getCapabilityQuestions.mockResolvedValueOnce({ data: [] });

                const { container } = render(VinLookupComponent, mountOptions);

                const vinInput = container.querySelector(vinInputSelector);
                await user.type(vinInput, mockValidVin);

                const continueButton = container.querySelector(continueButtonQuerySelector);
                await user.click(continueButton);

                await flushPromises();
                await waitFor(() => {
                    expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
                    expect(mockRouter.navigate)
                        .toHaveBeenCalledWith(
                            navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
                            mockRoute,
                            {},
                            {},
                            { partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions }
                        );
                });
            });

            // eslint-disable-next-line max-len
            test('Vehicle no additional Parts or Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS', async () => {
                const user = userEvent.setup();
                mountOptions.global.stubs.vinQuestion = false;
                getPartsOrQuestions.mockResponse = vehicleWithNoAdditionalPartsOrQuestionsMockResponse;

                jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                    .mockResolvedValue(lookupVehicleByVin.mockResponse);
                jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                    .mockResolvedValue(getPartsOrQuestions.mockResponse);

                const { container } = render(VinLookupComponent, mountOptions);

                const vinInput = container.querySelector(vinInputSelector);
                await user.type(vinInput, mockValidVin);

                const continueButton = container.querySelector(continueButtonQuerySelector);
                await user.click(continueButton);

                await flushPromises();
                await waitFor(() => {
                    expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
                    expect(mockRouter.navigate)
                        .toHaveBeenCalledWith(
                            navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
                            mockRoute
                        );
                });
            });
            test('Display no service alert if entered vin cannot be serviced', async () => {
                const user = userEvent.setup();
                mountOptions.global.stubs.vinQuestion = false;

                mountOptions.data = () => ({
                    vinWithNonMatchingCarId: false,
                    isCarIdDifferentFromTheStore: false,
                    vin: mockValidVin
                });

                getPartsOrQuestions.mockResponse = vehicleWithNoAdditionalPartsOrQuestionsMockResponse;

                jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                    .mockResolvedValue(lookupVehicleByVinCannotService.mockResponse);
                jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                    .mockResolvedValue(getPartsOrQuestions.mockResponse);

                const { container } = render(VinLookupComponent, mountOptions);

                const vinInput = container.querySelector(vinInputSelector);
                await user.type(vinInput, mockValidVin);

                const continueButton = container.querySelector(continueButtonQuerySelector);
                await user.click(continueButton);

                await flushPromises();
                await waitFor(() => {
                    expect(container).toContainHTML('noService');
                });
            });

            // eslint-disable-next-line max-len
            test('Policy Vehicle with invalid vin corrected. Click "Continue", execute navigate with navigationScenario.CORRECTED_VIN_FROM_POLICY_VEHICLE', async () => {
                const user = userEvent.setup();
                mountOptions.global.stubs.vinQuestion = false;

                mountOptions.data = () => ({
                    vinWithNonMatchingCarId: true,
                    isCarIdDifferentFromTheStore: false,
                    vin: mockValidVin
                });

                getPartsOrQuestions.mockResponse = vehicleWithNoAdditionalPartsOrQuestionsMockResponse;

                jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                    .mockResolvedValue(lookupVehicleByVin.mockResponse);
                jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                    .mockResolvedValue(getPartsOrQuestions.mockResponse);

                const { container } = render(VinLookupComponent, mountOptions);

                const vinInput = container.querySelector(vinInputSelector);
                await user.type(vinInput, mockValidVin);

                const continueButton = container.querySelector(continueButtonQuerySelector);
                await user.click(continueButton);

                await flushPromises();
                await waitFor(() => {
                    expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
                    expect(mockRouter.navigate)
                        .toHaveBeenCalledWith(
                            navigationScenarios.CORRECTED_VIN_FROM_POLICY_VEHICLE,
                            mockRoute
                        );
                });
            });
            test(
                'Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario',
                async () => {
                    const user = userEvent.setup();
                    mountOptions.global.stubs.vinQuestion = false;

                    mountOptions.data = () => ({
                        vinWithNonMatchingCarId: false,
                        isCarIdDifferentFromTheStore: false,
                        vin: mockValidVin,
                        hasBailedOut: true
                    });

                    getPartsOrQuestions.mockResponse = partsOrQuestionsErrorMockResponse;

                    jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
                        .mockResolvedValue(lookupVehicleByVin.mockResponse);
                    jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
                        .mockResolvedValue(getPartsOrQuestions.mockResponse);

                    const { container } = render(VinLookupComponent, mountOptions);

                    const vinInput = container.querySelector(vinInputSelector);
                    await user.type(vinInput, mockValidVin);

                    const continueButton = container.querySelector(continueButtonQuerySelector);
                    await user.click(continueButton);

                    await flushPromises();
                    await waitFor(() => {
                        expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
                        expect(mockRouter.navigate)
                            .toHaveBeenCalledWith(
                                navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
                                mockRoute
                            );
                    });
                }
            );
        });
    });
});
