import { createStore } from "vuex";
import { endpoints } from "@/constants/endpoints.js";
import { storeMutations } from "@/constants/store-mutations";
import { getDateForSavedSessionTimeout } from "@/helpers/heritage-integration/session-helper";
import createPersistedState from "vuex-persistedstate";
import globalMethods from "@/global-methods";
import { storeActions } from "@/constants/store-actions";
import { applicationConfig } from "@/constants/application-config";
import { experimentTriggers } from "@/constants/experiments";
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
import { singleWindshieldCarIds } from "@/constants/single-windshield-carids";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js";
import { deepEqual } from "@/helpers/object-helper";
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { queryStrings } from "@/constants/query-strings";
import { partTypeStrings } from "@/constants/part-type-strings";
import {
    convertDateStringToDate,
    militaryToTwelveHourTime,
    getDisplayTextForDurationLength,
} from "@/layouts/schedule/helpers/schedule-helper";

// Export State
const getDefaultState = () => {
    return {
        submitAfterSave: false,
        order: {
            vehicle: {
                year: null,
                make: null,
                model: null,
                style: null,
                carId: null,
                category: null,
                vin: null,
                imageUrl: null,
                imageVifNumber: null,
                imageColor: null,
                registration: {
                    licensePlate: null,
                },
            },
            serviceLocation: {
                address: null,
                address2: null,
                city: null,
                state: null,
                zipCode: null,
                zipCodeCtu: null,
                appointmentType: null,
                isVehicleProtected: null,
                provider: {
                    providerNumber: null,
                    address: {
                        streetAddress: null,
                        city: null,
                        state: null,
                        zipCode: null,
                        zipCodeCtu: null,
                    },
                },
                techNotes: null,
            },
            customer: {
                firstName: null,
                lastName: null,
                emailAddress: null,
                phoneNumber: null,
                isSmsOptIn: null,
            },
            damage: {
                isRepair: null,
                numberOfChips: null,
                glassToReplace: null,
                partQuestionAnswers: null,
                moldingQuestionAnswers: null,
                capabilityQuestionAnswers: null,
            },
            lineItems: {
                glassParts: null,
                supportingItems: null,
                vaps: null,
                serverData: null,
            },
            payment: {
                isInsurance: null,
                insuranceCoverage: {
                    isVerified: null,
                    coverageStatus: null,
                },
                parentAccountNumber: 0,
            },
            schedule: {
                date: null,
                startTime: null,
                endTime: null,
                routeCode: null,
                jobMaxMinutes: null,
                jobMinMinutes: null,
            },
            referralNumber: null,
            referralSequenceNumber: null,
            referralDate: null,
            referralCorrelationId: null,
            eon: null,
        },
        applicationUser: {
            eventBus: [],
            pageData: {},
            savedSessionTimeout: getDateForSavedSessionTimeout(),
            saveSessionPromise: null,
            savedSessionId: null,
            crmCustomerId: null,
            lastPageVisited: null,
            experiments: [],
            triggeredSiteEntry: false,
        },
    };
};

export const state = getDefaultState();

// Export Mutations
export const mutations = {
    updateWorkOrderFlag(state, submitAfterSave) {
        state.submitAfterSave = submitAfterSave;
    },

    // VEHICLE MUTATIONS
    updateYear(state, year) {
        state.order.vehicle.year = year;
    },
    updateMake(state, make) {
        state.order.vehicle.make = make;
    },
    updateModel(state, model) {
        state.order.vehicle.model = model;
    },
    updateStyle(state, style) {
        state.order.vehicle.style = style;
    },
    updateCarId(state, carId) {
        state.order.vehicle.carId = carId;
    },
    updateVehicleCategory(state, category) {
        state.order.vehicle.category = category;
    },
    updateVehicleImageUrl(state, imageUrl) {
        state.order.vehicle.imageUrl = imageUrl;
    },
    updateVehicleImageVifNumber(state, imageVifNumber) {
        state.order.vehicle.imageVifNumber = imageVifNumber;
    },
    updateVehicleImageColor(state, imageColor) {
        state.order.vehicle.imageColor = imageColor;
    },
    updateVehicleVin(state, vin) {
        state.order.vehicle.vin = vin;
    },
    updateIsRepair(state, isRepair) {
        state.order.damage.isRepair = isRepair;
    },
    updateNumberOfChips(state, numberOfChips) {
        state.order.damage.numberOfChips = numberOfChips;
    },
    updateGlassToReplace(state, glassToReplace) {
        state.order.damage.glassToReplace = glassToReplace;
    },
    updatePartQuestionAnswers(state, answersArray) {
        state.order.damage.partQuestionAnswers = answersArray;
    },
    updateMoldingQuestionAnswers(state, answersArray) {
        state.order.damage.moldingQuestionAnswers = answersArray;
    },
    updateCapabilityQuestionAnswers(state, answersArray) {
        state.order.damage.capabilityQuestionAnswers = answersArray;
    },
    updateGlassParts(state, partsData) {
        state.order.lineItems.glassParts = partsData;
    },
    updateVaps(state, partsData) {
        state.order.lineItems.vaps = partsData;
    },
    updateSupportingItems(state, partsData) {
        state.order.lineItems.supportingItems = partsData;
    },
    updateLineItemsServerData(state, serverData) {
        state.order.lineItems.serverData = serverData;
    },
    updatePageData(state, pageData) {
        state.applicationUser.pageData[pageData.page] = pageData.data;
    },
    updateReferralCorrelationId(state, referralCorrelationId) {
        state.order.referralCorrelationId = referralCorrelationId;
    },
    updateReferralNumber(state, referralNumber) {
        state.order.referralNumber = referralNumber;
    },
    updateReferralSequenceNumber(state, referralSequenceNumber) {
        state.order.referralSequenceNumber = referralSequenceNumber;
    },
    updateReferralDate(state, referralDate) {
        state.order.referralDate = referralDate;
    },
    updateParentAcctNumber(state, parentAcctNumber) {
        state.order.payment.parentAccountNumber = parentAcctNumber;
    },
    updateEON(state, eon) {
        state.order.eon = eon;
    },
    updateIsInsurance(state, isInsurance) {
        state.order.payment.isInsurance = isInsurance;
    },
    updateInsuranceVerifiedStatus(state, isVerified) {
        state.order.payment.insuranceCoverage.isVerified = isVerified;
    },
    updateCustomerEmailAddress(state, customerEmailAddress) {
        state.order.customer.emailAddress = customerEmailAddress;
    },
    updateVehicle(state, vehicleInfo) {
        state.order.vehicle.year = vehicleInfo.year;
        state.order.vehicle.make = vehicleInfo.make;
        state.order.vehicle.model = vehicleInfo.model;
        state.order.vehicle.style = vehicleInfo.style;
        state.order.vehicle.carId = vehicleInfo.carId;
        state.order.vehicle.category = vehicleInfo.category;
        state.order.vehicle.vin = vehicleInfo.vin;

        state.order.vehicle.imageUrl = vehicleInfo.imageUrl;
        state.order.vehicle.imageVifNumber = vehicleInfo.imageVifNumber;
        state.order.vehicle.imageColor = vehicleInfo.imageVifColor;
    },
    updateRegistration(state, registrationInfo) {
        state.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate;
        state.order.customer.firstName = registrationInfo?.firstName;
        state.order.customer.lastName = registrationInfo?.lastName;
    },
    updateServiceZip(state, serviceZipInfo) {
        state.order.serviceLocation.state = serviceZipInfo.state;
        state.order.serviceLocation.zipCode = serviceZipInfo.zipCode;
        state.order.serviceLocation.zipCodeCtu = serviceZipInfo.zipCodeCtu;
    },
    updateServiceLocation(state, serviceLocationInfo) {
        state.order.serviceLocation.address = serviceLocationInfo.address;
        state.order.serviceLocation.address2 = serviceLocationInfo.address2;
        state.order.serviceLocation.city = serviceLocationInfo.city;
        state.order.serviceLocation.state = serviceLocationInfo.state;
        state.order.serviceLocation.zipCode = serviceLocationInfo.zipCode;
        state.order.serviceLocation.zipCodeCtu = serviceLocationInfo.zipCodeCtu;
        state.order.serviceLocation.appointmentType = serviceLocationInfo.appointmentType;
        state.order.serviceLocation.isVehicleProtected = serviceLocationInfo.isVehicleProtected;

        state.order.serviceLocation.provider = {
            providerNumber: serviceLocationInfo.provider?.providerNumber,
            address: {
                streetAddress: serviceLocationInfo.provider?.address?.streetAddress,
                city: serviceLocationInfo.provider?.address?.city,
                state: serviceLocationInfo.provider?.address?.state,
                zipCode: serviceLocationInfo.provider?.address?.zipCode,
                zipCodeCtu: serviceLocationInfo.provider?.address?.zipCodeCtu,
            },
        };
    },
    updateServiceLocationTechNotes(state, techNotes) {
        state.order.serviceLocation.techNotes = techNotes;
    },
    updateSchedule(state, scheduleInfo) {
        if (scheduleInfo) {
            state.order.schedule.date = scheduleInfo.date;
            state.order.schedule.startTime = scheduleInfo.startTime;
            state.order.schedule.endTime = scheduleInfo.endTime;
            state.order.schedule.routeCode = scheduleInfo.routeCode;
            state.order.schedule.jobMaxMinutes = scheduleInfo.jobMaxMinutes;
            state.order.schedule.jobMinMinutes = scheduleInfo.jobMinMinutes;
        }
    },
    updateCustomerDetails(state, customerDetails) {
        if (customerDetails) {
            state.order.customer.firstName = customerDetails.firstName;
            state.order.customer.lastName = customerDetails.lastName;
            state.order.customer.emailAddress = customerDetails.emailAddress;
            state.order.customer.phoneNumber = customerDetails.phoneNumber;
            state.order.customer.isSmsOptIn = customerDetails.isSmsOptIn;
        }
    },

    // applicationUser MUTATIONS
    updateSaveSessionPromise(state, saveSessionPromise) {
        state.applicationUser.saveSessionPromise = saveSessionPromise;
    },
    updateSavedSessionId(state, savedSessionId) {
        state.applicationUser.savedSessionId = savedSessionId;
    },
    updateCrmCustomerId(state, crmCustomerId) {
        state.applicationUser.crmCustomerId = crmCustomerId;
    },
    updateLastPageVisited(state, lastPageVisited) {
        state.applicationUser.lastPageVisited = lastPageVisited;
    },
    // EVENT BUS MUTATIONS
    addEventToBus(state, event) {
        state.applicationUser.eventBus.push(event);
    },
    removeEventFromBus(state, eventData) {
        const matchedEvent = state.applicationUser.eventBus.find(
            ({ category, subCategory }) =>
                category === eventData.category && subCategory === eventData.subCategory
        );
        const itemIndex = state.applicationUser.eventBus.indexOf(matchedEvent);

        // If the item exists, remove it.
        if (itemIndex > -1) {
            state.applicationUser.eventBus.splice(itemIndex, 1);
        }
    },

    // RESET DEPENDENCY MUTATIONS
    resetVehicleState(state) {
        state.order.vehicle.year = null;
        state.order.vehicle.make = null;
        state.order.vehicle.model = null;
        state.order.vehicle.style = null;
        state.order.vehicle.carId = null;
        state.order.vehicle.category = null;
        state.order.vehicle.vin = null;
        state.order.vehicle.imageUrl = null;
        state.order.vehicle.imageVifNumber = null;
        state.order.vehicle.imageColor = null;
    },
    resetDamageState(state) {
        state.order.damage.isRepair = null;
        state.order.damage.numberOfChips = null;
        state.order.damage.glassToReplace = null;
    },
    resetRegistrationState(state) {
        state.order.vehicle.registration.licensePlate = null;
        state.order.customer.firstName = null;
        state.order.customer.lastName = null;
    },
    resetGlassPartsState(state) {
        state.order.lineItems.glassParts = null;
        state.order.damage.partQuestionAnswers = null;
        state.order.damage.moldingQuestionAnswers = null;
        state.order.damage.capabilityQuestionAnswers = null;
        state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null;
        state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null;
        state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null;
        state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null;
    },
    resetSchedule(state) {
        state.order.schedule.date = null;
        state.order.schedule.startTime = null;
        state.order.schedule.endTime = null;
        state.order.schedule.routeCode = null;
        state.order.schedule.jobMaxMinutes = null;
        state.order.schedule.jobMinMinutes = null;

        //premium appointment fee used on schedule page also needs reset when schedule is reset
        const supportingItems = state.order.lineItems.supportingItems;
        const premiumAppointmentFeeIndex = supportingItems?.findIndex(
            (item) => item.partType == PREMIUM_FEE_PART_TYPE
        );

        if (premiumAppointmentFeeIndex >= 0) {
            supportingItems.splice(premiumAppointmentFeeIndex, 1);
            state.order.lineItems.supportingItems = supportingItems;
        }
    },
    resetState(state) {
        Object.assign(state, getDefaultState());
    },
    resetSaveSessionPromise(state) {
        state.applicationUser.saveSessionPromise = null;
    },
    resetServiceLocationAppointmentType(state) {
        state.order.serviceLocation.appointmentType = null;
    },
    resetServiceLocationProvider(state) {
        state.order.serviceLocation.provider = {
            providerNumber: null,
            address: {
                streetAddress: null,
                city: null,
                state: null,
                zipCode: null,
                zipCodeCtu: null,
            },
        };
    },
    resetServiceLocationMobileAddress(state) {
        state.order.serviceLocation.address = null;
        state.order.serviceLocation.address2 = null;
        state.order.serviceLocation.city = null;
        state.order.serviceLocation.state = null;
        state.order.serviceLocation.isVehicleProtected = null;
    },
    // Misc Mutations
    updateStateWithOrderInformation(state, sessionInformation) {
        state.order.referralNumber = sessionInformation.order.referralNumber;
        state.order.referralSequenceNumber = sessionInformation.order.referralSequenceNumber;
        state.order.referralDate = sessionInformation.order.referralDate;
        state.order.referralCorrelationId = sessionInformation.order.referralCorrelationId;
        state.order.eon = sessionInformation.order.eon;

        if (state.order.vehicle.vin !== sessionInformation.order.vehicle?.vin) {
            state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null;
            state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null;
            state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null;
            state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null;
        }

        state.order.vehicle = Object.assign(state.order.vehicle, {
            year: sessionInformation.order.vehicle?.year,
            make: sessionInformation.order.vehicle?.make,
            model: sessionInformation.order.vehicle?.model,
            style: sessionInformation.order.vehicle?.style,
            vin: sessionInformation.order.vehicle?.vin,
            carId: sessionInformation.order.vehicle?.carId,
            category: sessionInformation.order.vehicle?.category,
            imageUrl: sessionInformation.order.vehicle?.imageUrl,
            imageVifNumber: sessionInformation.order.vehicle?.imageVifNumber,
            imageColor: sessionInformation.order.vehicle?.imageVifColor,
        });

        state.order.damage.glassToReplace = sessionInformation.order.damage.glassToReplace;
        state.order.damage.isRepair = sessionInformation.order.damage.isRepair;
        state.order.damage.numberOfChips = sessionInformation.order.damage.numberOfChips;

        state.order.damage.partQuestionAnswers =
            sessionInformation.order.damage.partQuestionAnswers;
        state.order.damage.moldingQuestionAnswers =
            sessionInformation.order.damage.moldingQuestionAnswers;
        state.order.damage.capabilityQuestionAnswers =
            sessionInformation.order.damage.capabilityQuestionAnswers;

        state.order.lineItems.glassParts = sessionInformation.order.lineItems.glassParts;
        state.order.lineItems.supportingItems = sessionInformation.order.lineItems.supportingItems;
        state.order.lineItems.vaps = sessionInformation.order.lineItems.vaps;
        state.order.lineItems.serverData = sessionInformation.order.lineItems.serverData;

        state.order.payment.parentAccountNumber =
            sessionInformation.order.payment.parentAccountNumber;

        state.order.serviceLocation.address =
            sessionInformation.order.serviceLocation.streetAddress;
        state.order.serviceLocation.address2 =
            sessionInformation.order.serviceLocation.streetAddress2;
        state.order.serviceLocation.city = sessionInformation.order.serviceLocation.city;
        state.order.serviceLocation.state = sessionInformation.order.serviceLocation.state;
        state.order.serviceLocation.zipCode = sessionInformation.order.serviceLocation.zipCode;
        state.order.serviceLocation.zipCodeCtu =
            sessionInformation.order.serviceLocation.zipCodeCtu;

        state.order.serviceLocation.appointmentType =
            sessionInformation.order.serviceLocation.appointmentType;
        state.order.serviceLocation.isVehicleProtected =
            sessionInformation.order.serviceLocation.isVehicleProtected;

        state.order.serviceLocation.provider.providerNumber =
            sessionInformation.order.serviceLocation.provider?.providerNumber;
        state.order.serviceLocation.provider.address.streetAddress =
            sessionInformation.order.serviceLocation.provider?.address?.streetAddress;
        state.order.serviceLocation.provider.address.city =
            sessionInformation.order.serviceLocation.provider?.address?.city;
        state.order.serviceLocation.provider.address.state =
            sessionInformation.order.serviceLocation.provider?.address?.state;
        state.order.serviceLocation.provider.address.zipCode =
            sessionInformation.order.serviceLocation.provider?.address?.zipCode;
        state.order.serviceLocation.provider.address.zipCodeCtu =
            sessionInformation.order.serviceLocation.provider?.address?.zipCodeCtu;
        state.order.serviceLocation.techNotes = sessionInformation.order.serviceLocation.techNotes;

        state.order.payment.isInsurance = sessionInformation.order.payment.isInsurance;
        state.order.payment.insuranceCoverage.isVerified =
            sessionInformation?.order.payment.insuranceCoverage.isVerified;
        state.order.payment.insuranceCoverage.coverageStatus =
            sessionInformation?.insuranceInfo?.coverageStatus;

        state.order.customer.emailAddress = sessionInformation.order.customer.emailAddress;
        state.order.customer.firstName = sessionInformation.order.customer.firstName;
        state.order.customer.lastName = sessionInformation.order.customer.lastName;
        state.order.customer.phoneNumber = sessionInformation.order.customer.servicePhoneNumber;
        state.order.customer.isSmsOptIn = sessionInformation.order.customer.isSmsOptIn;

        state.order.existingPromoCode = sessionInformation.order.existingPromoCode;
        state.applicationUser.experiments = sessionInformation.applicationUser.experiments;
        state.applicationUser.crmCustomerId = sessionInformation.applicationUser.crmCustomerId;
        state.applicationUser.pageData = sessionInformation.applicationUser.pageData;
        state.applicationUser.lastPage = sessionInformation.applicationUser.lastPage;

        state.order.schedule.date = sessionInformation.order.schedule?.date;
        state.order.schedule.startTime = sessionInformation.order.schedule?.startTime;
        state.order.schedule.endTime = sessionInformation.order.schedule?.endTime;
        state.order.schedule.routeCode = sessionInformation.order.schedule?.routeCode;
        state.order.schedule.jobMaxMinutes = sessionInformation.order.schedule?.jobMaxMinutes;
        state.order.schedule.jobMinMinutes = sessionInformation.order.schedule?.jobMinMinutes;
    },
    updateExperiments(state, experiments) {
        state.applicationUser.experiments = experiments;
    },
    updateTriggeredSiteEntry(state, wasSiteEntryTriggered) {
        state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered;
    },
};

// Export Getters
export const getters = {
    vehicle: (state) => state.order.vehicle,
    eventBusItem: (state) => (eventCategory, eventSubCategory) => {
        const matchedEvent = state.applicationUser.eventBus.find(
            ({ category, subCategory }) =>
                category === eventCategory && subCategory === eventSubCategory
        );

        return matchedEvent !== undefined ? matchedEvent.eventValue : undefined;
    },
    eventBus: (state) => state.applicationUser.eventBus,
    damage: (state) => state.order.damage,
    hasExactlyOneChip: (state) => {
        return state.order.damage?.numberOfChips === 1;
    },
    hasAnyNonWindshieldGlassParts: (state) => {
        const nonWindshieldItems = state.order.damage.glassToReplace?.filter(
            (glassToReplace) => glassToReplace.glassLocation != "Windshield"
        );
        return !!nonWindshieldItems?.length;
    },
    isMobileAppointment: (state) => {
        return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
    },
    isDropOffAppointment: (state) => {
        return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF;
    },
    isRecalibrationOnOrder: (state) => {
        return getHasRecalibrationPart(state);
    },
    areRearWipersOnOrder: (state) => {
        return !!state.order.lineItems.vaps?.some(
            (vap) => vap.partType.toUpperCase() === partTypeStrings.REAR_WIPER.toUpperCase()
        );
    },
    scheduleDisplayText: (state) => {
        const dateModel = convertDateStringToDate(state.order.schedule.date);
        const readableDate = dateModel?.toLocaleDateString("en-us", {
            weekday: "long",
            month: "long",
            day: "numeric",
            year: "numeric",
        });

        const readableStartTime = militaryToTwelveHourTime(state.order.schedule.startTime);
        const readableEndTime = militaryToTwelveHourTime(state.order.schedule.endTime);

        const readableDuration = getDisplayTextForDurationLength(
            state.order.schedule.jobMinMinutes,
            state.order.schedule.jobMaxMinutes
        );

        return {
            date: readableDate,
            startTime: readableStartTime,
            endTime: readableEndTime,
            duration: readableDuration,
        };
    },
    lineItems: (state) => state.order.lineItems,
    pageData: (state) => (page) => {
        return state.applicationUser.pageData[page];
    },
    applicationUser: (state) => state.applicationUser,
    order: (state) => state.order,
    payment: (state) => state.order.payment,
    experimentOrder: (state) => {
        return {
            funnelVehicleYear: state.order.vehicle.year,
            funnelVehicleMake: state.order.vehicle.make,
            funnelVehicleModel: state.order.vehicle.model,
            funnelVehicleStyle: state.order.vehicle.style,
            funnelIsRepair: state.order.damage.isRepair,
            funnelNumberOfChips: state.order.damage.numberOfChips,
            funnelCarId: state.order.vehicle.carId,
            funnelServiceCity: state.order.serviceLocation.city,
            funnelServiceState: state.order.serviceLocation.state,
            funnelServiceZipCode: state.order.serviceLocation.zipCode,
            funnelParentAccountNumber: state.order.payment.parentAccountNumber,
            funnelIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified,
            funnelHasRecalibrationPart: getHasRecalibrationPart(state),
            funnelSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1,
            funnelSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
                state.order.damage.glassToReplace,
                "glassLocation"
            ).includes(damageLocationsSelected.WINDSHIELD),
            funnelSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
                state.order.damage.glassToReplace,
                "glassLocation"
            ).includes(damageLocationsSelected.REAR),
            funnelSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
                state.order.damage.glassToReplace,
                "glassLocation"
            ).includes(damageLocationsSelected.DRIVER),
            funnelSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
                state.order.damage.glassToReplace,
                "glassLocation"
            ).includes(damageLocationsSelected.PASSENGER),

            funnelOrderPartNumbers: [
                ...getNonFalseValuesOfPropertyInArrayOfObjects(
                    state.order.lineItems.glassParts,
                    "partNumber"
                ),
                ...getNonFalseValuesOfPropertyInArrayOfObjects(
                    state.order.lineItems.supportingItems,
                    "partNumber"
                ),
                ...getNonFalseValuesOfPropertyInArrayOfObjects(
                    state.order.lineItems.vaps,
                    "partNumber"
                ),
            ],

            funnelOrderPartTypes: [
                ...getNonFalseValuesOfPropertyInArrayOfObjects(
                    state.order.lineItems.glassParts,
                    "recalibrationType"
                ),
            ],
        };
    },
    experimentSettings: (state) =>
        state.applicationUser.experiments
            .map((x) => x.settings)
            .reduce((r, c) => Object.assign(r, c), {}) ?? {},
};

function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
    return (array ?? []).map((x) => x[propertyName]).filter((x) => x);
}

function provisionalTriggersToString(provisionalTriggers) {
    return "ProvisionalTriggers:" + provisionalTriggers.join(",");
}

// Export Actions
export const actions = {
    // Vehicle API Actions
    getVehicleYears(context) {
        return globalMethods.callHttpClient({
            method: endpoints.GetVehicleYears.method,
            endpoint: endpoints.GetVehicleYears.url,
            payload: {},
        });
    },

    lookupVehicleByYmms(context, { year, make, model, style }) {
        return globalMethods.callHttpClient({
            method: endpoints.LookupVehicleByYmms.method,
            endpoint: `${endpoints.LookupVehicleByYmms.url}/${year}/${make}/${model}/${style}`,
            payload: {},
        });
    },

    lookupVehicleByVin(context, { vin }) {
        return globalMethods.callHttpClient({
            method: endpoints.LookupVehicleByVin.method,
            endpoint: endpoints.LookupVehicleByVin.url,
            payload: {
                vin: vin, // EX "1J4GW58S4XC541166"
            },
        });
    },

    lookupVinByPlate(context, { licensePlate, licenseState }) {
        return globalMethods.callHttpClient({
            method: endpoints.LookupVinByPlate.method,
            endpoint: endpoints.LookupVinByPlate.url,
            payload: {
                licensePlate: licensePlate,
                licenseState: licenseState,
            },
        });
    },

    lookupVinByAddress(
        context,
        { licenseLastName, licenseStreetAddress, licenseZip, licenseState }
    ) {
        return globalMethods.callHttpClient({
            method: endpoints.LookupVinByAddress.method,
            endpoint: endpoints.LookupVinByAddress.url,
            payload: {
                licenseLastName: licenseLastName,
                licenseStreetAddress: licenseStreetAddress,
                licenseZip: licenseZip,
                licenseState: licenseState,
            },
        });
    },

    lookupVinByImage(context, image) {
        return new Promise((resolve, reject) => {
            let reader = new FileReader();
            reader.onload = (e) => {
                resolve(reader.result);
            };
            reader.readAsDataURL(image);
        }).then((result) => {
            const components = result.split(",");
            const contentType = image.type;
            const imageBase64 = components[1];

            const data = {
                imageData: imageBase64,
                contentType: contentType,
                fileName: image.name,
            };

            return globalMethods.callHttpClient({
                method: endpoints.LookupVinByImage.method,
                endpoint: endpoints.LookupVinByImage.url,
                payload: data,
            });
        });
    },

    isVinByAddressPermissible(context, zip) {
        return globalMethods.callHttpClient({
            method: endpoints.IsVinByAddressPermissible.method,
            endpoint: `${endpoints.IsVinByAddressPermissible.url}?zipcode=${zip}`,
            payload: {},
        });
    },

    getVehicleMakes(context, { year }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetVehicleMakes.method,
            endpoint: `${endpoints.GetVehicleMakes.url}/${year}`,
            payload: {},
        });
    },

    getVehicleModels(context, { year, make }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetVehicleModels.method,
            endpoint: `${endpoints.GetVehicleModels.url}/${year}/${make}`,
            payload: {},
        });
    },

    getVehicleStyles(context, { year, make, model }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetVehicleStyles.method,
            endpoint: `${endpoints.GetVehicleStyles.url}/${year}/${make}/${model}`,
            payload: {},
        });
    },

    getVehicle(context, { year, make, model, style }) {
        return globalMethods
            .callHttpClient({
                methods: endpoints.GetVehicle.method,
                endpoint: `${endpoints.GetVehicle.url}/${year}/${make}/${model}/${style}`,
                payload: {},
            })
            .then((response) => {
                return response;
            });
    },

    getDamageOptions(context, { carId }) {
        return globalMethods.callHttpClient({
            methods: endpoints.GetDamageOptions.method,
            endpoint: `${endpoints.GetDamageOptions.url}/${carId}`,
            payload: {},
            additionalSuccessEventDataHandler: (response) =>
                "QueryStringZip: " + getQuerystringParameter(queryStrings.ZIP_CODE),
        });
    },

    validateZip(context, { zip }) {
        return globalMethods.callHttpClient({
            methods: endpoints.ValidateZip.method,
            endpoint: `${endpoints.ValidateZip.url}/${zip}`,
        });
    },

    // Dependency Actions
    resetDamageAndDependencies(context) {
        context.commit(storeMutations.RESET_DAMAGE_STATE);

        context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);

        context.commit(storeMutations.UPDATE_VAPS, null);
    },

    resetRegistrationAndDependencies(context) {
        context.commit(storeMutations.RESET_REGISTRATION_STATE);

        context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
    },

    resetPartsAndDependencies(context) {
        context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
        context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);

        context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
    },

    resetServiceLocationAndDependencies(context) {
        context.commit(storeMutations.RESET_SERVICE_LOCATION_APPOINTMENT_TYPE);
        context.commit(storeMutations.RESET_SERVICE_LOCATION_PROVIDER);

        context.commit(storeMutations.RESET_SCHEDULE);
    },

    resetState(context) {
        context.commit(storeMutations.RESET_STATE);
    },

    resetSaveSessionPromise(context) {
        context.commit(storeMutations.RESET_SAVE_SESSION_PROMISE);
    },

    // Content API Actions
    getRouteInfo(context, { pageName }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetRouteInfo.method,
            endpoint: endpoints.GetRouteInfo.url(applicationConfig.APPLICATION_ABBREVIATION),
            payload: {
                pageName: pageName,
            },
        });
    },

    getHomepageName(context) {
        return globalMethods.callHttpClient({
            method: endpoints.GetHomepageInfo.method,
            endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION),
        });
    },

    getPageData(context, { pageName }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetPageData.method,
            endpoint: endpoints.GetPageData.url(
                applicationConfig.APPLICATION_ABBREVIATION,
                pageName
            ),
            payload: {},
        });
    },

    // Analytics Actions
    logExperimentExposure(context, { userId, sessionKey, pageName, experiment }) {
        return globalMethods.callHttpClient({
            method: endpoints.LogExperimentExposureIfAssigned.method,
            endpoint: endpoints.LogExperimentExposureIfAssigned.url,
            payload: {
                experimentForLogging: {
                    userId: userId,
                    experimentUniverseId: experiment.universeId,
                    experimentUniverseName: experiment.universeName,
                    experimentTestId: experiment.testId,
                    experimentTestName: experiment.testName,
                    experimentVariationId: experiment.variationId,
                    experimentVariationName: experiment.variationName,
                    enabled: experiment.isActive,
                    isExposed: experiment.isExposed,
                    userPartitionNumber: experiment.userPartitionNumber,
                    assignmentId: experiment.assignmentId,
                    sessionKey: sessionKey,
                    pageName: pageName,
                },
            },
        });
    },

    // Location API Actions
    getAlertReasonsByCtu(context, { ctu }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetAlertReasons.method,
            endpoint: `${endpoints.GetAlertReasons.url}/${ctu}`,
            payload: {},
        });
    },

    // Misc Actions
    updateStoreWithSaveSessionResponse(
        context,
        {
            referralNumber,
            referralSequenceNumber,
            referralDate,
            referralCorrelationId,
            eon,
            parentAccountNumber,
            savedSessionId,
            crmCustomerId,
        }
    ) {
        context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
        context.commit(storeMutations.UPDATE_REFERRAL_SEQUENCE_NUMBER, referralSequenceNumber);
        context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
        context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
        context.commit(storeMutations.UPDATE_EON, eon);
        context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber);
        context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
        context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
    },

    logPageView(
        context,
        {
            userId,
            sessionKey,
            pageName,
            sessionId,
            action,
            event,
            shouldUseSessionId,
            experimentsForUser,
            referralSequenceNumber,
            parentAccountNumber,
        }
    ) {
        var payload = {
            userId: userId,
            sessionKey: sessionKey,
            sessionId: sessionId,
            pageName: pageName,
            applicationName: applicationConfig.APPLICATION_NAME,
            action: action,
            event: event,
            shouldUseSessionId: shouldUseSessionId,
            experimentsForUser: experimentsForUser,
            referralSequenceNumber: referralSequenceNumber,
            parentAccountNumber: parentAccountNumber,
        };

        return globalMethods
            .callHttpClient({
                method: endpoints.LogPageView.method,
                endpoint: endpoints.LogPageView.url,
                payload: payload,
                logApiCall: false,
            })
            .then(
                (response) => {
                    return response;
                },
                (error) => {
                    console.log("Analytics Service Error: " + error.data);
                }
            );
    },

    logCustomEvent(
        context,
        {
            userId,
            sessionKey,
            pageName,
            sessionId,
            category,
            action,
            label,
            value,
            shouldUseSessionId,
            experimentsForUser,
            referralSequenceNumber,
            parentAccountNumber,
        }
    ) {
        var payload = {
            userId: userId,
            sessionKey: sessionKey,
            sessionId: sessionId,
            pageName: pageName,
            applicationName: applicationConfig.APPLICATION_NAME,
            category: category,
            action: action,
            label: label,
            value: value,
            shouldUseSessionId: shouldUseSessionId,
            experimentsForUser: experimentsForUser,
            referralSequenceNumber: referralSequenceNumber,
            parentAccountNumber: parentAccountNumber,
        };

        return globalMethods
            .callHttpClient({
                method: endpoints.LogCustomEvent.method,
                endpoint: endpoints.LogCustomEvent.url,
                payload: payload,
                logApiCall: false,
            })
            .then(
                (response) => {
                    return response;
                },
                (error) => {
                    console.log("Analytics Service Error: " + error.data);
                }
            );
    },
    initializeSession(context, { userId, sessionId, userAgent, referrer }) {
        var payload = {
            applicationName: applicationConfig.APPLICATION_NAME,
            userId: userId,
            deviceId: userId,
            sessionId: sessionId,
            userAgent: userAgent,
            operatorId: "WEB",
            userName: "SafeliteConceptFunnel",
            referrer: referrer,
        };

        return globalMethods
            .callHttpClient({
                method: endpoints.InitializeSession.method,
                endpoint: endpoints.InitializeSession.url,
                payload: payload,
                logApiCall: false,
            })
            .then(
                (response) => {
                    return response;
                },
                (error) => {
                    console.log("Analytics Service Error: " + error.data);
                }
            );
    },

    // Misc Actions
    setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId, eon }) {
        context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
        context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
        context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
        context.commit(storeMutations.UPDATE_EON, eon);
    },

    GetExperimentsByUser(context, { userId }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetExperimentsByUser.method,
            endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`,
            payload: {},
        });
    },

    async runExperimentsForTrigger(context, { userId, triggerEvent, triggerValue }) {
        if (triggerEvent == experimentTriggers.SITE_ENTRY) {
            context.commit(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true);
        }

        var payload = {
            applicationName: applicationConfig.APPLICATION_NAME,
            userId: userId,
            triggerEvent: triggerEvent,
            triggerValue: triggerValue,
            experimentOrder: context.getters.experimentOrder,
        };

        const response = await globalMethods.callHttpClient({
            method: endpoints.RunExperimentsForTrigger.method,
            endpoint: endpoints.RunExperimentsForTrigger.url,
            payload: payload,
        });

        context.commit(storeMutations.UPDATE_EXPERIMENTS, response.data.experiments);
    },

    getEvoxImage(context, { relativeUrl }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetPageData.method,
            endpoint: relativeUrl,
            payload: {},
        });
    },

    // PartsOrQuestions API Actions
    async getPartsOrQuestions(context) {
        const vehicle = context.getters.vehicle;
        const damage = context.getters.damage;
        const order = context.state.order;

        const carId = vehicle.carId;
        const glassArray = damage.glassToReplace;
        const zipCode = order.serviceLocation.zipCode;
        const vin = vehicle.vin;

        // create a new array to avoid mutating state
        const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray);

        const response = await globalMethods.callHttpClient({
            method: endpoints.GetPartsOrQuestions.method,
            endpoint: endpoints.GetPartsOrQuestions.url,
            payload: {
                carId: carId,
                glassPieces: glassArrayForPayload,
                zip: zipCode,
                vin: vin,
            },
        });

        // Flatten location and name properties
        response.data.partsOrQuestions = convertGlassPieceNamingFromApi(
            response.data.partsOrQuestions
        );

        return response;
    },

    // Parts API Actions
    async getParts(context) {
        const vehicle = context.getters.vehicle;
        const damage = context.getters.damage;
        const order = context.state.order;

        const carId = vehicle.carId;
        const glassArray = damage.glassToReplace;
        const resultsArray = damage.partQuestionAnswers;
        const zipCode = order.serviceLocation.zipCode;
        const vin = vehicle.vin;

        // create a new array to avoid mutating state
        const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray);
        const resultsArrayForPayload = convertResultsForApi(resultsArray);

        const response = await globalMethods.callHttpClient({
            method: endpoints.GetParts.method,
            endpoint: endpoints.GetParts.url,
            payload: {
                carId: carId,
                glassPieces: glassArrayForPayload,
                answerResults: resultsArrayForPayload,
                zip: zipCode,
                vin: vin,
            },
        });

        // Flatten location and name properties
        response.data.glassPieceParts = convertGlassPieceNamingFromApi(
            response.data.glassPieceParts
        );

        return response;
    },

    getWipers(context) {
        const carId = context.getters.vehicle.carId;
        const serviceZipCode = context.getters.order.serviceLocation.zipCode;
        return globalMethods
            .callHttpClient({
                method: endpoints.GetWipers.method,
                endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}`,
            })
            .catch((error) => {
                // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow
                return [];
            });
    },

    getRainDefense(context) {
        return globalMethods.callHttpClient({
            method: endpoints.GetRainDefense.method,
            endpoint: endpoints.GetRainDefense.url,
        });
    },

    getMobileFeePart(context) {
        const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
        const parentAccountNumber = context.getters.payment.parentAccountNumber;
        const billToAccountNumber = 87291; // TODO: MAKE THIS REAL

        return globalMethods.callHttpClient({
            method: endpoints.GetMobileFeePart.method,
            endpoint: `${endpoints.GetMobileFeePart.url}/${damageType}/${parentAccountNumber}/${billToAccountNumber}`,
        });
    },

    getServiceabilityDetails(context, { serviceZipCode }) {
        const lineItemsWithOnlyPartNumbers = context.getters.order.lineItems.supportingItems.map(
            (lineItem) => ({
                partNumber: lineItem.partNumber,
            })
        );
        const lineItems = buildQueryStringParameterFromArrayOfComplexObjects(
            lineItemsWithOnlyPartNumbers,
            "lineItems"
        );

        const vehicle = context.getters.vehicle;
        const carId = vehicle.carId;
        const damage = context.getters.damage;
        const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace);

        const glassPieces = buildQueryStringParameterFromArrayOfComplexObjects(
            glassArray,
            "glassPieces"
        );

        return globalMethods.callHttpClient({
            method: endpoints.GetServiceabilityDetails.method,
            endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&${lineItems}&${glassPieces}`,
        });
    },

    getProviders(context, { serviceZipCode }) {
        const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
        const shopRadiusInMiles = 100;

        return globalMethods.callHttpClient({
            method: endpoints.GetProviders.method,
            endpoint: `${endpoints.GetProviders.url}/${serviceZipCode}/${damageType}/${shopRadiusInMiles}`,
        });
    },

    getSupportingItems(context) {
        const glassPartsArray = context.getters.lineItems.glassParts ?? [];
        const carId = context.getters.vehicle.carId;
        const isRepair = context.getters.damage.isRepair;
        const numberOfChips = context.getters.damage.numberOfChips;

        return globalMethods.callHttpClient({
            method: endpoints.GetSupportingItems.method,
            endpoint: endpoints.GetSupportingItems.url,
            payload: {
                carId: carId,
                damageType: isRepair ? "Repair" : "Replace",
                parentAccountNumber: applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
                parts: glassPartsArray,
                numberOfRepairChips: isRepair ? numberOfChips : 0,
            },
        });
    },

    getCapabilityQuestions(context, { carId, partNumber }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetCapabilityQuestions.method,
            endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}`,
        });
    },

    getPartFromCapabilityQuestionAnswer(context, glassLocation) {
        const pageData = context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);

        const part = pageData.partsOrQuestions.find((x) => x.glassLocation === glassLocation)
            .parts[0];
        const capabilityQuestionAnswers = context.getters.damage.capabilityQuestionAnswers;
        const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find(
            (x) => x.glassLocation === glassLocation
        );

        return globalMethods.callHttpClient({
            method: endpoints.GetPartFromCapabilityAnswer.method,
            endpoint: endpoints.GetPartFromCapabilityAnswer.url,
            payload: {
                part,
                capabilityAnswerResults: capabilityQuestionAnswersForPart,
            },
        });
    },

    getShopTimeSlots(context, { startDate, endDate, shopAppointmentType, providerNumber }) {
        const order = context.state.order;
        const vehicle = context.state.order.vehicle;

        let lineItems = [
            ...(order.lineItems.supportingItems ?? []),
            ...(order.lineItems.vaps ?? []),
            ...getFlattenedArrayOfLineItemsWithChildParts(order.lineItems.glassParts),
        ];
        lineItems = lineItems.map((lineItem) => {
            return {
                partNumber: lineItem.partNumber,
                partType: lineItem.partType,
            };
        });
        const glassPieces = order.damage.glassToReplace
            ? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
            : [];
        var payload = {
            providerNumber: providerNumber,
            startDate: startDate,
            endDate: endDate,
            shopAppointmentType: shopAppointmentType,
            applicationName: applicationConfig.APPLICATION_NAME,
            parentAccountNumber: context.getters.payment.parentAccountNumber,
            carId: vehicle.carId,
            lineItems: lineItems,
            glassPieces: glassPieces,
            eon: order.eon,
            coverage: {
                status: "",
                deductible: 0,
                additionalAuthFlag: "",
            },
            partSelection: {
                hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length,
                hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length,
                hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length,
                hasManuallySelectedParts:
                    !!context.state.applicationUser.pageData["vehicle-parts"]?.partsOrQuestions
                        .length,
            },
            vehicle: {
                year: vehicle.year,
                make: vehicle.make,
                model: vehicle.model,
                style: vehicle.style,
                vin: vehicle.vin ?? "",
            },
        };

        return globalMethods.callHttpClient({
            method: endpoints.GetShopTimeSlots.method,
            endpoint: endpoints.GetShopTimeSlots.url,
            payload: payload,
            additionalSuccessEventDataHandler: (response) =>
                provisionalTriggersToString(response.data.provisionalTriggers),
        });
    },

    getMobileTimeSlots(context, { startDate, endDate }) {
        const order = context.state.order;
        const vehicle = context.state.order.vehicle;
        let lineItems = [
            ...(order.lineItems.supportingItems ?? []),
            ...(order.lineItems.vaps ?? []),
            ...getFlattenedArrayOfLineItemsWithChildParts(order.lineItems.glassParts),
        ];
        lineItems = lineItems.map((lineItem) => {
            return {
                partNumber: lineItem.partNumber,
                partType: lineItem.partType,
            };
        });
        const glassPieces = order.damage.glassToReplace
            ? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
            : [];
        var payload = {
            startDate: startDate,
            endDate: endDate,
            applicationName: applicationConfig.APPLICATION_NAME,
            parentAccountNumber: context.getters.payment.parentAccountNumber,
            carId: vehicle.carId,
            lineItems: lineItems,
            glassPieces: glassPieces,
            eon: order.eon,
            coverage: {
                status: "",
                deductible: 0,
                additionalAuthFlag: "",
            },
            partSelection: {
                hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length,
                hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length,
                hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length,
                hasManuallySelectedParts:
                    !!context.state.applicationUser.pageData["vehicle-parts"]?.partsOrQuestions
                        .length,
            },
            vehicle: {
                year: vehicle.year,
                make: vehicle.make,
                model: vehicle.model,
                style: vehicle.style,
                vin: vehicle.vin ?? "",
            },
            zipCode: order.serviceLocation.zipCode,
        };
        return globalMethods.callHttpClient({
            method: endpoints.GetMobileTimeSlots.method,
            endpoint: endpoints.GetMobileTimeSlots.url,
            payload: payload,
            additionalSuccessEventDataHandler: (response) =>
                provisionalTriggersToString(response.data.provisionalTriggers),
        });
    },

    getMobilePremiumFee(context) {
        const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
        const paymentType = context.getters.order.payment.isInsurance ? "Insurance" : "Cash";

        return globalMethods.callHttpClient({
            method: endpoints.GetMobilePremiumFee.method,
            endpoint: `${endpoints.GetMobilePremiumFee.url}/${paymentType}/${damageType}`,
        });
    },

    // Session API Actions
    saveSession(context) {
        const vehicle = context.getters.vehicle;
        const damage = context.getters.damage;
        const order = context.state.order;
        const applicationUser = context.getters.applicationUser;
        const lineItems = context.state.order.lineItems;

        // create a new array to avoid mutating state
        const newGlassToReplace = convertGlassPieceNamingForApi(damage.glassToReplace);

        return globalMethods.callHttpClient({
            method: endpoints.SaveSession.method,
            endpoint: endpoints.SaveSession.url,
            payload: {
                submitAfterSave: context.state.submitAfterSave,
                applicationUser: {
                    crmCustomerId: applicationUser.crmCustomerId,
                    experiments: applicationUser.experiments,
                    lastPage: applicationUser.lastPageVisited,
                    pageData: applicationUser.pageData,
                    savedSessionId: applicationUser.savedSessionId,
                },
                order: {
                    vehicle: {
                        carId: vehicle.carId,
                        year: vehicle.year,
                        make: vehicle.make,
                        model: vehicle.model,
                        style: vehicle.style,
                        vin: vehicle.vin,
                        registration: {
                            firstName: vehicle.registration.firstName,
                            lastName: vehicle.registration.lastName,
                            streetAddress: vehicle.registration.address,
                            city: vehicle.registration.city,
                            state: vehicle.registration.state,
                            zipCode: vehicle.registration.zipCode,
                            licensePlateNumber: vehicle.registration.licensePlate,
                        },
                    },
                    customer: {
                        emailAddress: order.customer.emailAddress,
                        firstName: order.customer.firstName,
                        lastName: order.customer.lastName,
                        isSmsOptIn: order.customer.isSmsOptIn,
                        servicePhoneNumber: order.customer.phoneNumber,
                    },
                    damage: {
                        numberOfChips: damage.numberOfChips,
                        glassToReplace: newGlassToReplace,
                        isRepair: damage.isRepair,
                        partQuestionAnswers: order.damage.partQuestionAnswers,
                        moldingQuestionAnswers: order.damage.moldingQuestionAnswers,
                        capabilityQuestionAnswers: order.damage.capabilityQuestionAnswers,
                    },
                    lineItems: {
                        glassParts: lineItems.glassParts,
                        supportingItems: lineItems.supportingItems,
                        vaps: lineItems.vaps,
                        serverData: lineItems.serverData,
                    },
                    payment: {
                        InsuranceCoverage: {
                            isVerified: order.payment.insuranceCoverage.isVerified ?? false,
                        },
                        isInsurance: order.payment.isInsurance,
                        parentAccountNumber: order.payment.parentAccountNumber,
                    },
                    serviceLocation: {
                        streetAddress: order.serviceLocation.address,
                        streetAddress2: order.serviceLocation.address2,
                        city: order.serviceLocation.city,
                        state: order.serviceLocation.state,
                        zipCode: order.serviceLocation.zipCode,
                        zipCodeCtu: order.serviceLocation.zipCodeCtu,
                        appointmentType: order.serviceLocation.appointmentType,
                        isVehicleProtected: order.serviceLocation.isVehicleProtected,
                        provider: {
                            providerNumber: order.serviceLocation.provider?.providerNumber,
                            address: {
                                streetAddress:
                                    order.serviceLocation.provider?.address?.streetAddress,
                                city: order.serviceLocation.provider?.address?.city,
                                state: order.serviceLocation.provider?.address?.state,
                                zipCode: order.serviceLocation.provider?.address?.zipCode,
                                zipCodeCtu: order.serviceLocation.provider?.address?.zipCodeCtu,
                            },
                        },
                        techNotes: order.serviceLocation.techNotes,
                    },
                    schedule: {
                        date: order.schedule?.date,
                        startTime: order.schedule?.startTime,
                        endTime: order.schedule?.endTime,
                        routeCode: order.schedule?.routeCode,
                        jobMaxMinutes: order.schedule?.jobMaxMinutes,
                        jobMinMinutes: order.schedule?.jobMinMinutes,
                    },
                    existingPromoCode: null,
                    referralCorrelationId: order.referralCorrelationId,
                    referralDate: order.referralDate,
                    referralNumber: order.referralNumber?.toString(),
                    referralSequenceNumber: order.referralSequenceNumber,
                    eon: order.eon,
                },
            },
            additionalSuccessEventDataHandler: (response) =>
                "Email provided: " + (order.customer.emailAddress ? "true" : "false"),
        });
    },

    loadSession(
        context,
        {
            savedSessionId,
            referralNumber,
            referralDate,
            parentAccountNumber,
            referralCorrelationId,
            isConceptInsurance,
        }
    ) {
        const order = context.state.order;

        return globalMethods
            .callHttpClient({
                method: endpoints.LoadSession.method,
                endpoint: endpoints.LoadSession.url,
                payload: {
                    savedSessionId: savedSessionId?.toString(),
                    referralNumber: referralNumber?.toString(),
                    referralDate: referralDate?.toString(),
                    parentAccountNumber: parentAccountNumber,
                    referralCorrelationId: referralCorrelationId,
                },
            })
            .then(
                async (response) => {
                    // Flatten location and name properties
                    response.data.order.damage?.glassToReplace?.map((glass) => {
                        glass.glassLocation = glass.location;
                        glass.glassName = glass.name;
                        delete glass.location;
                        delete glass.name;
                        return glass;
                    });

                    // clear the state if the existing EON does not equal what is returned from loadSession
                    if (context.state.order.eon && context.state.order.eon != response.data.eon) {
                        context.commit(storeMutations.RESET_STATE);
                    }

                    context.commit(
                        storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION,
                        response.data
                    );

                    await resetScheduleIfUnavailable(context, response.data.order);
                    return response;
                },
                (error) => {
                    deleteFunnelCookie();
                    context.commit(storeMutations.RESET_STATE);
                }
            );
    },

    saveWorkOrderFlag(context, submitAfterSave) {
        context.commit(storeMutations.UPDATE_WORK_ORDER_FLAG, submitAfterSave);
    },

    // Business domain actions

    // Vehicle
    saveVehicle(
        context,
        { year, make, model, style, carId, category, imageUrl, imageVifNumber, imageVifColor }
    ) {
        context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
        context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);

        if (context.state.order.vehicle.year !== year) {
            context.commit(storeMutations.UPDATE_YEAR, year);
        }
        if (context.state.order.vehicle.make !== make) {
            context.commit(storeMutations.UPDATE_MAKE, make);
        }
        if (context.state.order.vehicle.model !== model) {
            context.commit(storeMutations.UPDATE_MODEL, model);
        }
        if (context.state.order.vehicle.style !== style) {
            context.commit(storeMutations.UPDATE_STYLE, style);
        }
        context.commit(storeMutations.UPDATE_CAR_ID, carId);
        context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, category);
        context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, imageUrl);
        context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, imageVifNumber);
        context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, imageVifColor);
    },

    saveVehicleDamage(
        context,
        { isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount }
    ) {
        const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort();
        const isGlassToReplaceTheSame =
            context.state.order.damage.glassToReplace?.length === selectedGlassToReplace.length &&
            context.state.order.damage.glassToReplace
                .slice()
                .sort()
                .every(
                    (obj, index) =>
                        obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation &&
                        obj.glassName === selectedGlassPassedInSorted[index].glassName
                );
        const isWindshieldRepairTheSame =
            isWindshieldRepair === context.state.order.damage.isRepair;

        const isChipCountTheSame =
            selectedWindshieldChipCount === context.state.order.damage.numberOfChips;

        const isDamageChanging =
            !isGlassToReplaceTheSame ||
            !isWindshieldRepairTheSame ||
            (isWindshieldRepair && !isChipCountTheSame);

        if (isDamageChanging) {
            //Reset dependent state when changing
            context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
            context.commit(storeMutations.UPDATE_VAPS, null);

            // Save new values
            context.commit(storeMutations.UPDATE_IS_REPAIR, isWindshieldRepair);
            context.commit(
                storeMutations.UPDATE_NUMBER_OF_CHIPS,
                isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null
            );
            context.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, selectedGlassToReplace);
        }
    },

    // Vin lookup
    saveVinLookup(context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) {
        //Reset dependent state when changing
        if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
            context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
            context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);

            if (!isSelectedGlassAvailableForVehicle) {
                context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
            }

            //Save new values
            context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);
            context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo);
        }
    },

    saveRegistrationLicensePlateLookup(
        context,
        { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }
    ) {
        //Reset dependent state when changing
        if (
            registrationInfo?.licensePlate !==
            context.state.order.vehicle.registration?.licensePlate
        ) {
            context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);

            if (!isSelectedGlassAvailableForVehicle) {
                context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
            }

            //Save new values
            context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);
            context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo);
        }
    },

    saveRegistrationAddressLookup(
        context,
        { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }
    ) {
        //Reset dependent state when changing
        if (
            registrationInfo?.firstName !== context.state.order.customer?.firstName ||
            registrationInfo?.lastName !== context.state.order.customer?.lastName
        ) {
            context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);

            if (!isSelectedGlassAvailableForVehicle) {
                context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
            }

            //Save new values
            context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);
            context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo);
        }
    },

    savePartQuestionAnswers(context, partQuestionAnswersArray) {
        // if part question answers have changed, reset subsequent question answers
        const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
            context.getters.damage.partQuestionAnswers,
            "result"
        );
        const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
            partQuestionAnswersArray,
            "result"
        );
        const havePartQuestionAnswersChanged =
            sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length ||
            !sortedPreviousResultsArray?.every(
                (x, i) => x.result === sortedPartQuestionAnswersArray[i].result
            );

        if (havePartQuestionAnswersChanged) {
            context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);

            context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
            context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
            context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
            context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
            context.commit(storeMutations.UPDATE_PAGE_DATA, {
                page: fmgPageValues.VEHICLE_PARTS,
                data: null,
            });
            context.commit(storeMutations.UPDATE_PAGE_DATA, {
                page: fmgPageValues.MOLDING_QUESTIONS,
                data: null,
            });
            context.commit(storeMutations.UPDATE_PAGE_DATA, {
                page: fmgPageValues.CAPABILITY_QUESTIONS,
                data: null,
            });
        }

        //Save new values
        context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray);
    },

    resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, matchedParts) {
        const partsOrQuestionsDataToCompareWith =
            context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ??
            context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)?.partsOrQuestions ??
            [];

        function getAllPartNumbers(partsOrQuestions) {
            return partsOrQuestions[0]?.parts
                ? [...partsOrQuestions]
                      .map((glass) => glass.parts)
                      .flat()
                      .map((part) => part.partNumber)
                      .filter((partNumber) => !partNumber.toUpperCase().includes("FEE"))
                      .sort()
                      .join(",")
                : [];
        }

        const previouslySelectedPartNumbers = getAllPartNumbers(partsOrQuestionsDataToCompareWith);
        const currentlySelectedPartNumbers = getAllPartNumbers(matchedParts);

        const haveSelectedVehiclePartsChanged =
            previouslySelectedPartNumbers !== currentlySelectedPartNumbers;

        if (haveSelectedVehiclePartsChanged) {
            context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);

            context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
            context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
            context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
            context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
            context.commit(storeMutations.UPDATE_PAGE_DATA, {
                page: fmgPageValues.MOLDING_QUESTIONS,
                data: null,
            });
            context.commit(storeMutations.UPDATE_PAGE_DATA, {
                page: fmgPageValues.CAPABILITY_QUESTIONS,
                data: null,
            });
        }
    },

    saveMoldingQuestionAnswers(context, moldingQuestionAnswers) {
        const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
            context.getters.damage.moldingQuestionAnswers,
            "partNum"
        );
        const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
            moldingQuestionAnswers,
            "partNum"
        );
        const haveMoldingQuestionAnswersChanged =
            sortedPreviousResultsArray?.length !== sortedMoldingQuestionAnswersArray.length ||
            !sortedPreviousResultsArray?.every(
                (x, i) => x.partNum === sortedMoldingQuestionAnswersArray[i].partNum
            );

        if (haveMoldingQuestionAnswersChanged) {
            context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);

            context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
            context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
            context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
            context.commit(storeMutations.UPDATE_PAGE_DATA, {
                page: fmgPageValues.CAPABILITY_QUESTIONS,
                data: null,
            });
        }

        //Save new values
        context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, moldingQuestionAnswers);
    },

    saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) {
        const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
            context.getters.damage.capabilityQuestionAnswers,
            "result"
        );
        const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
            capabilityQuestionAnswers,
            "result"
        );
        const haveCapabilityQuestionAnswersChanged =
            sortedPreviousResultsArray?.length !== sortedCapabilityQuestionAnswersArray.length ||
            !sortedPreviousResultsArray?.every(
                (x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result
            );

        if (haveCapabilityQuestionAnswersChanged) {
            context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);

            context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
            context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
        }

        //Save new values
        context.commit(
            storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS,
            capabilityQuestionAnswers
        );
    },

    savePaymentType(context, isInsurance) {
        context.commit(storeMutations.UPDATE_IS_INSURANCE, isInsurance);
    },

    saveParentAccountNumber(context, parentAccountNumber) {
        context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber);
    },

    saveSupportingItems(context, supportingItems) {
        if (!deepEqual(supportingItems, context.state.order.lineItems.supportingItems)) {
            context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
        }

        context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
    },

    saveSupportingItemsSuppressingStateResetting(context, supportingItems) {
        context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
    },

    saveVaps(context, vaps) {
        context.commit(storeMutations.UPDATE_VAPS, vaps);
    },

    // Price order actions
    async priceOrderItemsAndSaveServerData(
        context,
        { availableLineItems, serviceZipCode, serviceZipCodeCtu }
    ) {
        const zipCodeToUse = serviceZipCode
            ? serviceZipCode
            : context.getters.order.serviceLocation.zipCode;
        const ctuToUse = serviceZipCodeCtu
            ? serviceZipCodeCtu
            : context.getters.order.serviceLocation.zipCodeCtu;
        const flattenedLineItemsWithChildParts =
            getFlattenedArrayOfLineItemsWithChildParts(availableLineItems);
        const lineItemsWithOnlyPartNumbers = flattenedLineItemsWithChildParts.map((lineItem) => ({
            partNumber: lineItem.partNumber,
        }));
        const availableLineItemsFormattedForRequest =
            buildQueryStringParameterFromArrayOfComplexObjects(
                lineItemsWithOnlyPartNumbers,
                "lineItems"
            );

        const vehicle = context.getters.order.vehicle;

        let queryString =
            `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` +
            `&CTU=${ctuToUse}` +
            `&CarId=${vehicle.carId}` +
            `&Make=${vehicle.make}` +
            `&Model=${vehicle.model}` +
            `&Year=${vehicle.year}` +
            `&EON=${context.getters.order.eon}` +
            `&ZipCode=${zipCodeToUse}` +
            `&${availableLineItemsFormattedForRequest}`;

        const lineItemServerData = context.getters.order.lineItems.serverData;
        if (lineItemServerData) {
            queryString += `&ServerData=${encodeURIComponent(lineItemServerData)}`;
        }

        const response = await globalMethods.callHttpClient({
            method: endpoints.PriceOrderItems.method,
            endpoint: `${endpoints.PriceOrderItems.url}?${queryString}`,
        });

        context.commit(storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA, response.data.serverData);

        availableLineItems = addPricesToLineItems(availableLineItems, response.data.lineItems);

        return availableLineItems;
    },

    // Misc order actions
    saveSchedule(context, scheduleInfo) {
        context.commit(storeMutations.UPDATE_SCHEDULE, scheduleInfo);
    },

    saveCustomerDetails(context, customerDetails) {
        context.commit(storeMutations.UPDATE_CUSTOMER_DETAILS, customerDetails);
    },

    saveServiceZipCodeInfo(context, serviceZipCodeInfo) {
        if (
            context.state.order.serviceLocation &&
            serviceZipCodeInfo.zipCode !== context.state.order.serviceLocation.zipCode
        ) {
            context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);

            context.commit(storeMutations.RESET_SERVICE_LOCATION_MOBILE_ADDRESS);
        }

        context.commit(storeMutations.UPDATE_SERVICE_ZIP, serviceZipCodeInfo);
    },

    saveServiceLocation(context, serviceLocationInfo) {
        if (context.state.order.serviceLocation) {
            if (
                serviceLocationInfo.zipCode !== context.state.order.serviceLocation.zipCode ||
                !providersEqual(
                    serviceLocationInfo.provider,
                    context.state.order.serviceLocation.provider
                ) ||
                serviceLocationInfo.appointmentType !==
                    context.state.order.serviceLocation.appointmentType
            ) {
                context.commit(storeMutations.RESET_SCHEDULE);
            }
        }

        context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo);
    },

    saveServiceLocationTechNotes(context, techNotesInfo) {
        context.commit(storeMutations.UPDATE_SERVICE_LOCATION_TECH_NOTES, techNotesInfo);
    },

    saveEmail(context, email) {
        context.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, email === "" ? null : email);
    },

    saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) {
        //Reset dependent state when changing
        if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
            if (!isSelectedGlassAvailableForVehicle) {
                context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
            }

            //Save new values
            context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);
        }
    },

    saveGlassParts(context, parts) {
        if (!deepEqual(parts, context.state.order.lineItems.glassParts)) {
            context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
        }

        context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
    },

    saveGlassPartPrices(context, parts) {
        context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
    },

    clearVin(context) {
        context.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
    },

    isVinOptionalVehicle(context) {
        //Optional for carIds with only a single windshield
        if (
            singleWindshieldCarIds.find((item) => item === context.state.order.vehicle.carId) &&
            context.state.order.damage.glassToReplace.length == 1 &&
            context.state.order.damage.glassToReplace.find(
                (glassToReplace) =>
                    glassToReplace.glassLocation.toLowerCase() ===
                    damageLocationsSelected.WINDSHIELD.toLowerCase()
            )
        ) {
            return true;
        }
        //Optional for specific YMMSs
        switch (context.state.order.vehicle.make.toLowerCase()) {
            case "mercedes benz":
            case "volkswagen":
            case "audi":
            case "porsche":
                return true;
            default:
        }
        if (
            context.state.order.vehicle.make.toLowerCase() === "bmw" &&
            context.state.order.vehicle.year <= 2017
        )
            return true;

        return false;
    },
};

export default createStore({
    plugins: [createPersistedState()],
    // IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons:
    //    * The CMS can reference the fields by name
    //    * Return users may have a previous "version" of the model, and we don't want
    //      them to have a breaking experience, because the model might have changed.
    state,
    mutations,
    getters,
    actions,
});

// Private Functions

function getHasRecalibrationPart(state) {
    var hasRequiresRecalibration =
        getNonFalseValuesOfPropertyInArrayOfObjects(
            state.order.lineItems.glassParts,
            "requiresRecalibration"
        )?.length > 0;
    var hasRecalibrationType =
        getNonFalseValuesOfPropertyInArrayOfObjects(
            state.order.lineItems.glassParts,
            "recalibrationType"
        )?.length > 0;

    if (hasRequiresRecalibration) {
        if (hasRecalibrationType) {
            // Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType'
            return (
                getNonFalseValuesOfPropertyInArrayOfObjects(
                    state.order.lineItems.glassParts,
                    "recalibrationType"
                )[0].toLowerCase() != "unknown"
            );
        } else {
            // Has 'requiresRecalibration' but no 'recalibrationType' at all
            return true;
        }
    } else {
        // Does not have 'requiresRecalibration'
        return false;
    }
}

function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) {
    if (!arrayOfObjects) return null;

    return arrayOfObjects.sort((a, b) => {
        if (a[propertyName] < b[propertyName]) return -1;
        else if (a[propertyName] > b[propertyName]) return 1;
        else return 0;
    });
}

function convertGlassPieceNamingForApi(glassArray) {
    if (!glassArray || glassArray.length === 0) return [];

    // check if array already converted.  (likely when a session has been saved previously and then reloaded)
    if (glassArray[0].location !== undefined) {
        return glassArray;
    }

    const converted = [];
    glassArray.forEach((glass) => {
        converted.push({
            location: glass.glassLocation,
            name: glass.glassName,
        });
    });
    return converted;
}

function convertResultsForApi(resultsArray) {
    if (!resultsArray) return [];
    const converted = [];
    resultsArray.forEach((answer) => {
        converted.push({
            location: answer.glassLocation,
            name: answer.glassName,
            result: answer.result,
        });
    });
    return converted;
}

function convertGlassPieceNamingFromApi(glassArray) {
    glassArray.forEach((glass) => {
        glass.glassLocation = glass.glassPiece.location;
        glass.glassName = glass.glassPiece.name;
        delete glass.glassPiece;
        return glass;
    });
    return glassArray;
}

function addPricesToLineItems(lineItems, pricingLineItems) {
    lineItems.forEach((lineItem) => {
        let lineItemIndex = pricingLineItems.findIndex(
            (pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber
        );
        if (lineItem.childParts) {
            addPricesToLineItems(lineItem.childParts, pricingLineItems);
        }
        let pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0];
        lineItem.laborAmount = pricedLineItem.laborAmount;
        lineItem.sellingPrice = pricedLineItem.sellingPrice;
        lineItem.kitPrice = pricedLineItem.kitPrice;
    });
    return lineItems;
}

function getFlattenedArrayOfLineItemsWithChildParts(lineItems) {
    let flattenedArray = [];
    lineItems?.forEach((lineItem) => {
        flattenedArray.push(lineItem);
        if (lineItem.childParts) {
            flattenedArray = [
                ...flattenedArray,
                ...getFlattenedArrayOfLineItemsWithChildParts(lineItem.childParts),
            ];
        }
    });
    return flattenedArray;
}

function buildQueryStringParameterFromArrayOfComplexObjects(arrayOfObjects, parameterName) {
    let queryStringParameter = "";
    for (let i = 0; i < arrayOfObjects.length; i++) {
        for (const [key, value] of Object.entries(arrayOfObjects[i])) {
            queryStringParameter += `${parameterName}[${i}].${key}=${value}&`;
        }
    }
    // Remove trailing &
    return queryStringParameter.slice(0, -1);
}

function convertGlassPieceToBackEndCompatibleFormat(glassPieces) {
    return glassPieces.map((glassPiece) => {
        return {
            location: glassPiece.glassLocation,
            name: glassPiece.glassName,
        };
    });
}

function providersEqual(providerA, providerB) {
    return (
        providerA.providerNumber === providerB.providerNumber &&
        providerA.address?.city === providerB.address?.city &&
        providerA.address?.state === providerB.address?.state &&
        providerA.address?.streetAddress === providerB.address?.streetAddress &&
        providerA.address?.zipCode === providerB.address?.zipCode
    );
    //TODO: Change back to deepEqual once zipCodeCtu is added to saveSession.
}

// This function will verify schedule info is still valid.
// check to see if we have an appointment date on the order object.
// if so, make sure it's not in the past. if in the past, clear schedule info in store.
// if date not in past, then call schedule service to verify appointment is still available.
async function resetScheduleIfUnavailable(context, order) {
    if (!order.schedule?.date) {
        return;
    }

    // Date string with slashes is parsed as local time, not UTC. Our date has dashes, '-'.
    // If you put any kind of time stamp on the date string with dashes, then it IS parsed as local time.
    var aptDate = new Date(order.schedule.date + "T00:00:00");
    var curDate = new Date();
    curDate.setHours(0, 0, 0, 0);

    // if appointment date is in the past, clear schedule
    if (aptDate.getTime() < curDate.getTime()) {
        context.commit(storeMutations.RESET_SCHEDULE);
        return;
    }

    // create date range to pass to the schedule service to see if our appointment is still available.
    var endRange = new Date(aptDate);
    endRange.setDate(aptDate.getDate() + 1);

    var endDay = "" + endRange.getDate();
    var endMonth = "" + (endRange.getMonth() + 1); // 0 based so add 1
    const endYear = endRange.getFullYear();

    if (endMonth.length < 2) {
        endMonth = "0" + endMonth;
    }
    if (endDay.length < 2) {
        endDay = "0" + endDay;
    }

    const endDate = [endYear, endMonth, endDay].join("-");

    let newTimeSlotsResponse;
    if (order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE) {
        newTimeSlotsResponse = await context.dispatch(
            storeActions.GET_MOBILE_TIME_SLOTS,
            {
                startDate: order.schedule.date,
                endDate: endDate,
            },
            false
        );

        if (newTimeSlotsResponse?.data.days?.length === 0) {
            context.commit(storeMutations.RESET_SCHEDULE);
            return;
        }

        var mobileRouteCodeFound = false;
        // if early bird fee is in supporting items then need to check the timeslot to see if offer premium is also still available
        if (
            order.lineItem?.supportingItems?.findIndex(
                (item) => item.partType == PREMIUM_FEE_PART_TYPE
            )
        ) {
            newTimeSlotsResponse.data.days?.forEach((day) => {
                day.timeSlots.forEach((ts) => {
                    if (ts.id === order.schedule.routeCode && ts.offerPremium) {
                        mobileRouteCodeFound = true;
                    }
                });
            });
        } else {
            newTimeSlotsResponse.data.days?.forEach((day) => {
                day.timeSlots.forEach((ts) => {
                    if (ts.id === order.schedule.routeCode) {
                        mobileRouteCodeFound = true;
                    }
                });
            });
        }

        if (!mobileRouteCodeFound) {
            context.commit(storeMutations.RESET_SCHEDULE);
            return;
        }
    } else {
        newTimeSlotsResponse = await context.dispatch(
            storeActions.GET_SHOP_TIME_SLOTS,
            {
                startDate: order.schedule.date,
                endDate: endDate,
                shopAppointmentType: order.serviceLocation.appointmentType,
                providerNumber: order.serviceLocation.provider.providerNumber,
            },
            false
        );

        if (newTimeSlotsResponse?.data.days?.length === 0) {
            context.commit(storeMutations.RESET_SCHEDULE);
            return;
        }

        var routeCodeFound = false;
        newTimeSlotsResponse.data.days?.forEach((day) => {
            day.timeSlots.forEach((ts) => {
                if (ts.id === order.schedule.routeCode) {
                    routeCodeFound = true;
                }
            });
        });

        if (!routeCodeFound) {
            context.commit(storeMutations.RESET_SCHEDULE);
            return;
        }
    }
}
