// Components
import { shallowMount } from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import contactDetails from '@/layouts/contact-details/contact-details.vue';

// Supporting Files
import baseMixin from '@/mixins/base-mixin';
import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { getRandomString, getRandomInt, getRandomBoolean } from '@/helpers/data-generation.js';
import navigationScenarios from '@/router/router-constants/navigation-scenarios.js';
import { useMainStore } from '@/store/index.js';

/** @ignore */
function setupMocks({
    storeData,
    props
}) {
    const mountOptions = getMountOptions({
        router: {
            navigate: jest.fn(),
            navigateWithSpinner: jest.fn()
        },
        loadScript: jest.fn().mockResolvedValue()
    });
    
    if (storeData) mountOptions.global.plugins = [createTestingPinia({
        initialState: {
            main: storeData
        }
    })];

    window.google = {
        maps: {
            event: {
                addListener: jest
                    .fn()
                    .mockImplementation((element, eventName, callbackFunction) => {
                        /** @ignore */
                        function interceptedCallbackFunction(e) {
                            callbackFunction(e.detail);
                        }
                        // selectedPlace = "Woogly";
                        element.addEventListener(eventName, interceptedCallbackFunction);
                    }),
                removeListener: jest.fn(),
                clearInstanceListeners: jest.fn()
            },
            places: {
                Autocomplete: jest.fn().mockImplementation((el) => el),
                AutocompleteService: jest.fn().mockImplementation(() => {
                    return {
                        getPlacePredictions: (request, callback) => {
                            // Throwing an error here guarantees that the function that calls it finishes 
                            throw new Error();
                        }
                    };
                }),
                PlacesService: jest.fn().mockImplementation(() => {}),
                AutocompleteSessionToken: jest.fn().mockImplementation(() => {})
            }
        }
    };

    if (props) mountOptions.propsData = props;

    const wrapper =  shallowMount(contactDetails, mountOptions);

    return { wrapper };
}

describe('contactDetails.vue', () => {
    describe('Rendering', () => {
        test('Should render site header', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const siteHeader = wrapper.findComponent({ ref: 'siteHeader' });

            // Assert
            expect(siteHeader.exists()).toBe(true);
        });
        test('Should render sub title', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const siteSubHeader = wrapper.findComponent({ ref: 'siteSubHeader' });

            // Assert
            expect(siteSubHeader.exists()).toBe(true);
        });
        test('Should render appointment information alert', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const appointmentInformationAlert = wrapper.findComponent({ ref: 'appointmentInformationAlert' });

            // Assert
            expect(appointmentInformationAlert.exists()).toBe(true);
        });
        test('Should render same as policy address question subcomponent if service zip is same as customer zip', async () => {
            // Arrange
            const customerStreetAddress = getRandomString(10, 50);
            const customerStreetAddress2 = getRandomString(0, 50);
            const customerCity = getRandomString(4, 20);
            const customerState = getRandomString(2, 2);
            const serviceStreetAddress = getRandomString(10, 50);
            const serviceStreetAddress2 = getRandomString(0, 50);
            const serviceCity = getRandomString(4, 20);
            const serviceState = getRandomString(2, 2);
            const zipCode = getRandomInt(10000, 99999).toString();
            const storeData = {
                order: {
                    customer: {
                        address: {
                            streetAddress: customerStreetAddress,
                            streetAddress2: customerStreetAddress2,
                            city: customerCity,
                            state: customerState,
                            zipCode
                        }
                    },
                    serviceLocation: {
                        streetAddress: serviceStreetAddress,
                        streetAddress2: serviceStreetAddress2,
                        city: serviceCity,
                        state: serviceState,
                        zipCode
                    }
                }
            };
            const { wrapper } = setupMocks({ storeData });

            await wrapper.vm.$nextTick();

            // Act
            const sameAsPolicyAddressQuestion = wrapper.findComponent({ ref: 'sameAsPolicyAddressQuestion' });

            // Assert
            expect(sameAsPolicyAddressQuestion.exists()).toBe(true);
        });
        test('Should not render same as policy address question subcomponent if service zip is different from customer zip', () => {
            // Arrange
            const firstName = getRandomString(4, 15);
            const lastName = getRandomString(4, 15);
            const emailAddress = getRandomString(10, 20);
            const servicePhone = getRandomInt(1000000000, 9999999999).toString();
            const testZipCode = getRandomInt(10000, 99999);
            const customerZipCode = testZipCode.toString();
            const serviceZipCode = (testZipCode + 1).toString();
            const storeData = {
                order: {
                    customer: {
                        firstName,
                        lastName,
                        emailAddress,
                        address: {
                            zipCode: customerZipCode
                        }
                    },
                    contactInfo: {
                        servicePhone
                    },
                    serviceLocation: {
                        zipCode: serviceZipCode
                    }
                }
            };
            const { wrapper } = setupMocks({ storeData });

            // Act
            const sameAsPolicyAddressQuestion = wrapper.findComponent({ ref: 'sameAsPolicyAddressQuestion' });

            // Assert
            expect(sameAsPolicyAddressQuestion.exists()).toBe(false);
        });
        test('Should render address question subcomponent', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const addressQuestion = wrapper.findComponent({ ref: 'addressQuestion' });

            // Assert
            expect(addressQuestion.exists()).toBe(true);
        });
        test('Should render apartment question subcomponent', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const address2Question = wrapper.findComponent({ ref: 'address2Question' });

            // Assert
            expect(address2Question.exists()).toBe(true);
        });
        test('Should render city question subcomponent', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const cityQuestion = wrapper.findComponent({ ref: 'cityQuestion' });

            // Assert
            expect(cityQuestion.exists()).toBe(true);
        });
        test('Should render state question subcomponent', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const stateQuestion = wrapper.findComponent({ ref: 'stateQuestion' });

            // Assert
            expect(stateQuestion.exists()).toBe(true);
        });
        test('Should render zip code question subcomponent', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const zipCodeQuestion = wrapper.findComponent({ ref: 'zipCodeQuestion' });

            // Assert
            expect(zipCodeQuestion.exists()).toBe(true);
        });
        test('Should render change zip code alert', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const changeZipCodeAlert = wrapper.findComponent({ ref: 'changeZipCodeAlert' });

            // Assert
            expect(changeZipCodeAlert.exists()).toBe(true);
        });
        test('Should render vehicle protected question subcomponent', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const vehicleProtectedQuestion = wrapper.findComponent({ ref: 'vehicleProtectedQuestion' });

            // Assert
            expect(vehicleProtectedQuestion.exists()).toBe(true);
        });
        test('Should render technician notes textarea question subcomponent', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const notesQuestion = wrapper.findComponent({ ref: 'notesQuestion' });

            // Assert
            expect(notesQuestion.exists()).toBe(true);
        });
        test('Should render clearance text subcomponent', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const clearanceText = wrapper.findComponent({ ref: 'clearanceText' });

            // Assert
            expect(clearanceText.exists()).toBe(true);
        });
        test('Should render site footer', () => {
            // Arrange
            const { wrapper } = setupMocks({});

            // Act
            const footer = wrapper.findComponent({ ref: 'siteFooter' });

            // Assert
            expect(footer.exists()).toBe(true);
        });
        test('Mocked store yields expected data', () => {
            // Arrange
            const address = getRandomString(10, 50);
            const address2 = getRandomString(0, 50);
            const city = getRandomString(4, 20);
            const state = getRandomString(2, 2);
            const zipCode = getRandomInt(10000, 99999).toString();
            const notesForTechnician = getRandomString(1, 100);
            const isVehicleProtected = getRandomBoolean();
            const storeData = {
                order: {
                    serviceLocation: {
                        address,
                        address2,
                        city,
                        state,
                        zipCode,
                        isVehicleProtected
                    },
                    contactInfo: {
                        notesForTechnician
                    }
                }
            };

            const { wrapper } = setupMocks({ storeData });

            // Assert
            expect(wrapper.vm.address).toBe(address);
            expect(wrapper.vm.address2).toBe(address2);
            expect(wrapper.vm.city).toBe(city);
            expect(wrapper.vm.state).toBe(state);
            expect(wrapper.vm.zipCode).toBe(zipCode);
            expect(wrapper.vm.isVehicleProtected).toBe(isVehicleProtected);
            expect(wrapper.vm.notesForTechnician).toBe(notesForTechnician);
        });
    });

    describe('Navigation', () => {
        test('Back button clicked triggers navigation', () => {
            // Arrange
            const { wrapper } = setupMocks({});
            wrapper.vm.navigateBack = baseMixin.methods.navigateBack;

            // Act
            wrapper.vm.navigateBack();

            // Assert
            expect(wrapper.vm.$router.navigateWithSpinner).toHaveBeenCalled();
            expect(wrapper.vm.$router.navigateWithSpinner)
                .toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, undefined);
        });
        test('Forward button clicked triggers appropriate navigation', async () => {
            // Arrange
            const storeData = {
                order: {
                    serviceLocation: { IsSafeliteProvider: true }
                }
            };
            const { wrapper } = setupMocks({ storeData });

            // Act
            await wrapper.vm.forwardButtonAction();

            // Assert
            expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
            expect(wrapper.vm.$router.navigate)
                .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined);
        });
        test('Forward button click updates service location info', async () => {
            // Arrange
            const provider = {
                address: {
                    city: getRandomString(4, 20),
                    state: getRandomString(2, 2),
                    streetAddress: getRandomString(10, 50),
                    zipCode: getRandomInt(10000, 99999).toString(),
                    zipCodeCtu: getRandomInt(10000, 99999).toString()
                },
                companyName: getRandomString(5, 15),
                phoneNumber: getRandomInt(1000000000, 9999999999).toString(),
                providerNumber: getRandomInt(100000, 999999).toString()
            }
            const storeData = {
                order: {
                    serviceLocation: {
                        provider
                    }
                }
            };
            const { wrapper } = setupMocks({ storeData });
            const address = getRandomString(10, 50);
            const address2 = getRandomString(0, 50);
            const city = getRandomString(4, 20);
            const state = getRandomString(2, 2);
            const zipCode = getRandomInt(10000, 99999).toString();
            const isVehicleProtected = getRandomBoolean().toString();
            wrapper.setData({
                address,
                address2,
                city,
                state,
                zipCode,
                isVehicleProtected
            });

            // Act
            await wrapper.vm.forwardButtonAction();

            // Assert
            expect(useMainStore().updateServiceLocation).toHaveBeenCalledWith({
                address,
                address2,
                city,
                isVehicleProtected,
                provider
            });
        });

        test('Forward button click updates notes for technician', async () => {
            // Arrange
            const { wrapper } = setupMocks({});
            const notesForTechnician = getRandomString(1, 100);
            wrapper.setData({
                notesForTechnician
            });

            // Act
            await wrapper.vm.forwardButtonAction();

            // Assert
            expect(useMainStore().updateContactInfo).toHaveBeenCalledWith({
                notesForTechnician
            });
        });
    });
});
