import { createStore } from "vuex";
import { endpoints } from "@/constants/endpoints.js";
import { storeMutations } from "@/constants/store-mutations";
import { sessionStorageKeyConstants } from "@/constants/session-storage.js";
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 baseMixin from "@/mixins/base-mixin.js";
import {
    AppointmentTypeStrings,
    PREMIUM_FEE_PART_TYPE,
    RouteCodeFlags,
} from "@/constants/schedule-constants";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { deepClone } from "@/helpers/object-helper";
import {
    getExperimentSettingsFromExperimentList,
    hasSettingEqualTo,
} from "../helpers/experiment-helper";
import { queryStrings } from "@/constants/query-strings";
import { partTypeStrings } from "@/constants/part-type-strings";
import {
    convertDateStringToDate,
    militaryToTwelveHourTime,
} from "@/layouts/schedule/helpers/schedule-helper";
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
import { paymentMethods } from "@/constants/payment-method-constants";
import {
    getPromoCodeWithoutBundleIdentifier,
    removeCurrentlyActivePromoCodesFromInactivePromos,
} from "@/helpers/promotions-helper";
import { getDateDifferenceInDays } from "@/helpers/date-helper";
import sharedMutations from "./vuex-shared-mutations/vuexSharedMutations";
import {
    coverageStatus,
    coverageStatusValue,
    coverageStatusEnum,
    coverageType,
    coverageTypeValue,
    coverageTypeEnum,
} from "@/constants/insurance";
import {
    containsRecalParts,
    getTopLevelPartsWithRecal,
    isRecalPartOrHasChildRecalPart,
    getRecalPartNumbers,
} from "@/helpers/recal-helper";
import { externalParameterStatus } from "@/constants/external-parameters";
import { experimentSettings } from "@/constants/experiments";
import experimentMixin from "@/mixins/experiment-mixin.js";

// Export State
const getDefaultState = () => {
    return {
        order: {
            vehicle: {
                year: null,
                make: null,
                model: null,
                style: null,
                vehicleSubType: null,
                vehicleSpecialClass: null,
                isBigTruck: false,
                carId: null,
                category: null,
                vin: null,
                canSafeliteService: 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,
                waitListRequested: null,
            },
            damage: {
                isRepair: null,
                numberOfChips: null,
                glassToReplace: null,
                partQuestionAnswers: null,
                moldingQuestionAnswers: null,
                capabilityQuestionAnswers: null,
                dateOfLoss: null,
                damageCause: null,
            },
            lineItems: {
                glassParts: null,
                supportingItems: null,
                vaps: null,
                serverData: null,
                promos: null,
            },
            payment: {
                isInsurance: null,
                insuranceCoverage: {
                    isVerified: null,
                    coverageStatus: null,
                    coverageSubStatus: null,
                    coverageType: null,
                    coverageVerificationType: null,
                },
                parentAccountNumber: 0,
                billToAccountNumber: null,
                isPia: null,
                piaType: null,
                inactivePromos: null,
                paypalToken: null,
                nextGenSettledAmount: 0,
                ccToken: {
                    subscriptionId: null,
                    expMonth: null,
                    expYear: null,
                    cardType: null,
                    billToPostalCode: null,
                    billToFirstName: null,
                    billToLastName: null,
                    referenceNumber: null,
                    authCode: null,
                    transactionId: null,
                    transReferenceNumber: null,
                    lastFour: null,
                },
            },
            policy: {
                currentDeductible: 0,
                originalDeductible: 0,
                policyNumber: null,
                isItac: false,
                additionalAuthFlag: null,
                isNoComp: false,
                insuranceCompanyName: null,
            },
            schedule: {
                date: null,
                startTime: null,
                endTime: null,
                routeCode: null,
                jobMaxMinutes: null,
                jobMinMinutes: null,
            },
            referralNumber: null,
            referralSequenceNumber: null,
            referralDate: null,
            referralCorrelationId: null,
            eon: null,
            workOrderNumber: null,
            workOrderId: null,
            customerPortalLoginToken: null,
            lockToken: null,
            settledTenderAmount: 0,
            isRecalAckOptIn: false,
            isMSRFeeApplicable: false,
        },
        applicationUser: {
            eventBus: [],
            pageData: {},
            savedSessionTimeout: getDateForSavedSessionTimeout(),
            saveSessionPromise: null,
            savedSessionId: null,
            crmCustomerId: null,
            lastPageVisited: null,
            experiments: [],
            triggeredSiteEntry: false,
            affiliateCookies: [],
            loggingOption: false,
        },
    };
};

export const state = getDefaultState();
export const externalParameterState = getExternalParameterDefaultState();
// Export Mutations
export const mutations = {
    // 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;
    },
    updateVehicleSubType(state, vehicleSubType) {
        state.order.vehicle.vehicleSubType = vehicleSubType;
    },
    updateVehicleSpecialClass(state, vehicleSpecialClass) {
        state.order.vehicle.vehicleSpecialClass = vehicleSpecialClass;
    },
    updateIsBigTruck(state, isBigTruck) {
        state.order.vehicle.isBigTruck = isBigTruck;
    },
    updateCarId(state, carId) {
        state.order.vehicle.carId = carId;
    },
    updateVehicleCategory(state, category) {
        state.order.vehicle.category = category;
    },
    updateVehicleCanSafeliteService(state, canSafeliteService) {
        state.order.vehicle.canSafeliteService = canSafeliteService;
    },
    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;
    },
    updateInactivePromos(state, inactivePromos) {
        state.order.payment.inactivePromos = inactivePromos;
    },
    updatePromos(state, promos) {
        state.order.lineItems.promos = promos;
    },
    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;
    },
    updateBillToAcctNumber(state, billToAcctNumber) {
        state.order.payment.billToAccountNumber = billToAcctNumber;
    },
    updateEON(state, eon) {
        state.order.eon = eon;
    },
    updateIsInsurance(state, isInsurance) {
        state.order.payment.isInsurance = isInsurance;
    },
    updateIsPia(state, isPia) {
        state.order.payment.isPia = isPia;
    },
    updatePiaType(state, piaType) {
        state.order.payment.piaType = piaType;
    },
    updateWorkOrderNumber(state, workOrderNumber) {
        state.order.workOrderNumber = workOrderNumber;
    },
    updateWorkOrderId(state, workOrderId) {
        state.order.workOrderId = workOrderId;
    },
    updateCustomerPortalLoginToken(state, customerPortalLoginToken) {
        state.order.customerPortalLoginToken = customerPortalLoginToken;
    },
    updateLockToken(state, lockToken) {
        state.order.lockToken = lockToken;
    },
    updateSettledTenderAmount(state, settledTenderAmount) {
        state.order.settledTenderAmount = settledTenderAmount;
    },
    updateIsRecalAckOptIn(state, isRecalAckOptIn) {
        state.order.isRecalAckOptIn = isRecalAckOptIn;
    },
    updateIsMSRFeeApplicable(state, isMSRFeeApplicable) {
        state.order.isMSRFeeApplicable = isMSRFeeApplicable;
    },
    updateCCToken(state, ccToken) {
        state.order.payment.ccToken.subscriptionId = ccToken.subscriptionId;
        state.order.payment.ccToken.expMonth = ccToken.expMonth;
        state.order.payment.ccToken.expYear = ccToken.expYear;
        state.order.payment.ccToken.cardType = ccToken.cardType;
        state.order.payment.ccToken.billToPostalCode = ccToken.billToPostalCode;
        state.order.payment.ccToken.billToFirstName = ccToken.billToFirstName;
        state.order.payment.ccToken.billToLastName = ccToken.billToLastName;
        state.order.payment.ccToken.referenceNumber = ccToken.referenceNumber;
        state.order.payment.ccToken.authCode = ccToken.authCode;
        state.order.payment.ccToken.transactionId = ccToken.transactionId;
        state.order.payment.ccToken.transReferenceNumber = ccToken.transReferenceNumber;
        state.order.payment.ccToken.lastFour = ccToken.lastFour;
    },
    updatePaypalToken(state, ppToken) {
        state.order.payment.paypalToken = ppToken;
    },
    updateNextGenSettledAmount(state, nextGenSettledAmount) {
        state.order.payment.nextGenSettledAmount = nextGenSettledAmount;
    },
    updateInsuranceVerifiedStatus(state, isVerified) {
        state.order.payment.insuranceCoverage.isVerified = isVerified;
    },
    updateCustomerEmailAddress(state, customerEmailAddress) {
        state.order.customer.emailAddress = customerEmailAddress;
    },
    updateCustomerPhoneNumber(state, phoneNumber) {
        state.order.customer.phoneNumber = phoneNumber;
    },
    updateCustomerIsSmsOptin(state, isSmsOptIn) {
        state.order.customer.isSmsOptIn = isSmsOptIn;
    },
    updateCustomerWaitListRequested(state, waitListRequested) {
        state.order.customer.waitListRequested = waitListRequested;
    },
    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;
    },
    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;
    },
    updateLoggingOption(state, loggingOption) {
        state.applicationUser.loggingOption = loggingOption;
    },
    // 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);
        }
    },
    //ExternalParameter MUTATIONS
    updateIsExternalParameter(state, isExternalParameter) {
        externalParameterState.isExternalParameter = isExternalParameter;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterYear(state, year) {
        externalParameterState.vehicle.year = year;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterMake(state, make) {
        externalParameterState.vehicle.make = make;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterModel(state, model) {
        externalParameterState.vehicle.model = model;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterStyle(state, style) {
        externalParameterState.vehicle.style = style;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterIsRepair(state, isRepair) {
        externalParameterState.vehicleDamage.isRepair = isRepair;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterNumberOfChips(state, numberOfChips) {
        externalParameterState.vehicleDamage.numberOfChips = numberOfChips;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterDamageType(state, damageType) {
        externalParameterState.vehicleDamage.damageType = damageType;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterZipCode(state, zipCode) {
        externalParameterState.serviceZip.zipCode = zipCode;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterEmailAddress(state, emailAddress) {
        externalParameterState.customer.emailAddress = emailAddress;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterIsInsurance(state, isInsurance) {
        externalParameterState.quote.isInsurance = isInsurance;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterVinSelection(state, vinSelection) {
        externalParameterState.estimate.vinSelection = vinSelection;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterServicePackage(state, servicePackage) {
        externalParameterState.quote.servicePackage = servicePackage;
        saveExternalParameterState(externalParameterState);
    },
    updateExternalParameterPhoneNumber(state, phoneNumber) {
        externalParameterState.customer.phoneNumber = phoneNumber;
        saveExternalParameterState(externalParameterState);
    },
    //RESET ExternalParameter MUTATIONS
    resetExternalParameterVehicleState(state) {
        externalParameterState.vehicle.year = null;
        externalParameterState.vehicle.make = null;
        externalParameterState.vehicle.model = null;
        externalParameterState.vehicle.style = null;

        saveExternalParameterState(externalParameterState);
    },
    resetExternalParameterDamageState(state) {
        externalParameterState.vehicleDamage.isRepair = null;
        externalParameterState.vehicleDamage.numberOfChips = null;
        externalParameterState.vehicleDamage.damageType = null;

        saveExternalParameterState(externalParameterState);
    },
    resetExternalParameterEstimateState(state) {
        externalParameterState.estimate.vinSelection = null;
        saveExternalParameterState(externalParameterState);
    },
    resetExternalParameterServiceZipState(state) {
        externalParameterState.serviceZip.zipCode = null;
        saveExternalParameterState(externalParameterState);
    },
    resetExternalParameterQuoteState(state) {
        externalParameterState.quote.servicePackage = null;
        externalParameterState.quote.isInsurance = null;
        saveExternalParameterState(externalParameterState);
    },
    resetIsExternalParameter(state) {
        externalParameterState.isExternalParameter = externalParameterStatus.INACTIVE;
        saveExternalParameterState(externalParameterState);
    },
    resetExternalParameterCustomerState(state) {
        externalParameterState.customer.emailAddress = null;
        externalParameterState.customer.phoneNumber = null;
        saveExternalParameterState(externalParameterState);
    },

    // 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;
    },
    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;
    },
    resetPaymentMethodChoice(state) {
        state.order.payment.isPia = null;
        state.order.payment.piaType = 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;
        state.order.customerPortalLoginToken = sessionInformation.order.CustomerPortalLoginToken;
        state.order.isRecalAckOptIn = sessionInformation.order.isRecalAckOptIn;

        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,
            vehicleSubType: sessionInformation.order.vehicle?.vehicleSubType,
            vehicleSpecialClass: sessionInformation.order.vehicle?.vehicleSpecialClass,
            isBigTruck: sessionInformation.order.vehicle?.isBigTruck,
        });

        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.dateOfLoss = sessionInformation.order.damage?.dateOfLoss;
        state.order.damage.damageCause = sessionInformation.order.damage?.damageCause;

        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.promos = sessionInformation.order.lineItems.promos;
        state.order.lineItems.serverData = sessionInformation.order.lineItems.serverData;

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

        //Do not assign default service type once user make service type selection
        if (state.order.serviceLocation.appointmentType == null) {
            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;
        }

        // if we're loading a session and we do not have a provider number yet, then set isInsurance to null so that
        // a service package is not selected by default on the quote page.
        if (
            (sessionInformation.order.payment.isInsurance === null ||
                sessionInformation.order.payment.isInsurance === undefined) &&
            !sessionInformation.order.serviceLocation.provider?.providerNumber
        ) {
            state.order.payment.isInsurance = null;
        } else {
            state.order.payment.isInsurance = sessionInformation.order.payment.isInsurance;
        }

        state.order.payment.insuranceCoverage.isVerified =
            coverageStatusValue(sessionInformation?.order.insuranceCoverage.coverageStatus) ===
            coverageStatus.VERIFIED
                ? true
                : false;
        state.order.payment.insuranceCoverage.coverageStatus = coverageStatusValue(
            sessionInformation?.order.insuranceCoverage.coverageStatus
        );
        state.order.payment.insuranceCoverage.coverageSubStatus =
            sessionInformation?.order.insuranceCoverage.coverageSubStatus;
        state.order.payment.insuranceCoverage.coverageType = coverageTypeValue(
            sessionInformation?.order.insuranceCoverage.coverageType
        );
        state.order.payment.insuranceCoverage.coverageVerificationType =
            sessionInformation?.order.insuranceCoverage.coverageVerificationType;

        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.homePhone;
        state.order.customer.isSmsOptIn = sessionInformation.order.customer.isSmsOptIn;

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

        if (sessionInformation.applicationUser.savedSessionId) {
            state.applicationUser.savedSessionId =
                sessionInformation.applicationUser.savedSessionId;
        }
        //Do not assign default schedule details once user make schedule selection
        if (state.order.schedule.date == null) {
            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;
        }
        state.order.policy.currentDeductible = sessionInformation.order.policy?.currentDeductible;
        state.order.policy.originalDeductible = sessionInformation.order.policy?.originalDeductible;
        state.order.policy.additionalAuthFlag = sessionInformation.order.policy?.additionalAuthFlag;
        state.order.policy.isItac =
            coverageTypeValue(sessionInformation?.order.insuranceCoverage.coverageType) ===
            coverageType.ITAC
                ? true
                : false;
        state.order.policy.policyNumber = sessionInformation.order.policy?.policyNumber;
        state.order.policy.insuranceCompanyName =
            sessionInformation.order.policy?.insuranceCompanyName;
        state.order.policy.isNoComp =
            coverageTypeValue(sessionInformation?.order.insuranceCoverage.coverageType) ===
            coverageType.NOCOMP
                ? true
                : false;
        sessionInformation.order.policy?.noComprehensive;
    },
    updateExperiments(state, experiments) {
        state.applicationUser.experiments = experiments;
    },
    updateTriggeredSiteEntry(state, wasSiteEntryTriggered) {
        state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered;
    },
    updateAffiliateCookies(state, affiliateCookies) {
        state.applicationUser.affiliateCookies = affiliateCookies;
    },
};

// 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;
    },
    coverageIsVerified: (state) => {
        let order;
        if (window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== null) {
            order = JSON.parse(
                window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE)
            ).order;
        } else {
            order = state.order;
        }

        if (
            order.payment?.insuranceCoverage?.isVerified &&
            order.policy?.currentDeductible != 9999 &&
            order.policy?.currentDeductible != 7777
        ) {
            return true;
        }

        return false;
    },
    isMobileAppointment: (state) => {
        return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
    },
    isDropOffAppointment: (state) => {
        return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF;
    },
    isOvernightDropOffAppointment: (state) => {
        return state.order.schedule.routeCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF);
    },
    isRecalibrationOnOrder: (state) => {
        return getHasRecalibrationPart(state);
    },
    isRecalibrationOnSubmittedState: (state) => {
        if (window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== null) {
            return getHasRecalibrationPart({
                order: JSON.parse(
                    window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE)
                ).order,
            });
        }
        return false;
    },
    shouldHideRecalibration: (state) => {
        if (window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== null) {
            state = JSON.parse(
                window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE)
            );
        }
        let order = state.order;
        let applicationUser = state.applicationUser;
        if (order.payment.isInsurance) {
            return false;
        }

        return (
            hasSettingEqualTo(
                experimentSettings.RECAL_PRICE_REMOVE,
                "true",
                getExperimentSettingsFromExperimentList(applicationUser.experiments)
            ) && getters.isRecalibrationOnOrder(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,
    policy: (state) => state.order.policy,
    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,
            funnelServiceZipCodeCtu: state.order.serviceLocation.zipCodeCtu,
            funnelProviderNumber: state.order.serviceLocation.provider.providerNumber,
            funnelParentAccountNumber: state.order.payment.parentAccountNumber,
            funnelReferralType: state.order.payment.isInsurance ? "INSURANCE" : "CASH QUOTE",
            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) =>
        getExperimentSettingsFromExperimentList(state.applicationUser.experiments),

    isVerifiedAndDeductibleZeroConfirmed: (state) => {
        // used in CMS on /payment-method in Header Sub Text, for FunnelSubHeaderWidget on cart pages
        if (
            state.order.policy.currentDeductible === 0 &&
            getters.coverageIsVerified(state) &&
            !state.order.policy.isNoComp
        ) {
            return true;
        }
        return false;
    },
    isItacOrNoComp: (state) => {
        // used in CMS on /payment-method in Header Sub Text, for FunnelSubHeaderWidget on cart pages
        if (state.order.policy.isNoComp || state.order.policy.isItac) {
            return true;
        } else {
            return false;
        }
    },
    requiresVerifiedRedirecting: (state) => {
        return state.order?.referralNumber?.length === 6;
    },
    externalParameterState: (state) => externalParameterState,
    externalParameterVehicle: (state) => externalParameterState?.vehicle,
    externalParameterDamage: (state) => externalParameterState?.vehicleDamage,
    externalParameterQuote: (state) => externalParameterState?.quote,
    externalParameterServiceZip: (state) => externalParameterState?.serviceZip,
    externalParameterEstimate: (state) => externalParameterState?.estimate,
    externalParameterCustomer: (state) => externalParameterState?.customer,
    isExternalParameter: (state) => externalParameterState?.isExternalParameter,

    emailOrSms: (state) => {
        const email =
            externalParameterState.customer.emailAddress ?? state.order.customer.emailAddress;
        const phone =
            externalParameterState.customer.phoneNumber ?? state.order.customer.phoneNumber;

        if (!phone && !email) {
            return null;
        }

        if (phone && !email) {
            return phone;
        }

        if (email && !phone) {
            return email;
        }

        // if we're here, then we have both email and phone so get the value from pageData to reflect what they entered on the page.
        // if they didn't enter anything on the page default to email
        return (
            state.applicationUser.pageData[fmgPageValues.EMAIL_SMS_PAGES]?.emailOrSmsValue ??
            state.order.customer.emailAddress
        );
    },
};

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

function getTimeSlotsAdditionalEventData(
    provisionalTriggers,
    zipCode,
    firstAvailableAppointmentDateString,
    shopAppointmentType
) {
    var numberOfDays = null;
    if (firstAvailableAppointmentDateString)
        numberOfDays = getDateDifferenceInDays(
            new Date().toISOString().split("T")[0],
            firstAvailableAppointmentDateString
        );

    if (shopAppointmentType)
        return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ShopAppointmentType:${shopAppointmentType},ProvisionalTriggers:${provisionalTriggers.join(
            ","
        )}`;
    else
        return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ProvisionalTriggers:${provisionalTriggers.join(
            ","
        )}`;
}

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

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

    lookupVehicleByVin(context, { payload: { vin }, pageNameToLog }) {
        return globalMethods.callHttpClient({
            method: endpoints.LookupVehicleByVin.method,
            endpoint: endpoints.LookupVehicleByVin.url,
            payload: {
                vin: vin, // EX "1J4GW58S4XC541166"
                logEnabled: context.getters.applicationUser.loggingOption,
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

    lookupVinByPlate(context, { payload: { licensePlate, licenseState }, pageNameToLog }) {
        return globalMethods.callHttpClient({
            method: endpoints.LookupVinByPlate.method,
            endpoint: endpoints.LookupVinByPlate.url,
            payload: {
                licensePlate: licensePlate,
                licenseState: licenseState,
                logEnabled: context.getters.applicationUser.loggingOption,
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

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

    lookupVinByImage(context, { payload, pageNameToLog }) {
        const image = payload;

        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,
                logApiCall: true,
                pageNameToLog: pageNameToLog,
            });
        });
    },

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

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

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

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

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

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

    validateZip(context, { payload: { zip }, pageNameToLog }) {
        const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
        return globalMethods.callHttpClient({
            methods: endpoints.ValidateZip.method,
            endpoint: `${endpoints.ValidateZip.url}/${zip}/${damageType}`,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

    getClosestApplicableShops(context, { payload: { zip, carId }, pageNameToLog }) {
        return globalMethods.callHttpClient({
            methods: endpoints.ClosestApplicableShops.method,
            endpoint: `${endpoints.ClosestApplicableShops.url}/${zip}/${carId}`,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

    // Dependency Actions
    resetVehicleStateAndDependencies(context) {
        context.commit(storeMutations.RESET_VEHICLE_STATE);

        context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
    },

    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.commit(storeMutations.UPDATE_VAPS, 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);
        context.commit(storeMutations.RESET_PAYMENT_METHOD_CHOICE);
    },

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

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

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

    // Analytics Actions
    logExperimentExposureAndUpdateStore(
        context,
        { payload: { userId, deviceId, sessionKey, pageName, experiment }, pageNameToLog }
    ) {
        const experiments = deepClone(context.getters.applicationUser.experiments);
        const experimentToMarkAsExposed = experiments.find((ex) => {
            return (
                ex.universeId == experiment.universeId &&
                ex.testId == experiment.testId &&
                ex.variationId == experiment.variationId &&
                ex.assignmentId == experiment.assignmentId
            );
        });
        if (experimentToMarkAsExposed) {
            experimentToMarkAsExposed.isExposed = true;
        }

        context.commit(storeMutations.UPDATE_EXPERIMENTS, experiments);

        return globalMethods.callHttpClient({
            method: endpoints.LogExperimentExposureIfAssigned.method,
            endpoint: endpoints.LogExperimentExposureIfAssigned.url,
            payload: {
                experimentForLogging: {
                    userId: userId,
                    deviceId: deviceId,
                    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,
                },
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

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

    // Misc Actions
    updateStoreWithSaveSessionResponse(
        context,
        {
            referralNumber,
            referralSequenceNumber,
            referralDate,
            referralCorrelationId,
            eon,
            parentAccountNumber,
            savedSessionId,
            crmCustomerId,
            workOrderNumber,
            workOrderId,
            customerPortalLoginToken,
            lockToken,
            settledTenderAmount,
            billToAccountNumber,
        }
    ) {
        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);
        context.commit(storeMutations.WORK_ORDER_NUMBER, workOrderNumber);
        context.commit(storeMutations.WORK_ORDER_ID, workOrderId);
        context.commit(storeMutations.LOCK_TOKEN, lockToken);
        context.commit(storeMutations.Customer_Portal_Login_Token, customerPortalLoginToken);
        context.commit(storeMutations.UPDATE_SETTLED_TENDER_AMOUNT, settledTenderAmount);
        context.commit(storeMutations.UPDATE_BILL_TO_ACCT_NUMBER, billToAccountNumber);
    },

    logPageView(
        context,
        {
            userId,
            sessionKey,
            pageName,
            sessionId,
            action,
            event,
            shouldUseSessionId,
            experimentsForUser,
            referralSequenceNumber,
            parentAccountNumber,
        }
    ) {
        if (!pageName) {
            return;
        }

        var payload = {
            userId: userId,
            sessionKey: sessionKey,
            sessionId: sessionId,
            pageName: pageName,
            applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME,
            action: action,
            event: event,
            shouldUseSessionId: shouldUseSessionId,
            experimentsForUser: experimentsForUser,
            referralSequenceNumber: referralSequenceNumber,
            parentAccountNumber:
                parentAccountNumber ?? applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
        };

        return globalMethods
            .callHttpClient({
                method: endpoints.LogPageView.method,
                endpoint: endpoints.LogPageView.url,
                payload: payload,
                logApiCall: false,
            })
            .then((response) => {
                return response;
            });
    },

    logCustomEvent(
        context,
        {
            userId,
            sessionKey,
            pageName,
            sessionId,
            category,
            action,
            label,
            value,
            shouldUseSessionId,
            experimentsForUser,
            referralSequenceNumber,
            parentAccountNumber,
        }
    ) {
        if (!pageName) {
            return;
        }

        var payload = {
            userId: userId,
            sessionKey: sessionKey,
            sessionId: sessionId,
            pageName: pageName,
            applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME,
            category: category,
            action: action,
            label: label,
            value: value,
            shouldUseSessionId: shouldUseSessionId,
            experimentsForUser: experimentsForUser,
            referralSequenceNumber: referralSequenceNumber,
            parentAccountNumber:
                parentAccountNumber ?? applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
        };

        return globalMethods
            .callHttpClient({
                method: endpoints.LogCustomEvent.method,
                endpoint: endpoints.LogCustomEvent.url,
                payload: payload,
                logApiCall: false,
            })
            .then((response) => {
                return response;
            });
    },
    initializeSession(context, { userId, deviceId, sessionId, userAgent, referrer }) {
        var payload = {
            applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME,
            userId: userId,
            deviceId: deviceId,
            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;
            });
    },

    logPartQuestions(
        context,
        { eon, ctu, workOrderId, workOrderNumber, carId, glassLocation, partQuestions, userAgent }
    ) {
        var payload = {
            applicationName: applicationConfig.APPLICATION_NAME,
            eon: eon,
            ctu: ctu,
            workOrderId: workOrderId,
            workOrderNumber: workOrderNumber,
            carId: carId,
            glassLocation: glassLocation,
            partQuestions: partQuestions,
            userAgent: navigator.userAgent,
        };

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

    // 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,
        { payload: { deviceId, triggerEvent, triggerValue }, pageNameToLog }
    ) {
        if (triggerEvent == experimentTriggers.SITE_ENTRY) {
            context.commit(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true);
        }

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

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

        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, { pageNameToLog }) {
        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;
        const serviceType = order.serviceLocation?.appointmentType;
        const referralSeqNumber = order.referralSequenceNumber;

        // 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,
                serviceType: serviceType,
                referralSeqNumber: referralSeqNumber,
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });

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

        return response;
    },

    // Parts API Actions
    async getParts(context, { pageNameToLog }) {
        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;
        const serviceType = order.serviceLocation?.appointmentType;
        const referralSeqNumber = order.referralSequenceNumber;

        // 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,
                serviceType: serviceType,
                referralSeqNumber: referralSeqNumber,
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });

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

        return response;
    },

    async getWipers(context, { payload, pageNameToLog }) {
        const carId = payload.carId;
        const serviceZipCode = payload.serviceZipCode;

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

        syncLineItemIds(response.data, context.getters.order.lineItems.vaps);
        return response;
    },

    async getRainDefense(context, { pageNameToLog }) {
        const response = await globalMethods.callHttpClient({
            method: endpoints.GetRainDefense.method,
            endpoint: endpoints.GetRainDefense.url,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
        syncLineItemIds([response.data], context.getters.order.lineItems.vaps);
        return response;
    },

    getMobileFeePart(context, { payload: { serviceZipCode, serviceZipCodeCtu }, pageNameToLog }) {
        const serviceType = context.getters.damage.isRepair ? "Repair" : "Install";
        const facilityType = "Mobile";
        const parentAccountNumber = context.getters.payment.parentAccountNumber;
        const billToAccountNumber = context.getters.payment.billToAccountNumber
            ? context.getters.payment.billToAccountNumber
            : applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER;

        const order = context.getters.order;
        const coverageStatus = coverageStatusEnum(order.payment?.insuranceCoverage?.coverageStatus);
        const coverageType = coverageTypeEnum(order.payment?.insuranceCoverage?.coverageType);
        const isItacOptimized = order.policy?.isItac ?? false;
        const zipCode =
            serviceZipCode ??
            order.serviceLocation?.provider?.address?.zipCode ??
            order.serviceLocation?.zipCode;

        var providerNumber =
            serviceZipCodeCtu ??
            order.serviceLocation?.provider?.providerNumber ??
            order.serviceLocation?.zipCodeCtu;
        if (providerNumber.startsWith("00") && providerNumber.length > 5) {
            providerNumber = providerNumber.substring(1);
        }

        var endPoint = `${endpoints.GetMobileFeePart.url}/?serviceType=${serviceType}&facilityType=${facilityType}&parentAccountNumber=${parentAccountNumber}&billToAccountNumber=${billToAccountNumber}&providerNumber=${providerNumber}&isItacOptimized=${isItacOptimized}&zipCode=${zipCode}`;

        const recalPartNumbers = getRecalPartNumbers(order.lineItems?.glassParts);
        const carId = order.vehicle?.carId;
        if (recalPartNumbers && recalPartNumbers.length > 0 && carId) {
            let additionalQueryParams = ``;
            for (let i = 0; i < recalPartNumbers.length; i++) {
                additionalQueryParams += `&partNumbers[${i}]=${recalPartNumbers[i]}`;
            }
            additionalQueryParams += `&carId=${carId}`;
            endPoint += additionalQueryParams;
        }

        if (coverageStatus) {
            endPoint = `${endPoint}&coverageStatus=${coverageStatus}`;
        }

        if (coverageType) {
            endPoint = `${endPoint}&coverageType=${coverageType}`;
        }

        return globalMethods.callHttpClient({
            method: endpoints.GetMobileFeePart.method,
            endpoint: endPoint,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },
    async getPricingByDayPart(context, { pageNameToLog }) {
        return await globalMethods
            .callHttpClient({
                method: endpoints.GetPricingByDayPart.method,
                endpoint: endpoints.GetPricingByDayPart.url,
                logApiCall: true,
                pageNameToLog: pageNameToLog,
            })
            .then((response) => {
                return response;
            });
    },
    getServicePackageDiscountPart(context, { pageNameToLog }) {
        const damage = context.getters.damage;
        const damageType = damage.isRepair ? "Repair" : "Replace";
        const glassPieces = convertGlassPieceNamingForApi(damage.glassToReplace);

        return globalMethods
            .callHttpClient({
                method: endpoints.GetServicePackageDiscountPart.method,
                endpoint: endpoints.GetServicePackageDiscountPart.url,
                payload: {
                    glassPieces: glassPieces,
                    damageType: damageType,
                },
                logApiCall: true,
                pageNameToLog: pageNameToLog,
            })
            .then((response) => {
                return response;
            });
    },

    getServiceabilityDetails(context, { payload: { serviceZipCode }, pageNameToLog }) {
        const escapeRecalibrationType = (rt) => rt.split("&").join("%26");

        const partialLineItemsObjects = context.getters.order.lineItems.glassParts?.map(
            (lineItem) => ({
                partNumber: lineItem.partNumber,
                recalibrationType:
                    lineItem.recalibrationType && isRecalPartOrHasChildRecalPart(lineItem)
                        ? escapeRecalibrationType(lineItem.recalibrationType)
                        : undefined,
            })
        );

        var lineItems = null;
        if (partialLineItemsObjects) {
            lineItems = buildQueryStringParameterFromArrayOfComplexObjects(
                partialLineItemsObjects,
                "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"
        );

        const parentAccountNumber =
            context.getters.order.payment.parentAccountNumber ??
            applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
        const referralSequenceNumber = context.getters.order.referralSequenceNumber;

        var endPoint = `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&parentAccountNumber=${parentAccountNumber}&referralSequenceNumber=${referralSequenceNumber}&applicationName=${applicationConfig.ANALYTICS_APPLICATION_NAME}`;
        if (lineItems) {
            endPoint += `&${lineItems}`;
        }

        if (glassPieces) {
            endPoint += `&${glassPieces}`;
        }

        endPoint += `&isHeavyTruckVehicle=${context.getters.order.vehicle.isBigTruck}`;

        return globalMethods.callHttpClient({
            method: endpoints.GetServiceabilityDetails.method,
            endpoint: endPoint,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

    getProviders(context, { payload: { serviceZipCode }, pageNameToLog }) {
        const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
        const accountNumber =
            context.getters.order.payment.parentAccountNumber ??
            applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
        const carId = context.getters.order.vehicle.carId;
        const partsWithRecal = getTopLevelPartsWithRecal(
            context.getters.order.lineItems.glassParts
        );
        const windshieldPartWithRecal = partsWithRecal?.length > 0 ? partsWithRecal[0] : null;

        const shopRadiusInMiles = 100;

        let url = `${endpoints.GetProviders.url}/${serviceZipCode}/${damageType}/${shopRadiusInMiles}/${accountNumber}/true/${carId}`;

        if (windshieldPartWithRecal) {
            url += `/${windshieldPartWithRecal.partNumber}`;
        }
        //heavy truck vehicle
        url += `/${context.getters.order.vehicle.isBigTruck}`;

        return globalMethods.callHttpClient({
            method: endpoints.GetProviders.method,
            endpoint: url,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

    getSupportingItems(context, { pageNameToLog }) {
        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,
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

    getCapabilityQuestions(context, { payload: { carId, partNumber }, pageNameToLog }) {
        return globalMethods.callHttpClient({
            method: endpoints.GetCapabilityQuestions.method,
            endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}`,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },
    async submitDonationPartToSV2(
        context,
        { payload: { donationAmount, submittedOrder }, pageNameToLog }
    ) {
        const state = JSON.parse(
            window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE)
        );
        const order = state.order;
        const payload = {
            donationAmount: parseInt(donationAmount),
            savedSessionId: state.applicationUser.savedSessionId,
            paymentInfo: {
                parentAccountNumber: order.payment.parentAccountNumber,
                billToAccountNumber: order.payment.billToAccountNumber,
                providerNumber: order.serviceLocation.provider.providerNumber,
                carId: order.vehicle.carId,
                make: order.vehicle.make,
                model: order.vehicle.model,
                year: order.vehicle.year,
                eon: order.eon,
                zipCode: order.serviceLocation.zipCode,
                state: order.serviceLocation.state,
                referralNumber: order.referralNumber,
                referralDate: order.referralDate,
                referralSequenceNumber: order.referralSequenceNumber,
                serviceZipCode: order.serviceLocation.zipCode,
                isReplacement: !order.damage.isRepair,
                deductible: order.policy.currentDeductible,
                coverageStatus: order.payment.insuranceCoverage.coverageStatus,
                coverageSubStatus: order.payment.insuranceCoverage.coverageSubStatus,
                serverData: order.lineItems.serverData,
            },
        };

        const response = await globalMethods.callHttpClient({
            method: endpoints.AddDonationPart.method,
            endpoint: endpoints.AddDonationPart.url,
            payload: payload,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
        return response.data;
    },

    getPartFromCapabilityQuestionAnswer(context, { payload, pageNameToLog }) {
        const glassLocation = payload;
        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,
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

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

        const lineItems = getFlattenedLineItemsWithGlassPartTag(order.lineItems);

        const glassPieces = order.damage.glassToReplace
            ? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
            : [];
        var payload = {
            zipCode: order.serviceLocation.provider.zipCode,
            providerNumber: providerNumber,
            startDate: startDate,
            endDate: endDate,
            shopAppointmentType: shopAppointmentType,
            applicationName: applicationConfig.APPLICATION_NAME,
            parentAccountNumber: payment.parentAccountNumber,
            carId: vehicle.carId,
            lineItems: lineItems,
            glassPieces: glassPieces,
            eon: order.eon,
            billToAccountNumber: context.getters.payment.billToAccountNumber,
            coverage: {
                status: payment.insuranceCoverage.coverageStatus,
                deductible: order.policy.currentDeductible,
                additionalAuthFlag: order.policy.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,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
            additionalSuccessEventDataHandler: (response) =>
                getTimeSlotsAdditionalEventData(
                    response.data.provisionalTriggers,
                    order.serviceLocation.zipCode,
                    response.data.days?.[0]?.date,
                    shopAppointmentType
                ),
        });
    },

    getMobileTimeSlots(context, { payload: { startDate, endDate }, pageNameToLog }) {
        const order = context.state.order;
        const vehicle = context.state.order.vehicle;
        const payment = context.state.order.payment;
        const lineItems = getFlattenedLineItemsWithGlassPartTag(order.lineItems);
        const glassPieces = order.damage.glassToReplace
            ? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
            : [];
        var payload = {
            startDate: startDate,
            endDate: endDate,
            applicationName: applicationConfig.APPLICATION_NAME,
            parentAccountNumber: payment.parentAccountNumber,
            carId: vehicle.carId,
            lineItems: lineItems,
            glassPieces: glassPieces,
            eon: order.eon,
            billToAccountNumber: context.getters.payment.billToAccountNumber,
            coverage: {
                status: payment.insuranceCoverage.coverageStatus,
                deductible: order.policy.currentDeductible,
                additionalAuthFlag: order.policy.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,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
            additionalSuccessEventDataHandler: (response) =>
                getTimeSlotsAdditionalEventData(
                    response.data.provisionalTriggers,
                    order.serviceLocation.zipCode,
                    response.data.days?.[0]?.date
                ),
        });
    },

    getMobilePremiumFee(context, { pageNameToLog }) {
        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}`,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

    // Signing Service for Payment
    getSignature() {
        return globalMethods.callHttpClient({
            method: endpoints.GetSignature.method,
            endpoint: endpoints.GetSignature.url,
        });
    },

    // Session API Actions
    saveQuote(context, { pageNameToLog, payload }) {
        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;
        const excludeRecalInquote = context.getters.shouldHideRecalibration;

        return globalMethods.callHttpClient({
            method: endpoints.SaveQuote.method,
            endpoint: endpoints.SaveQuote.url,
            payload: {
                referralCorrelationId: order.referralCorrelationId,
                referralNumber: order.referralNumber?.toString(),
                referralSequenceNumber: order.referralSequenceNumber,
                emailAddress: order.customer.emailAddress,
                lastPage: pageNameToLog,
                isRepair: damage.isRepair,
                firstName: order.customer.firstName,
                lastName: order.customer.lastName,
                savedSessionId: applicationUser.savedSessionId,
                serviceState: order.serviceLocation.state,
                serviceZipCode: order.serviceLocation.zipCode,
                parentAccountNumber: order.payment.parentAccountNumber,
                excludeRecalInquote: excludeRecalInquote,
                year: vehicle.year,
                make: vehicle.make,
                model: vehicle.model,
                subType: vehicle.vehicleSubType,
                specialClass: vehicle.vehicleSpecialClass,
                isBigTruck: vehicle.isBigTruck,
                isInsurance: order.payment.isInsurance ?? false,
                isVerified: order.payment.insuranceCoverage.isVerified ?? false,
                lineItems: {
                    glassParts: lineItems.glassParts,
                    supportingItems: lineItems.supportingItems,
                    vaps: lineItems.vaps,
                    serverData: lineItems.serverData,
                    promos: lineItems.promos,
                },
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });
    },

    saveSession(context, { pageNameToLog, payload }) {
        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;

        const submitAfterSave = payload?.submitAfterSave === "true";
        const createUnscheduledStatusWorkOrderForPIA =
            payload?.createUnscheduledStatusWorkOrderForPIA === "true";

        // create a new array to avoid mutating state
        const newGlassToReplace = convertGlassPieceNamingForApi(damage.glassToReplace);
        //affiliate cookie save only when submitAfterSave
        const affiliateCookies = submitAfterSave ? applicationUser.affiliateCookies : null;

        return globalMethods.callHttpClient({
            method: endpoints.SaveSession.method,
            endpoint: endpoints.SaveSession.url,
            payload: {
                submitAfterSave: submitAfterSave,
                userAgent: navigator.userAgent,
                applicationUser: {
                    crmCustomerId: applicationUser.crmCustomerId,
                    experiments: applicationUser.experiments,
                    lastPage: applicationUser.lastPageVisited,
                    pageData: applicationUser.pageData,
                    savedSessionId: applicationUser.savedSessionId,
                    affiliateCookies: affiliateCookies,
                },
                order: {
                    vehicle: {
                        carId: vehicle.carId,
                        year: vehicle.year,
                        make: vehicle.make,
                        model: vehicle.model,
                        style: vehicle.style,
                        vin: vehicle.vin,
                        vehicleSubType: vehicle.vehicleSubType,
                        vehicleSpecialClass: vehicle.vehicleSpecialClass,
                        isBigTruck: vehicle.isBigTruck,
                        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,
                        phoneNumber: 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,
                        promos: lineItems.promos,
                    },
                    payment: {
                        InsuranceCoverage: {
                            isVerified: order.payment.insuranceCoverage.isVerified ?? false,
                            coverageStatus:
                                order.payment.insuranceCoverage.coverageStatus?.toString(),
                            coverageVerificationType:
                                order.payment.insuranceCoverage.coverageVerificationType,
                            coverageSubStatus: order.payment.insuranceCoverage.coverageSubStatus,
                        },
                        isInsurance: order.payment.isInsurance,
                        parentAccountNumber: order.payment.parentAccountNumber,
                        billToAccountNumber: order.payment.billToAccountNumber,
                        inactivePromos: order.payment.inactivePromos,
                        isCreditCard:
                            order.payment.piaType == paymentMethods.CREDIT_CARD ? true : false,
                        isPaypal: order.payment.piaType == paymentMethods.PAYPAL ? true : false,
                        isAfterPay: order.payment.piaType == paymentMethods.AFTERPAY ? true : false,
                        isApplePay:
                            order.payment.piaType == paymentMethods.APPLE_PAY ? true : false,
                        paypalToken: order.payment.paypalToken,
                        nextGenSettledAmount: order.payment.nextGenSettledAmount,
                        ccToken: {
                            subscriptionId: order.payment.ccToken.subscriptionId,
                            expMonth: order.payment.ccToken.expMonth,
                            expYear: order.payment.ccToken.expYear,
                            cardType: order.payment.ccToken.cardType,
                            billToPostalCode: order.payment.ccToken.billToPostalCode,
                            billToFirstName: order.payment.ccToken.billToFirstName,
                            billToLastName: order.payment.ccToken.billToLastName,
                            referenceNumber: order.payment.ccToken.referenceNumber,
                            authCode: order.payment.ccToken.authCode,
                            transactionId: order.payment.ccToken.transactionId,
                            transReferenceNumber: order.payment.ccToken.transReferenceNumber,
                            lastFour: order.payment.ccToken.lastFour,
                        },
                    },
                    policy: {
                        isItac: order.policy?.isItac,
                        isNoComp: order.policy?.isNoComp,
                        currentDeductible: order.policy?.currentDeductible,
                        originalDeductible: order.policy?.originalDeductible,
                    },
                    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,
                    },
                    referralCorrelationId: order.referralCorrelationId,
                    referralDate: order.referralDate,
                    referralNumber: order.referralNumber?.toString(),
                    referralSequenceNumber: order.referralSequenceNumber,
                    eon: order.eon,
                    createUnscheduledStatusWorkOrderForPIA: createUnscheduledStatusWorkOrderForPIA,
                    lockToken: order.lockToken,
                    isRecalAckOptIn: order.isRecalAckOptIn,
                    isMSRFeeApplicable: order.isMSRFeeApplicable,
                },
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
            additionalSuccessEventDataHandler: (response) =>
                "Email provided: " + (order.customer.emailAddress ? "true" : "false"),
        });
    },

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

        var dtParts = referralDate.split("-");
        const year = dtParts[0];
        const month = dtParts[1];
        const day = dtParts[2].substring(0, 2);

        const loadDate = `${year}-${month}-${day}`;

        return globalMethods
            .callHttpClient({
                method: endpoints.LoadSession.method,
                endpoint: endpoints.LoadSession.url,
                payload: {
                    savedSessionId: savedSessionId?.toString(),
                    referralNumber: referralNumber?.toString(),
                    referralDate: loadDate,
                    parentAccountNumber: parentAccountNumber?.toString(),
                    referralCorrelationId: referralCorrelationId,
                },
                logApiCall: true,
                pageNameToLog: pageNameToLog,
            })
            .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.order.eon
                    ) {
                        context.commit(storeMutations.RESET_STATE);
                    }

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

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

    saveCCToken(context, ccToken) {
        context.commit(storeMutations.UPDATE_CCTOKEN, ccToken);
    },

    saveNextGenSettledAmount(context, nextGenSettledAmount) {
        context.commit(storeMutations.UPDATE_NEXTGEN_SETTLED_AMOUNT, nextGenSettledAmount);
    },

    savePaypalToken(context, ppToken) {
        context.commit(storeMutations.UPDATE_PAYPAL_TOKEN, ppToken);
    },

    // Business domain actions

    // Vehicle
    saveVehicle(
        context,
        {
            year,
            make,
            model,
            style,
            vehicleSubType,
            vehicleSpecialClass,
            isBigTruck,
            carId,
            category,
            canSafeliteService,
            imageUrl,
            imageVifNumber,
            imageVifColor,
        }
    ) {
        if (
            context.state.order.vehicle.year != year ||
            context.state.order.vehicle.make != make ||
            context.state.order.vehicle.model != model ||
            context.state.order.vehicle.style != style
        ) {
            context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
            context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);

            context.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
            context.commit(storeMutations.UPDATE_YEAR, year);
            context.commit(storeMutations.UPDATE_MAKE, make);
            context.commit(storeMutations.UPDATE_MODEL, model);
            context.commit(storeMutations.UPDATE_STYLE, style);
            context.commit(storeMutations.UPDATE_VEHICLE_SUBTYPE, vehicleSubType);
            context.commit(storeMutations.UPDATE_VEHICLE_SPECIAL_CLASS, vehicleSpecialClass);
            context.commit(storeMutations.UPDATE_IS_BIGTRUCK, isBigTruck);
            context.commit(storeMutations.UPDATE_CAR_ID, carId);
            context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, category);
            context.commit(storeMutations.UPDATE_VEHICLE_CAN_SAFELITE_SERVICE, canSafeliteService);
            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);

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

            context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);

            if (registrationInfo) {
                context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo);

                // only update name information if there isn't a value in state already
                if (!context.getters.order.customer.firstName) {
                    context.commit(storeMutations.UPDATE_CUSTOMER_DETAILS, {
                        firstName: registrationInfo.firstName,
                        lastName: registrationInfo.lastName,
                    });
                }
            }
        }
    },

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

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

        context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);

        // only update name information if there isn't a value in state already
        if (!context.getters.order.customer.firstName && customerInfo) {
            context.commit(storeMutations.UPDATE_CUSTOMER_DETAILS, {
                firstName: customerInfo.firstName,
                lastName: customerInfo.lastName,
            });
        }
    },

    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) {
        if (isInsurance !== context.getters.payment.isInsurance) {
            context.commit(storeMutations.RESET_PAYMENT_METHOD_CHOICE);
        }

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

    savePaymentMethodChoice(context, paymentMethod) {
        var isPia = null;
        if (paymentMethod !== paymentMethods.NONE) {
            isPia = paymentMethod !== paymentMethods.LATER;
        }

        context.commit(storeMutations.UPDATE_IS_PIA, isPia);
        context.commit(storeMutations.UPDATE_PIA_TYPE, isPia ? paymentMethod : null);
    },

    saveIsRecalAckOptIn(context, isRecalAckOptIn) {
        context.commit(storeMutations.UPDATE_IS_RECAL_ACK_OPT_IN, isRecalAckOptIn);
    },

    saveIsMSRFeeApplicable(context, isMSRFeeApplicable) {
        context.commit(storeMutations.UPDATE_IS_MSR_FEE_APPLICABLE, isMSRFeeApplicable);
    },

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

    saveSupportingItems(context, supportingItems) {
        syncLineItemIds(supportingItems, context.state.order.lineItems.supportingItems);
        if (!supportingItemsEqual(supportingItems, context.state.order.lineItems.supportingItems)) {
            context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
        }
        addGuidToLineItemsIfNotAlreadyThere(supportingItems);
        context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
    },

    saveSupportingItemsSuppressingStateResetting(context, supportingItems) {
        addGuidToLineItemsIfNotAlreadyThere(supportingItems);
        context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
    },
    saveVaps(context, vaps) {
        addGuidToLineItemsIfNotAlreadyThere(vaps);
        context.commit(storeMutations.UPDATE_VAPS, vaps);
    },
    // Manage promo saving to ensure a promoCode never ends up in both active and inactive
    saveActiveAndOrInactivePromos(context, { activePromos = null, inactivePromos = null }) {
        if (inactivePromos) {
            // Clean up inactivePromos
            // Remove bundle identifier
            inactivePromos = inactivePromos.map((promoCode) =>
                getPromoCodeWithoutBundleIdentifier(promoCode)
            );
            // Remove duplicates
            inactivePromos = inactivePromos.filter(
                (promoCode, index) => inactivePromos.indexOf(promoCode) === index
            );
        }
        activePromos = activePromos?.slice(0);
        inactivePromos = inactivePromos?.slice(0);
        let activePromosToSave;
        let inactivePromosToSave;
        if (!activePromos && !inactivePromos) {
            // Allow double null to save empty arrays to store
            activePromosToSave = [];
            inactivePromosToSave = [];
        } else if (activePromos && inactivePromos) {
            // Prioritize active promos when both inactive and active are supplied
            activePromosToSave = activePromos;
            inactivePromosToSave = removeCurrentlyActivePromoCodesFromInactivePromos(
                activePromosToSave,
                inactivePromos
            );
        } else if (!activePromos) {
            // Only inactivePromos supplied
            inactivePromosToSave = inactivePromos;
            activePromosToSave = (context.getters.lineItems.promos ?? []).filter((activePromo) => {
                return !inactivePromosToSave.includes(
                    getPromoCodeWithoutBundleIdentifier(activePromo.promoCode)
                );
            });
        } else if (!inactivePromos) {
            // Only activePromos supplied
            activePromosToSave = activePromos;
            inactivePromosToSave = removeCurrentlyActivePromoCodesFromInactivePromos(
                activePromosToSave,
                context.getters.payment.inactivePromos ?? []
            );
        }
        context.commit(storeMutations.UPDATE_PROMOS, activePromosToSave);
        context.commit(storeMutations.UPDATE_INACTIVE_PROMOS, inactivePromosToSave);
    },

    // Price order actions
    async priceOrderItemsAndSaveServerData(
        context,
        { payload: { availableLineItems, serviceZipCode, serviceZipCodeCtu }, pageNameToLog }
    ) {
        const flattenedLineItemsWithChildParts =
            getFlattenedArrayOfLineItemsWithChildParts(availableLineItems);

        const lineItemsWithOnlyPartNumbers = flattenedLineItemsWithChildParts.map((lineItem) => ({
            partNumber: lineItem.partNumber,
        }));

        const order = context.getters.order;

        var providerNumber =
            order.serviceLocation?.provider?.providerNumber ?? order.serviceLocation?.zipCodeCtu;
        if (providerNumber.startsWith("00") && providerNumber.length > 5) {
            providerNumber = providerNumber.substring(1);
        }

        const response = await globalMethods.callHttpClient({
            method: endpoints.PriceOrderItems.method,
            endpoint: endpoints.PriceOrderItems.url,
            payload: {
                parentAccountNumber: order.payment?.parentAccountNumber,
                billToAccountNumber:
                    order.payment?.billToAccountNumber ??
                    applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER,
                providerNumber: providerNumber,
                carId: order.vehicle.carId,
                year: order.vehicle.year,
                make: order.vehicle.make,
                model: order.vehicle.model,
                eon: order.eon,
                state: order.serviceLocation?.state,
                referralNumber: order.referralNumber,
                referralSequenceNumber: order.referralSequenceNumber,
                zipCode: order.serviceLocation?.zipCode,
                serviceZipCode: serviceZipCode ?? order.serviceLocation?.zipCode,
                referralDate: order.referralDate,
                isReplacement: !order.damage.isRepair,
                deductible: order.policy?.currentDeductible,
                coverageStatus: order.payment?.insuranceCoverage?.coverageStatus,
                lineItems: lineItemsWithOnlyPartNumbers,
                serverData: order.lineItems.serverData ? order.lineItems.serverData : "",
            },
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });

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

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

        return availableLineItems;
    },

    // Tax order actions
    async taxOrderItemsAndSaveServerData(
        context,
        {
            payload: {
                billToAccountNumber,
                providerNumber,
                appointmentType,
                serviceLocationCity,
                serviceLocationState,
                serviceLocationZipCode,
                pricedLineItems,
            },
            pageNameToLog,
        }
    ) {
        const flattenedLineItemsWithChildParts =
            getFlattenedArrayOfLineItemsWithChildParts(pricedLineItems);

        const lineItemsWithOnlyPriceInfo = flattenedLineItemsWithChildParts.map((lineItem) => ({
            partNumber: lineItem.partNumber,
            laborAmount: lineItem.laborAmount ?? 0,
            kitPrice: lineItem.kitPrice ?? 0,
            sellingPrice: lineItem.sellingPrice ?? 0,
            promoCode: lineItem.promoCode ?? null,
        }));

        const pricedLineItemsFormattedForRequest =
            buildQueryStringParameterFromArrayOfComplexObjects(
                lineItemsWithOnlyPriceInfo,
                "lineItems"
            );

        let queryString = "";
        if (appointmentType == "Mobile") {
            queryString =
                `ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` +
                `&BillToAccountNumber=${billToAccountNumber}` +
                `&ProviderNumber=${providerNumber}` +
                `&AppointmentType=${appointmentType}` +
                `&ServiceLocation.City=${serviceLocationCity}` +
                `&ServiceLocation.State=${serviceLocationState}` +
                `&ServiceLocation.ZipCode=${serviceLocationZipCode}` +
                `&${pricedLineItemsFormattedForRequest}`;
        } else {
            queryString =
                `ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` +
                `&BillToAccountNumber=${billToAccountNumber}` +
                `&ProviderNumber=${providerNumber}` +
                `&AppointmentType=${appointmentType}` +
                `&${pricedLineItemsFormattedForRequest}`;
        }

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

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

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

        pricedLineItems = addTaxesToPricedLineItems(pricedLineItems, response.data.taxedLineItems);

        return pricedLineItems;
    },

    // The <addableVaps> parameter is an array of ALL available front wipers and rain defense
    //   for the vehicle. There is no need to confirm an addableVap is not already on the order
    // <lineItemsToUse> is an optional parameter if the lineItems in the store may not be up to date
    async validateOrderPromoAndSaveServerData(
        context,
        {
            payload: {
                promoCode,
                lineItemsToUse,
                addableVaps,
                useDefaultCashParentAccount = false,
            },
            pageNameToLog,
        }
    ) {
        const order = context.getters.order;
        lineItemsToUse = lineItemsToUse ?? order.lineItems;
        addGuidToLineItemsIfNotAlreadyThere(lineItemsToUse.vaps);
        syncLineItemIds(addableVaps, lineItemsToUse.vaps);
        // addableVaps still won't have IDs if they weren't already in lineItemsToUse
        addGuidToLineItemsIfNotAlreadyThere(addableVaps);
        const requestObject = {
            promoCode: promoCode,
            addableVaps: addableVaps,
            order: {
                appointmentType: order.serviceLocation.appointmentType,
                carId: order.vehicle.carId,
                correlationId: order.referralCorrelationId,
                eon: order.eon,
                isInsurance: order.payment.isInsurance,
                isRepair: order.damage.isRepair,
                glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace),
                lineItemsOnOrder: getArrayOfAllLineItemsAndChildParts(lineItemsToUse),
                parentAccountNumber: useDefaultCashParentAccount
                    ? applicationConfig.CASH_PARENT_ACCOUNT_NUMBER
                    : order.payment.parentAccountNumber,
                referralSequenceNumber: order.referralSequenceNumber,
                serviceState: order.serviceLocation.state,
                serverData: order.lineItems.serverData,
                vehicleYear: "" + order.vehicle.year,
                zipCodeOrProviderCtu:
                    order.serviceLocation.appointmentType === "Inshop"
                        ? order.serviceLocation.provider.address.zipCodeCtu
                        : order.serviceLocation.zipCodeCtu,
            },
        };

        const validateResponse = await globalMethods
            .callHttpClient({
                method: endpoints.ValidatePromo.method,
                endpoint: endpoints.ValidatePromo.url,
                payload: requestObject,
                logApiCall: true,
                pageNameToLog: pageNameToLog,
            })
            .catch((error) => {
                // Validate promo will return 4xx errors when an invalid promo
                return error;
            });

        if (validateResponse?.data?.serverData) {
            context.commit(
                storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA,
                validateResponse.data.serverData
            );
        }

        return validateResponse?.data;
    },
    // All payload parameters are optional - will use store data if not provided
    async revalidateOrderPromosAndSaveServerData(
        context,
        {
            payload: {
                activePromosToUse,
                inactivePromosToUse,
                lineItemsToUse,
                useDefaultCashParentAccount = false,
            },
            pageNameToLog,
        }
    ) {
        const order = context.getters.order;

        activePromosToUse = activePromosToUse ?? order.lineItems.promos;
        lineItemsToUse = lineItemsToUse ? deepClone(lineItemsToUse) : deepClone(order.lineItems);
        addGuidToLineItemsIfNotAlreadyThere(lineItemsToUse.vaps);
        lineItemsToUse.promos = activePromosToUse;

        inactivePromosToUse = inactivePromosToUse ?? order.payment.inactivePromos;
        inactivePromosToUse = removeCurrentlyActivePromoCodesFromInactivePromos(
            lineItemsToUse.promos,
            inactivePromosToUse
        );

        let requestObject = {
            inactivePromos: inactivePromosToUse,
            order: {
                appointmentType: order.serviceLocation.appointmentType,
                carId: order.vehicle.carId,
                correlationId: order.referralCorrelationId,
                eon: order.eon,
                isInsurance: order.payment.isInsurance,
                isRepair: order.damage.isRepair,
                glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace),
                lineItemsOnOrder: getArrayOfAllLineItemsAndChildParts(lineItemsToUse),
                parentAccountNumber: useDefaultCashParentAccount
                    ? applicationConfig.CASH_PARENT_ACCOUNT_NUMBER
                    : order.payment.parentAccountNumber,
                referralSequenceNumber: order.referralSequenceNumber,
                serviceState: order.serviceLocation.state,
                serverData: order.lineItems.serverData,
                vehicleYear: "" + order.vehicle.year,
                zipCodeOrProviderCtu:
                    order.serviceLocation.appointmentType === "Inshop"
                        ? order.serviceLocation.provider.address.zipCodeCtu
                        : order.serviceLocation.zipCodeCtu,
            },
        };

        const revalidateResponse = await globalMethods.callHttpClient({
            method: endpoints.RevalidatePromos.method,
            endpoint: endpoints.RevalidatePromos.url,
            payload: requestObject,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });

        context.commit(
            storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA,
            revalidateResponse.data.lineItemsServerData
        );

        return revalidateResponse?.data;
    },
    ///////////////////////////
    // Account Service Calls //
    ///////////////////////////

    // List all insurance companies
    async getInsuranceCompanyList(context, { pageNameToLog }) {
        const insuranceCompanyList = await globalMethods.callHttpClient({
            method: endpoints.GetInsuranceCompanyList.method,
            endpoint: endpoints.GetInsuranceCompanyList.url,
            payload: {},
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });

        return insuranceCompanyList.data.sort((a, b) => {
            const nameA = a.accountName.toUpperCase();
            const nameB = b.accountName.toUpperCase();
            if (nameA < nameB) {
                return -1;
            }
            if (nameA > nameB) {
                return 1;
            }
            return 0;
        });
    },
    // Get BillToAccountNumber
    async getBillToAccountNumber(
        context,
        {
            payload: {
                parentAccountNumber = context.getters.order.payment.parentAccountNumber,
                providerNumber = context.getters.order.serviceLocation.zipCodeCtu,
                isItac = context.getters.order.policy.isItac,
            },
            pageNameToLog,
        }
    ) {
        if (!parentAccountNumber) {
            parentAccountNumber = applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
        }
        const queryString =
            `ParentAccountNumber=${parentAccountNumber}` +
            `&ProviderNumber=${providerNumber}` +
            `&IsItac=${isItac}`;

        const billToAccountNumberResponse = await globalMethods.callHttpClient({
            method: endpoints.GetBillToAccountNumber.method,
            endpoint: `${endpoints.GetBillToAccountNumber.url}?${queryString}`,
            logApiCall: true,
            pageNameToLog: pageNameToLog,
        });

        return billToAccountNumberResponse.data.toString();
    },
    //////////////////////////////////
    // END OF Account Service Calls //
    //////////////////////////////////

    // 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.RESET_PAYMENT_METHOD_CHOICE);
            }
        }

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

    savePhoneNumber(context, phoneNumber) {
        context.commit(
            storeMutations.UPDATE_CUSTOMER_PHONE_NUMBER,
            phoneNumber === "" ? null : phoneNumber
        );
    },
    saveIsSmsOptIn(context, isSmsOptIn) {
        context.commit(storeMutations.UPDATE_CUSTOMER_IS_SMS_OPT_IN, isSmsOptIn);
    },

    saveWaitListRequested(context, waitListRequested) {
        context.commit(storeMutations.UPDATE_CUSTOMER_WAITLIST_REQUESTED, waitListRequested);
    },

    savePageData(context, pageData) {
        context.commit(storeMutations.UPDATE_PAGE_DATA, pageData);
    },

    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);
        }
        addGuidToLineItemsIfNotAlreadyThere(parts);
        context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
    },

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

    createSubmittedState(context) {
        if (window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== null) {
            return;
        }

        // create a submitted order object from vuex.
        const submittedState = {
            order: context.state.order,
            applicationUser: context.state.applicationUser,
        };

        const experiments = context.state.applicationUser.experiments;
        const affiliateCookies = context.state.applicationUser.affiliateCookies;

        // set to local storage
        window.sessionStorage.setItem(
            sessionStorageKeyConstants.SUBMITTED_STATE,
            JSON.stringify(submittedState)
        );
        window.sessionStorage.setItem("createNewSessionForHeritage", true);

        // clear vuex
        context.commit(storeMutations.RESET_STATE);

        // delete cookie
        deleteFunnelCookie();

        //restore user's experiments
        context.commit(storeMutations.UPDATE_EXPERIMENTS, experiments);

        //clear externalParameter session storage
        window.sessionStorage.removeItem("externalParameterState");

        //restore affiliate cookies
        context.commit(storeMutations.UPDATE_AFFILIATE_COOKIES, affiliateCookies);
    },

    addDonationToSubmittedState(context, donationAmount) {
        const submittedOrder = deepClone(baseMixin.methods.getSubmittedOrder());
        const submittedApplicationUser = deepClone(baseMixin.methods.getSubmittedApplicationUser());

        submittedOrder.lineItems.supportingItems.push({
            partNumber: `DONATION_${donationAmount}`,
            description: `DONATION $${donationAmount}`,
            partType: partTypeStrings.DONATION,
            isInsurable: false,
            isChildPart: false,
            laborAmount: 0,
            sellingPrice: donationAmount,
            kitPrice: 0,
            salesTax: 0,
        });

        addGuidToLineItemsIfNotAlreadyThere(submittedOrder.lineItems.supportingItems);

        const submittedState = {
            order: submittedOrder,
            applicationUser: submittedApplicationUser,
        };
        // set to local storage
        window.sessionStorage.setItem(
            sessionStorageKeyConstants.SUBMITTED_STATE,
            JSON.stringify(submittedState)
        );
    },

    resetSubmittedState(context) {
        // clear from local storage
        window.sessionStorage.removeItem(sessionStorageKeyConstants.SUBMITTED_STATE);
    },
    resetExternalParameterState(context) {
        if (context.getters.isExternalParameter) {
            context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_VEHICLE_STATE);
            context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_DAMAGE_STATE);
            context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_ESTIMATE_STATE);
            context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_SERVICEZIP_STATE);
            context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_QUOTE_STATE);
            context.commit(storeMutations.RESET_EXTERNAL_PARAMETER_CUSTOMER_STATE);
            context.commit(storeMutations.RESET_IS_EXTERNAL_PARAMETER);
        }
    },
    updateExternalParameterMMS(context, externalParameterMMS) {
        context.commit(
            storeMutations.UPDATE_EXTERNAL_PARAMETER_MAKE,
            externalParameterMMS.externalParameterMake
        );
        context.commit(
            storeMutations.UPDATE_EXTERNAL_PARAMETER_MODEL,
            externalParameterMMS.externalParameterModel
        );
        context.commit(
            storeMutations.UPDATE_EXTERNAL_PARAMETER_STYLE,
            externalParameterMMS.externalParameterStyle
        );
    },

    saveLoggingOption(context, loggingOption) {
        var option = false;
        if (loggingOption != null) {
            option = loggingOption == "true" ? true : false;
        }

        context.commit(storeMutations.UPDATE_LOGGING_OPTION, option);
    },

    async getRecalPartsAndSaveToLineItems(context, { pageNameToLog }) {
        // identify each part that needs recal
        const glassParts = context.state.order?.lineItems?.glassParts
            ? deepClone(context.state.order.lineItems.glassParts)
            : [];

        for (let i = 0; i < glassParts.length; i++) {
            // does this part need recal?
            const needsRecal =
                !!glassParts[i].requiresRecalibration && !!glassParts[i].recalibrationType;

            if (needsRecal) {
                const recalType = glassParts[i].recalibrationType;
                const parentAccountNumber =
                    context.state.order.payment.parentAccountNumber ??
                    applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;

                // Get part from API
                const urlExtension = `${context.state.order.vehicle.carId}/${glassParts[i].partNumber}/${recalType}/${parentAccountNumber}/${context.state.order.serviceLocation.zipCode}/${applicationConfig.ANALYTICS_APPLICATION_NAME}/${context.state.order.referralSequenceNumber}`;

                const recalPartResponse = await globalMethods.callHttpClient({
                    method: endpoints.GetRecalPart.method,
                    endpoint: `${endpoints.GetRecalPart.url}/${urlExtension}`,
                    payload: {},
                    pageNameToLog: pageNameToLog,
                });

                // If any parts retrieved, add as children to the glass part.
                if (
                    recalPartResponse.status === 200 &&
                    recalPartResponse.data.recalibrationParts?.length > 0
                ) {
                    if (!glassParts[i].childParts) {
                        glassParts[i].childParts = [];
                    }

                    glassParts[i].childParts.push(...recalPartResponse.data.recalibrationParts);
                    context.dispatch(storeActions.SAVE_GLASS_PARTS, glassParts);
                }
            }
        }
    },
};

export default createStore({
    plugins: [
        createPersistedState(),
        sharedMutations({
            predicate: [...Object.values(storeMutations)],
        }),
    ],
    // 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) {
    return containsRecalParts(state.order.lineItems);
}

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) => {
        const lineItemIndex = pricingLineItems.findIndex(
            (pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber
        );

        if (lineItem.childParts) {
            addPricesToLineItems(lineItem.childParts, pricingLineItems);
        }

        const pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0];
        lineItem.laborAmount = pricedLineItem.laborAmount;
        lineItem.sellingPrice = pricedLineItem.sellingPrice;
        lineItem.kitPrice = pricedLineItem.kitPrice;
        lineItem.salesTax = pricedLineItem.salesTax;
    });

    return lineItems;
}

function addTaxesToPricedLineItems(pricedLineItems, taxingLineItems = []) {
    pricedLineItems.forEach((pricedLineItem) => {
        const lineItemIndex = taxingLineItems.findIndex(
            (taxingLineItem) => taxingLineItem.partNumber === pricedLineItem.partNumber
        );

        if (pricedLineItem.childParts) {
            addTaxesToPricedLineItems(pricedLineItem.childParts, taxingLineItems);
        }

        const taxedLineItem = taxingLineItems.splice(lineItemIndex, 1)[0];
        pricedLineItem.salesTax = taxedLineItem?.salesTax ?? 0;
    });

    return pricedLineItems;
}

export function mapTaxedLineItemsToStoreFormat(availableLineItems, storeLineItems) {
    // clone the lineItems array because what we're passing in is referencing the store directly
    const lineItems = deepClone(storeLineItems);

    for (let [category, lineItemsInCategory] of Object.entries(lineItems)) {
        lineItemsInCategory = lineItemsInCategory ?? [];

        if (category == "supportingItems") {
            // if the category is supporting items we need to filter out the items that aren't repair chips
            const nonRepairChipSupportItemsLineItems = lineItemsInCategory.filter(
                (lineItem) => lineItem.partNumber != "WSREPAIR"
            );

            /*
                Because repair chips all have the same part number but different prices based on the quantity,
                we have to sort the store line items and available line items by descending labor amount in order to 
                map the tax correctly to each repair chip
             */
            // get the supporting items that ARE repair chips and sort them by descending labor amount
            let repairChipLineItems = lineItemsInCategory.filter(
                (lineItem) => lineItem.partNumber == "WSREPAIR"
            );
            repairChipLineItems = repairChipLineItems.sort(
                (a, b) => parseFloat(b.laborAmount) - parseFloat(a.laborAmount)
            );

            // get the supporting items from the available line items (taxed) that ARE repair chips and sort them by descending labor amount
            let availableRepairChipLineItems = availableLineItems.filter(
                (lineItem) => lineItem.partNumber == "WSREPAIR"
            );
            availableRepairChipLineItems = availableRepairChipLineItems.sort(
                (a, b) => parseFloat(b.laborAmount) - parseFloat(a.laborAmount)
            );

            // go through each one of those mapping the taxes to the correct chip
            for (let i = 0; i < availableRepairChipLineItems.length; i++) {
                repairChipLineItems[i].salesTax = availableRepairChipLineItems[i].salesTax;
            }

            // then we map the non-repair chip items based on part number
            for (
                let lineItemIndex = 0;
                lineItemIndex < nonRepairChipSupportItemsLineItems.length;
                lineItemIndex++
            ) {
                const availableLineItem = availableLineItems.find(
                    (ali) =>
                        ali.partNumber ==
                        nonRepairChipSupportItemsLineItems[lineItemIndex].partNumber
                );
                if (availableLineItem) {
                    nonRepairChipSupportItemsLineItems[lineItemIndex].salesTax =
                        availableLineItem.salesTax;
                }
            }

            // finally we splice the two arrays back into one
            lineItemsInCategory = nonRepairChipSupportItemsLineItems.concat(repairChipLineItems);
        } else {
            for (
                let lineItemIndex = 0;
                lineItemIndex < lineItemsInCategory.length;
                lineItemIndex++
            ) {
                const availableLineItem = availableLineItems.find(
                    (ali) => ali.partNumber == lineItemsInCategory[lineItemIndex].partNumber
                );
                if (availableLineItem) {
                    lineItemsInCategory[lineItemIndex].salesTax = availableLineItem.salesTax;
                }
            }
        }
    }

    return lineItems;
}

export function getArrayOfAllLineItemsAndChildParts(lineItems) {
    let consolidatedLineItemsArray = [];

    if (lineItems.glassParts != null)
        consolidatedLineItemsArray = [
            ...consolidatedLineItemsArray,
            ...getFlattenedArrayOfLineItemsWithChildParts(lineItems.glassParts),
        ];

    if (lineItems.supportingItems != null)
        consolidatedLineItemsArray = [
            ...consolidatedLineItemsArray,
            ...getFlattenedArrayOfLineItemsWithChildParts(lineItems.supportingItems),
        ];

    if (lineItems.vaps != null)
        consolidatedLineItemsArray = [
            ...consolidatedLineItemsArray,
            ...getFlattenedArrayOfLineItemsWithChildParts(lineItems.vaps),
        ];

    if (lineItems.promos != null)
        consolidatedLineItemsArray = [
            ...consolidatedLineItemsArray,
            ...getFlattenedArrayOfLineItemsWithChildParts(lineItems.promos),
        ];

    return consolidatedLineItemsArray;
}

function getFlattenedArrayOfLineItemsWithChildParts(lineItems, childPartRecursiveCall = false) {
    let flattenedArray = [];
    lineItems?.forEach((lineItem) => {
        // this assumes childparts will never be a glass part
        lineItem.isChildPart = childPartRecursiveCall;
        flattenedArray.push(lineItem);
        if (lineItem.childParts) {
            flattenedArray = [
                ...flattenedArray,
                ...getFlattenedArrayOfLineItemsWithChildParts(lineItem.childParts, true),
            ];
        }
    });

    return flattenedArray;
}

function getFlattenedLineItemsWithGlassPartTag(lineItems) {
    const nonGlassLineItems = [...(lineItems.supportingItems ?? []), ...(lineItems.vaps ?? [])];
    const mappedNonGlassLineItems = nonGlassLineItems.map((lineItem) => {
        return {
            partNumber: lineItem.partNumber,
            partType: lineItem.partType,
            isGlassPart: false,
        };
    });

    const glassLineItems = [...getFlattenedArrayOfLineItemsWithChildParts(lineItems.glassParts)];
    const mappedGlassLineItems = glassLineItems.map((lineItem) => {
        return {
            partNumber: lineItem.partNumber,
            partType: lineItem.partType,
            isGlassPart: lineItem.isChildPart ? false : true,
        };
    });

    return [...mappedNonGlassLineItems, ...mappedGlassLineItems];
}

function buildQueryStringParameterFromArrayOfComplexObjects(arrayOfObjects, parameterName) {
    let queryStringParameter = "";
    for (let i = 0; i < arrayOfObjects.length; i++) {
        for (const [key, value] of Object.entries(arrayOfObjects[i])) {
            if (value || value === 0) {
                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 renameGlassToReplaceAttributes(glassToReplace) {
    let newGlassToReplace = [];
    if (glassToReplace) {
        newGlassToReplace = glassToReplace.map((item) => {
            return { location: item.glassLocation, name: item.glassName };
        });
    }
    return newGlassToReplace;
}

function addGuidToLineItemsIfNotAlreadyThere(lineItems) {
    lineItems?.forEach((lineItem) => {
        if (!lineItem.id) {
            lineItem.id = crypto.randomUUID();
        }
        if (lineItem.childParts) {
            addGuidToLineItemsIfNotAlreadyThere(lineItem.childParts);
        }
    });
}

function syncLineItemIds(lineItemsWithoutIds, lineItemsWithIds) {
    if (!lineItemsWithIds || !lineItemsWithoutIds) {
        return;
    }
    var clonedLineItemsWithIds = deepClone(lineItemsWithIds);
    lineItemsWithoutIds.forEach((noId) => {
        var matchedLineItemIndex = -1;
        clonedLineItemsWithIds.forEach((withId, index) => {
            if (noId.partNumber === withId.partNumber && noId.partType === withId.partType) {
                matchedLineItemIndex = index;
                noId.id = withId.id;
            }
        });
        if (matchedLineItemIndex > -1) {
            // Remove the matched line item
            clonedLineItemsWithIds.splice(matchedLineItemIndex, 1);
        }
    });
}

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.
}

function supportingItemsEqual(supportingItemsA, supportingItemsB) {
    if (typeof supportingItemsA !== typeof supportingItemsB) {
        return false;
    }
    if (supportingItemsA === null || supportingItemsB === null) {
        return supportingItemsA === supportingItemsB;
    }
    if (supportingItemsA.length != supportingItemsB.length) {
        return false;
    }
    const sortedA = supportingItemsA.slice().sort();
    const sortedB = supportingItemsB.slice().sort();
    for (let i = 0; i < sortedA.length; i++) {
        if (
            sortedA[i].partNumber != sortedB[i].partNumber ||
            sortedA[i].partType != sortedB[i].partType
        ) {
            return false;
        }
    }
    return true;
}

// 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, pageNameToLog) {
    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,
            {
                payload: {
                    startDate: order.schedule.date,
                    endDate: endDate,
                },
                pageNameToLog: pageNameToLog,
            },
            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,
            {
                payload: {
                    startDate: order.schedule.date,
                    endDate: endDate,
                    shopAppointmentType: order.serviceLocation.appointmentType,
                    providerNumber: order.serviceLocation.provider.providerNumber,
                },
                pageNameToLog: pageNameToLog,
            },
            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;
                }
            });
        });

        //reset schedule if jobMin is null because when switching from cash to insurance dynamo returns null for jobMin
        if (!routeCodeFound || order.schedule.jobMinMinutes == null) {
            context.commit(storeMutations.RESET_SCHEDULE);
            return;
        }
    }
}
function createExternalParameterDefaultState() {
    // create default externalParameter state
    const externalParameterDefaultState = {
        isExternalParameter: externalParameterStatus.NOT_SET,
        vehicle: {
            year: null,
            make: null,
            model: null,
            style: null,
        },
        vehicleDamage: {
            damageType: null,
            isRepair: null,
            numberOfChips: null,
        },
        estimate: {
            vinSelection: null,
        },
        serviceZip: {
            zipCode: null,
        },
        quote: {
            isInsurance: null,
            servicePackage: null,
        },
        customer: {
            emailAddress: null,
            phoneNumber: null,
        },
    };
    // set to session storage
    saveExternalParameterState(externalParameterDefaultState);
}
function getExternalParameterDefaultState() {
    const externalParameterState = window.sessionStorage.getItem("externalParameterState");
    if (externalParameterState === null) {
        createExternalParameterDefaultState();
    }
    return JSON.parse(window.sessionStorage.getItem("externalParameterState"));
}
function saveExternalParameterState(externalParameterState) {
    window.sessionStorage.setItem("externalParameterState", JSON.stringify(externalParameterState));
}
