<template>
    <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
        <loadingModal notFullScreen ref="loadingModal" />
        <div class="container-fluid">
            <div class="row justify-content-center">
                <div class="col-md-6">
                    <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
                </div>
            </div>
            <div class="row justify-content-center">
                <div class="col-md-6 col-xl-4">
                    <vehicleBanner
                        cmsWidgetName="VehicleBannerWidget"
                        :displayGenericVehicleImage="false" />

                    <funnelSubHeader class="mb-5 mt-4" cmsWidgetName="FunnelSubHeaderWidget" />

                    <p v-if="isPiaDisabled" class="fw-normal no-pia-disclaimer">
                        <span>{{ noPiaDisclaimer }}</span>
                    </p>

                    <hr class="my-0" />

                    <reviewDropdown ref="reviewDropdown" />

                    <hr class="my-0" />

                    <cart
                        :damage="damageInfo"
                        :availableVaps="availableVaps"
                        :allowItemRemoval="true"
                        v-model="lineItems"
                        servicePackageOptionsCmsName="ServicePackageTitle"
                        recyclingModalCmsWidgetName="RecycleModal" />

                    <promoModalQuestion
                        class="small mt-4"
                        v-model="lineItems"
                        :availableVaps="availableVaps"
                        modalWidgetName="PromoModalWidget" />

                    <hr class="my-5" />

                    <div>
                        <alert
                            ref="piaErrorAlert"
                            class="my-4"
                            v-if="shouldDisplayPiaAlert"
                            cmsWidgetName="PIAErrorAlertWidget"
                            alertClass="alert-danger"
                            v-bind:isDismissible="false" />
                    </div>

                    <add-vaps-modal-buttons
                        v-model="lineItems"
                        :availableVaps="availableVaps"
                        vapsTilesCmsName="VapsProductTiles"
                        @added-to-cart="showAlert" />

                    <paymentMethodQuestion
                        v-if="!isPiaDisabled"
                        v-model="paymentMethodInternalModel"
                        validationRules="option-required" />

                    <navbar
                        cmsWidgetName="FunnelFooterWidget"
                        ref="navbar"
                        :isForwardActionDisabled="!meta.valid"
                        :isBackButtonHidden="shouldHideBackButton"
                        @back-clicked="backButtonAction"
                        @ForwardClicked="forwardButtonAction" />
                </div>
            </div>
        </div>
    </Form>
</template>

<script>
// Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import paymentMethodQuestion from "@/layouts/payment-method/payment-method-question/payment-method-question";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import cart from "@/fmg-components/cart/cart";
import reviewDropdown from "@/layouts/payment-method/review-dropdown/review-dropdown";
import promoModalQuestion from "@/layouts/payment-method/promo-modal-question/promo-modal-question";
import addVapsModalButtons from "./add-vaps-modal-buttons/add-vaps-modal-buttons";
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
import alert from "@/ux-components/alert/alert";

// Supporting Items
import baseMixin from "@/mixins/base-mixin.js";
import { settleAllPromises } from "@/helpers/layout-helper";
import { storeActions } from "@/constants/store-actions";
import store from "@/store";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { paymentMethods } from "@/constants/payment-method-constants";
import { experimentSettings } from "@/constants/experiments";
import {
    revalidatePromosAndValidateQueryStringPromo,
    buildToastMessagesFromRevalidateOrValidatePromoResponse,
    getVapsThatNeedToBeAddedToSatisfyPromos,
    getPromoCodeWithoutBundleIdentifier,
    removeCurrentlyActivePromoCodesFromInactivePromos,
    createPromoSuccessAlert,
    createPromoErrorAlert,
    getNewlyInactivatedPromos,
} from "@/helpers/promotions-helper";
import { queryStrings } from "@/constants/query-strings";
import { routerParams } from "@/router/router-constants/router-params";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { deepClone } from "@/helpers/object-helper";

import { Form } from "vee-validate";
import { defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import { partTypeStrings } from "@/constants/part-type-strings";
import { mapTaxedLineItemsToStoreFormat } from "../../store";

defineRule("option-required", required(errorMessages.OPTION_REQUIRED));

export default {
    name: "paymentMethod",
    async beforeRouteEnter(to, from, next) {
        // Call APIs
        const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);

        const lineItemsFromStore = deepClone(store.getters.order.lineItems);
        const frontWipersOnOrder =
            lineItemsFromStore.vaps.filter(
                (wiper) => wiper.partType == partTypeStrings.FRONT_WIPER
            ) ?? [];
        const rearWipersOnOrder =
            lineItemsFromStore.vaps.filter(
                (wiper) => wiper.partType == partTypeStrings.REAR_WIPER
            ) ?? [];
        const orderHasFrontWipers = frontWipersOnOrder.length > 0;
        const orderHasRearWipers = rearWipersOnOrder.length > 0;

        const wipersPromise = orderHasFrontWipers
            ? baseMixin.methods.dispatchStoreActionWithLogging(
                  storeActions.GET_WIPERS,
                  {
                      serviceZipCode: store.getters.order.serviceLocation.zipCode,
                      carId: store.getters.vehicle.carId,
                  },
                  "payment-method"
              )
            : Promise.resolve([]);

        const rainDefensePromise = baseMixin.methods.dispatchStoreActionWithLogging(
            storeActions.GET_RAIN_DEFENSE,
            null,
            "payment-method"
        );

        const reviewDropdownPromise = reviewDropdown.methods.loadInitialData();

        const promiseResultMap = [
            {
                resultKey: "cmsContent",
                promise: cmsContentPromise,
            },
            {
                resultKey: "wipers",
                promise: wipersPromise,
            },
            {
                resultKey: "rainDefense",
                promise: rainDefensePromise,
            },
            {
                resultKey: "reviewDropdownData",
                promise: reviewDropdownPromise,
            },
        ];

        const resultMap = await settleAllPromises(promiseResultMap);

        const glassParts = lineItemsFromStore.glassParts ?? [];
        const supportingItems = lineItemsFromStore.supportingItems ?? [];
        const vaps = lineItemsFromStore.vaps ?? [];

        // if the order already has wipers on it from the quote page, use those as the available wipers
        // instead of what comes from the backend. This is to prevent issues with part interchange.
        const availableFrontWipers = orderHasFrontWipers
            ? frontWipersOnOrder
            : resultMap.wipers.filter((wiper) => wiper.partType == partTypeStrings.FRONT_WIPER) ??
              [];
        const availableRearWipers = orderHasRearWipers
            ? rearWipersOnOrder
            : resultMap.wipers.filter((wiper) => wiper.partType == partTypeStrings.REAR_WIPER) ??
              [];

        const lineItemsToTax = [
            resultMap.rainDefense,
            ...supportingItems,
            ...availableFrontWipers,
            ...availableRearWipers,
            ...glassParts,
            ...vaps,
        ];

        const availableVaps = [
            resultMap.rainDefense,
            ...availableFrontWipers,
            ...availableRearWipers,
        ];

        const pricedLineItemsToTax = await baseMixin.methods.dispatchStoreActionWithLogging(
            storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
            {
                availableLineItems: lineItemsToTax,
            },
            "payment-method",
            false
        );

        // Promo logic
        // Populate the previous state of promos for toast message usage in "next()"
        const oldActivePromos = store.getters.lineItems.promos?.slice(0);
        const oldInactivePromos = store.getters.order.payment.inactivePromos?.slice(0);

        const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
        const { validatePromoResponse, revalidatePromoResponse } =
            await revalidatePromosAndValidateQueryStringPromo(
                promoCodeFromQueryString,
                pricedLineItemsToTax,
                "payment-method"
            );

        // Add newly validated promos to the array to get taxed
        const newValidatedPromos = validatePromoResponse?.orderPromos ?? [];
        newValidatedPromos.push(
            ...(revalidatePromoResponse ? revalidatePromoResponse.promoLineItems : [])
        );
        pricedLineItemsToTax.push(...newValidatedPromos);
        // End of promo logic

        const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging(
            storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
            {
                billToAccountNumber: "87291",
                providerNumber: store.getters.order.serviceLocation.provider.providerNumber,
                appointmentType: store.getters.order.serviceLocation.appointmentType,
                serviceLocationCity: store.getters.order.serviceLocation.city,
                serviceLocationState: store.getters.order.serviceLocation.state,
                serviceLocationZipCode: store.getters.order.serviceLocation.zipCode,
                pricedLineItems: pricedLineItemsToTax,
            },
            "payment-method",
            false
        );

        // Match all line items to the line items as they are in the store
        // and rebuild the original structure.
        const lineItems = mapTaxedLineItemsToStoreFormat(taxedLineItems, lineItemsFromStore);
        const taxedVaps = mapTaxedLineItemsToStoreFormat(taxedLineItems, availableVaps);

        lineItems.promos = newValidatedPromos ?? [];
        const vapsToAddToCart = getVapsThatNeedToBeAddedToSatisfyPromos(
            newValidatedPromos,
            taxedVaps,
            lineItems
        );
        lineItems.vaps = lineItems.vaps ?? [];
        lineItems.vaps.push(...vapsToAddToCart);

        // Call the "next" function to complete the transition to this page.
        next((vm) => {
            vm.setCmsContent(resultMap.cmsContent);
            vm.availableVaps = taxedVaps;
            vm.lineItems = lineItems;
            vm.inactivePromos = removeCurrentlyActivePromoCodesFromInactivePromos(
                vm.lineItems.promos,
                vm.inactivePromos
            );
            vm.updateFooterButtonText(vm.customCtaCopy);

            vm.$refs.reviewDropdown.initializeComponent(resultMap.reviewDropdownData);

            if (revalidatePromoResponse) {
                const revalidateAlerts = buildToastMessagesFromRevalidateOrValidatePromoResponse(
                    revalidatePromoResponse,
                    oldActivePromos,
                    oldInactivePromos
                );

                revalidateAlerts.forEach((alert) => {
                    vm.$refs.funnelHeader.pushGlobalAlert(alert, alert.shouldAutoFade);
                });
            }
            if (validatePromoResponse) {
                const validateAlerts =
                    buildToastMessagesFromRevalidateOrValidatePromoResponse(validatePromoResponse);
                vm.$refs.funnelHeader.pushGlobalAlert(
                    validateAlerts[0],
                    validateAlerts[0].shouldAutoFade
                );
            }
        });
    },
    data() {
        return {
            lineItems: [],
            availableVaps: [],
            paymentMethodInternalModel: this.getPaymentMethodFromStore(),
            inactivePromos: this.getInactivePromosFromStore(),
        };
    },
    methods: {
        arePagePrerequisitesValid() {
            // Line Items
            const packageReqs = !!store.getters.order.lineItems.supportingItems;

            // Service Location
            const serviceLocation = store.getters.order.serviceLocation;
            const mobileReqs = !!(
                serviceLocation.address &&
                serviceLocation.city &&
                serviceLocation.state &&
                serviceLocation.zipCode
            );

            const providerLocation = serviceLocation.provider.address;
            const dropOffInshopReqs = !!(
                providerLocation.streetAddress &&
                providerLocation.city &&
                providerLocation.state &&
                providerLocation.zipCode
            );

            const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;

            const serviceLocationReqs =
                (isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);

            // Insurance
            const isInsuranceSet = store.getters.order.payment.isInsurance !== null;

            // Schedule
            const schedule = store.getters.order.schedule;
            const scheduleReqs = !!(
                schedule.date &&
                schedule.startTime &&
                schedule.endTime &&
                schedule.jobMaxMinutes &&
                schedule.jobMinMinutes
            );

            // Customer
            const customer = store.getters.order.customer;
            const customerReqs = !!(
                customer.firstName &&
                customer.lastName &&
                customer.phoneNumber &&
                customer.emailAddress
            );

            return (
                packageReqs && serviceLocationReqs && isInsuranceSet && scheduleReqs && customerReqs
            );
        },
        getPaymentMethodFromStore() {
            const isPia = store.getters.order.payment.isPia;

            if (isPia === null || isPia === undefined) {
                return null;
            }

            if (isPia) {
                return store.getters.order.payment.piaType;
            } else {
                return paymentMethods.LATER;
            }
        },
        updateFooterButtonText(newValue) {
            this.$refs.navbar.updateButtonText(newValue);
        },
        getInactivePromosFromStore() {
            return this.$store.getters.order.payment.inactivePromos;
        },
        async revalidatePromos() {
            this.$refs.loadingModal.showModal();
            const revalidatePromoResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
                storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA,
                {
                    activePromosToUse: this.lineItems.promos,
                    inactivePromosToUse: this.inactivePromos,
                    lineItemsToUse: this.lineItems,
                },
                "payment-method",
                false
            );

            // Handle error alerts here, success alerts are handled in the watcher
            if (revalidatePromoResponse.errors.length) {
                getNewlyInactivatedPromos(
                    this.inactivePromos,
                    revalidatePromoResponse.errors
                ).forEach((promoCode) => {
                    const errorAlert = createPromoErrorAlert(promoCode);
                    this.$refs.funnelHeader.pushGlobalAlert(errorAlert, errorAlert.shouldAutoFade);
                });
            }

            if (revalidatePromoResponse.promoLineItems.length > 0) {
                await baseMixin.methods.dispatchStoreActionWithLogging(
                    storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
                    {
                        billToAccountNumber: "87291",
                        providerNumber:
                            this.$store.getters.order.serviceLocation.provider.providerNumber,
                        appointmentType: this.$store.getters.order.serviceLocation.appointmentType,
                        serviceLocationCity: this.$store.getters.order.serviceLocation.city,
                        serviceLocationState: this.$store.getters.order.serviceLocation.state,
                        serviceLocationZipCode: this.$store.getters.order.serviceLocation.zipCode,
                        pricedLineItems: revalidatePromoResponse.promoLineItems,
                    },
                    "payment-method",
                    false
                );
            }

            const vapsToAddToCart = getVapsThatNeedToBeAddedToSatisfyPromos(
                revalidatePromoResponse.promoLineItems,
                this.availableVaps,
                this.lineItems
            );

            this.lineItems.vaps.push(...vapsToAddToCart);

            this.lineItems.promos = revalidatePromoResponse.promoLineItems;
            this.inactivePromos = revalidatePromoResponse.errors.map((x) =>
                getPromoCodeWithoutBundleIdentifier(x.promoCode)
            );

            this.$refs.loadingModal.hideModal();
        },
        backButtonAction() {
            this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
        },
        async forwardButtonAction() {
            await this.dispatchStoreAction(
                storeActions.SAVE_PAYMENT_METHOD_CHOICE,
                this.paymentMethod,
                false
            );

            // save lineitems as they now have salestax added
            await this.dispatchStoreAction(
                storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING,
                this.lineItems.glassParts,
                false
            );

            await this.dispatchStoreAction(
                storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
                this.lineItems.supportingItems,
                false
            );
            await this.dispatchStoreAction(storeActions.SAVE_VAPS, this.lineItems.vaps, false);
            this.dispatchStoreAction(
                storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
                {
                    activePromos: this.lineItems.promos,
                    inactivePromos: this.inactivePromos,
                },
                false
            );

            if (this.paymentMethod == paymentMethods.LATER) {
                // this creates the final work order
                await submitWorkOrder({ pageNameToLog: "payment-method", submitAfterSave: true });
                this.$router.navigateWithoutSaving(
                    this.navigationScenarios.CLICKED_FORWARD,
                    this.$route
                );
            } else {
                this.setupPia();
            }
        },
        async setupPia() {
            this.$refs.loadingModal.showModal();

            // if we don't have a work order in delete substatus, set the flag and submit.
            // we need a work order number for pia so we can pass it to safeliteHop.
            if (!store.getters.order.workOrderNumber) {
                try {
                    await submitWorkOrder({
                        pageNameToLog: "payment-method",
                        submitAfterSave: false,
                        createDeleteStatusWorkOrderForPia: true,
                    });
                } catch (error) {
                    console.log("error: response from pia submit work order:" + error.message);
                    this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
                    this.$route.params[routerParams.DISPLAY_PIA_ALERT] = true;
                    return;
                }
            }
            this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
            this.$router.navigateWithoutSaving(
                this.navigationScenarios.CLICKED_PAY_NOW,
                this.$route
            );
        },
        showAlert(alert) {
            this.$refs.funnelHeader.pushGlobalAlert(alert, true);
        },
    },
    computed: {
        damageInfo() {
            return this.$store.getters.damage;
        },
        noPiaDisclaimer() {
            return this.getCmsContent("NoPiaDisclaimerWidget", "Text");
        },
        customCtaCopy() {
            switch (this.paymentMethod) {
                case paymentMethods.CREDIT_CARD:
                    return "Continue to checkout";
                case paymentMethods.PAYPAL:
                    return "Continue to Paypal";
                case paymentMethods.AFTERPAY:
                    return "Continue to Afterpay";
                default:
                    return null;
            }
        },
        isPiaDisabled() {
            const piaExperience = this.getSettingValue(experimentSettings.PIA_EXPERIENCE);

            const isEnabled = piaExperience === "PIA Optional" || piaExperience === "PIA Required";

            return !isEnabled;
        },
        paymentMethod() {
            if (this.isPiaDisabled) {
                return paymentMethods.LATER;
            }

            return this.paymentMethodInternalModel;
        },
        shouldDisplayPiaAlert() {
            return (
                this.$route.query[queryStrings.DISPLAY_PIA_ALERT] ||
                this.$route.params[routerParams.DISPLAY_PIA_ALERT]
            );
        },
        // Necessary to make the watcher of lineItems work
        // JavaScript does not keep a record of the old value, only a reference to it's location in the memory.
        //  This creates a separate location for oldValue/newValue so they can be compared
        // See Vue github issue #2164
        lineItemsCloneForWatcher() {
            return Object.assign({}, this.lineItems);
        },
    },
    watch: {
        customCtaCopy(newValue) {
            this.updateFooterButtonText(newValue);
        },
        lineItemsCloneForWatcher: {
            handler(newValue, oldValue) {
                if (
                    !oldValue ||
                    oldValue.length == 0 ||
                    !oldValue.vaps ||
                    !newValue ||
                    newValue.length == 0
                ) {
                    return;
                }

                if (oldValue.promos.length < newValue.promos.length) {
                    const oldPromoCodes = oldValue.promos.map(
                        (promoObject) => promoObject.promoCode
                    );

                    const newlyActivatedPromoCodes = newValue.promos.filter(
                        (newPromo) => !oldPromoCodes.includes(newPromo.promoCode)
                    );

                    const alert = createPromoSuccessAlert(newlyActivatedPromoCodes[0].promoCode);
                    this.$refs.funnelHeader.pushGlobalAlert(alert, alert.shouldAutoFade);
                } else if (
                    oldValue.promos.length == newValue.promos.length &&
                    oldValue.vaps.length != newValue.vaps.length
                ) {
                    this.revalidatePromos();
                }
            },
            deep: true,
        },
    },
    components: {
        funnelHeader,
        navbar,
        funnelSubHeader,
        Form,
        loadingModal,
        cart,
        alert,
        addVapsModalButtons,
        paymentMethodQuestion,
        promoModalQuestion,
        reviewDropdown,
    },
};
</script>

<style lang="scss" scoped>
.cart {
    margin-bottom: 0;
}
.no-pia-disclaimer {
    color: $gray-550;
    margin: 0;
}
</style>
