<template>
    <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
        <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
        <div class="container">
            <div class="row">
                <div class="col-12">
                    <div>
                        <alert
                            ref="piaErrorAlert"
                            class="my-4"
                            v-if="hasPaymentFailureError"
                            cmsWidgetName="PIAErrorAlertWidget"
                            alertClass="alert-danger"
                            @textLinkClicked="paymentFailedPayLater"
                            v-bind:isDismissible="false" />
                    </div>
                    <div id="card-container">
                        <div id="payment-container">
                            <funnelSubHeader class="py-5" cmsWidgetName="FunnelSubHeaderWidget" />
                            <div id="adyen-container"></div>
                        </div>

                        <div id="cart-container">
                            <cart
                                ref="cart"
                                :damage="damageInfo"
                                :availableVaps="[]"
                                :allowItemRemoval="false"
                                v-model="lineItems"
                                servicePackageOptionsCmsName="ServicePackageTitle"
                                recyclingModalCmsWidgetName="RecycleModal"
                                :isInsurance="isInsurance"
                                :insuranceDeductible="currentDeductible"
                                :insuranceCompanyName="insuranceCompanyName"
                                :showInsuranceCoverageAs="showInsuranceCoverageAs"
                                :isItac="isItac"
                                :isNoComp="isNoComp"
                                :isCollapsible="false" />
                            <textBlock
                                class="payment-disclaimer"
                                marginTopSizeOverride="0"
                                cmsWidgetName="PaymentDisclaimerWidget" />
                        </div>
                    </div>

                    <navbar
                        cmsWidgetName="FunnelFooterWidget"
                        ref="navbar"
                        isForwardActionDisabled="true"
                        isSubmitHidden="true"
                        @back-clicked="backButtonAction" />
                </div>
            </div>
        </div>
    </Form>
</template>
<script>
import Form from "vee-validate";
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 alert from "@/ux-components/alert/alert";
import textBlock from "@/digital-components/text-block/text-block";

import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import globalMethods from "@/global-methods";
import { endpoints } from "../../constants/endpoints";
import { AppointmentTypeStrings } from "../../constants/schedule-constants";
import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions";
import { getAmountDue } from "@/helpers/pricing-helper.js";
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
import { createAdyenCheckout } from "@/helpers/adyen-helper";
import { Dropin } from "@adyen/adyen-web/auto";
import { applicationConfig } from "@/constants/application-config";
import { paymentMethods } from "@/constants/payment-method-constants";

import "@adyen/adyen-web/styles/adyen.css";
import { mapAdyenToFmgPaymentMethod, mapFmgToAdyenPaymentMethod } from "../../helpers/adyen-helper";
import { showFmgLoadingModal } from "../../helpers/loading-modal-helper";
import cart from "@/fmg-components/cart/cart";
import { coverageStatus } from "@/constants/insurance";
import { deepClone } from "@/helpers/object-helper";
import store from "@/store";
import {
    flushPagePrereqsLogs,
    hasServiceLocationInfo,
    hasInsuranceInfo,
    hasSchedulingInfo,
    hasCustomerInfo,
    hasPaymentMethodInfo,
    hasGlassPartsOrRepairInfo,
} from "@/helpers/page-prerequisites-helper.js";

export default {
    name: "payment-adyen",
    mixins: [],
    data() {
        return {
            sessionId: String,
            dropinComponent: null,
            hasPaymentFailureError: false,
        };
    },

    async beforeRouteEnter(to, from, next) {
        // Call APIs
        const cmsContentPromise = fetchCmsContentForPage(to.name);

        // Settle API calls in parallel before handling results.
        const promiseResultMap = [
            {
                resultKey: "cmsContent",
                promise: cmsContentPromise,
            },
        ];

        const resultMap = await settleAllPromises(promiseResultMap);

        const hasExistingPiaError = to.query?.piaFailure;

        // Hydrate page with results
        next(async (vm) => {
            vm.setCmsContent(resultMap.cmsContent);

            if (hasExistingPiaError) {
                vm.hasPaymentFailureError = true;
            }
        });
    },

    methods: {
        async backButtonAction() {
            // Stub, for navigating back via nav-bar.
            this.$router.navigateWithoutSaving(
                this.navigationScenarios.CLICKED_BACK,
                this.pageName
            );
        },

        async forwardButtonAction() {
            // Stub, for navigating forward via nav-bar
        },

        arePagePrerequisitesValid() {
            const order = store.getters.order;
            const logQueue = [];
            const result =
                hasServiceLocationInfo(order, logQueue) &&
                hasInsuranceInfo(order, logQueue) &&
                hasSchedulingInfo(order, logQueue) &&
                hasCustomerInfo(order, logQueue) &&
                hasPaymentMethodInfo(order, logQueue) &&
                hasGlassPartsOrRepairInfo(order, logQueue);
            flushPagePrereqsLogs("payment-adyen.vue", result, logQueue);
            return result;
        },

        async initializeAdyen() {
            console.log(`Price = ${this.amountDue}`);
            console.log(`Adyen Price = ${this.adyenPriceTotal}`);

            const requestBody = this.getAdyenInitRequestInfo();

            console.log(`Calling with:`);
            console.log(requestBody);

            const response = await globalMethods.callHttpClient({
                method: endpoints.InitializeAdyenPayment.method,
                endpoint: endpoints.InitializeAdyenPayment.url,
                payload: requestBody,
                logApiCall: true,
                pageNameToLog: "payment-adyen",
            });

            const responseJson = response?.data;

            console.log(`Got data:`);
            console.log(responseJson);

            const session = {
                id: responseJson.sessionId,
                sessionData: responseJson.sessionData,
            };

            this.sessionId = responseJson.sessionId;

            const checkout = await createAdyenCheckout({
                session: session,
                handlers: {
                    onPaymentCompleted: (result, component) => {
                        console.log(`Payment completed from Adyen.`);
                        this.handleCompletedPayment(result);
                    },
                    onPaymentFailed: (result, component) => {
                        console.log(`Payment failed from Adyen`);
                        this.handleFailedPayment(result);
                    },
                    onError: (error, component) => {
                        console.log(`Error from Adyen`);
                        this.handleError(error);
                    },
                },
                options: {
                    amount: this.adyenPriceTotal,
                },
            });

            console.log(checkout);

            const expiryTime = new Date(checkout.options.expiresAt);
            const expiryInterval = expiryTime.getTime() - new Date().getTime();

            const handleTimeout = () => {
                this.resetAdyenDropin();
            };

            const timeout = setTimeout(handleTimeout, expiryInterval);

            const configuration = {
                paymentMethodsConfiguration: {
                    ideal: {
                        showImage: true,
                    },
                    paypal: {
                        amount: {
                            value: this.adyenPriceTotal,
                            currency: "USD",
                        },
                        environment: applicationConfig.ADYEN_ENVIRONMENT,
                        countryCode: "US", // Only needed for test. This will be automatically retrieved when you are in production.
                        blockPayPalVenmoButton: true,
                        blockPayPalPayLaterButton: true,
                    },
                    card: {
                        hasHolderName: true,
                        holderNameRequired: true,
                        billingAddressRequired: true,
                        name: "Credit or debit card",
                    },
                },
            };

            const adyenPaymentType = mapFmgToAdyenPaymentMethod(this.piaType);

            if (adyenPaymentType) {
                configuration.openPaymentMethod = {
                    type: adyenPaymentType,
                };
            }

            const dropin = new Dropin(checkout, configuration);

            this.dropinComponent = dropin;

            dropin.mount("#adyen-container");
        },

        async handleCompletedPayment(result) {
            console.log(`Result =`);
            console.log(result);
            // Fetch session info
            const paymentSessionRequest = {
                zipCodeCtu: this.locationInfo.zipCodeCtu,
                workOrderNumber: this.workOrderNumberLastSixDigits,
                sourceSystem: this.sourceSystem,
                sessionId: this.sessionId,
                sessionResult: result?.sessionResult,
            };

            console.log(`Get session payload =`);
            console.log(paymentSessionRequest);

            showFmgLoadingModal(true);

            const response = await globalMethods.callHttpClient({
                method: endpoints.GetAdyenSessionResult.method,
                endpoint: endpoints.GetAdyenSessionResult.url,
                payload: paymentSessionRequest,
                logApiCall: true,
                pageNameToLog: "payment-adyen",
            });

            const responseJson = response?.data;

            console.log(`Session response =`);
            console.log(responseJson);

            const ccToken = {
                subscriptionId: responseJson?.storedToken,
                expMonth: responseJson?.cardExpiryMonth,
                expYear: responseJson?.cardExpiryYear,
                cardType: responseJson?.cardType,
                billToPostalCode: this.locationInfo.zipCode, // TODO
                billToFirstName: this.$store.getters.order.customer.firstName, // TODO
                billToLastName: this.$store.getters.order.customer.lastName, // TODO
                referenceNumber: responseJson?.workOrderNumber,
                authCode: responseJson?.authCode,
                transactionId: responseJson?.transactionReference, // TODO
                transReferenceNumber: responseJson?.transactionReference,
                lastFour: responseJson?.last4DigitsOfCard,
            };

            console.log(`CC Token generated =`);
            console.log(ccToken);

            const paymentMethodFromSession = responseJson?.paymentMethod;

            const paymentMethod = mapAdyenToFmgPaymentMethod(paymentMethodFromSession);

            // Save payment type.
            // For now, CC.
            // Need to determine payment type from Adyen response
            await this.dispatchStoreAction(
                storeActions.SAVE_PAYMENT_METHOD_CHOICE,
                paymentMethod,
                false
            );

            if (paymentMethod === paymentMethods.PAYPAL) {
                await baseMixin.methods.dispatchStoreAction(
                    storeActions.SAVE_PAYPAL_TOKEN,
                    responseJson?.storedToken,
                    false
                );
            } else {
                await baseMixin.methods.dispatchStoreAction(
                    storeActions.SAVE_CCTOKEN,
                    ccToken,
                    false
                );
            }

            await baseMixin.methods.dispatchStoreAction(
                storeActions.SAVE_NEXTGEN_SETTLED_AMOUNT,
                this.amountDue,
                false
            );

            await this.saveAndSubmitWorkOrder();
        },
        async handleFailedPayment(result) {
            console.log(`Result =`);
            console.log(result);

            const wasPaymentCancelled = result?.resultCode === "Cancelled";

            if (!wasPaymentCancelled) {
                const stringToLog = `ADYEN PAYMENT NOT AUTHORIZED. Code = ${result?.resultCode}. Id = ${this.sessionId}`;

                await global.$logger.logError(stringToLog);

                this.hasPaymentFailureError = true;
            }

            this.dropinComponent?.update();
        },
        async handleError(error) {
            console.log(error);

            const wasPaymentCancelled = error?.name === "CANCEL";

            if (!wasPaymentCancelled) {
                const stringToLog = `ADYEN ERROR. Name = ${error?.name}. Details = ${error?.message}.`;

                await global.$logger.logError(stringToLog);

                this.hasPaymentFailureError = true;
            }

            this.dropinComponent?.update();
        },

        async saveAndSubmitWorkOrder() {
            // Work order submission after successful payment.
            await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
            try {
                await submitWorkOrder({
                    pageNameToLog: this.pageName,
                    submitAfterSave: true,
                });
            } catch (error) {
                console.log(`error: response from submit work order: ${error.message}`);

                this.$router.navigateWithoutSaving(
                    this.navigationScenarios.PIA_ERROR,
                    this.pageName
                );

                return;
            }

            // clear order
            await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE);
            this.$router.navigateWithoutSaving(
                this.navigationScenarios.CLICKED_FORWARD,
                this.pageName
            );
        },

        async paymentFailedPayLater() {
            console.log(`Doing final work order submission...`);
            showFmgLoadingModal(true);

            await this.dispatchStoreAction(
                storeActions.SAVE_PAYMENT_METHOD_CHOICE,
                paymentMethods.PAY_LATER,
                false
            );

            await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);

            try {
                await submitWorkOrder({
                    pageNameToLog: this.pageName,
                    submitAfterSave: true,
                });

                await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE);

                this.$router.navigateWithoutSaving(
                    this.navigationScenarios.CLICKED_PAY_LATER,
                    this.pageName
                );
            } catch (error) {
                console.log(`Final submission failed after failed payment.`);
                this.$router.handleSoftError();
                return;
            }
        },

        async resetAdyenDropin() {
            this?.dropinComponent?.unmount();
            await this.initializeAdyen();
        },

        // Now a Method so it is always freshly called and not cached.
        getIdempotencyKey() {
            const currentDateTime = new Date();
            const currentHour = currentDateTime.getUTCHours();
            const currentDate = currentDateTime.getUTCDate();
            const id = this?.$store?.getters?.order?.referralCorrelationId;
            const system = this.sourceSystem;
            const total = this.adyenPriceTotal;

            return `${id}-${system}-${currentDate}-${currentHour}-${total}`;
        },

        // Now a Method so it is always freshly called and not cached.
        getAdyenInitRequestInfo() {
            return {
                sourceSystem: this.sourceSystem,
                referralSequenceNumber: this.$store.getters.order.referralSequenceNumber,
                workOrderNumber: this.workOrderNumberLastSixDigits,
                zipCodeCtu: this.locationInfo.zipCodeCtu,
                amount: this.adyenPriceTotal,
                city: this.locationInfo.city,
                houseNumberOrName: this.splitStreetAddress.number,
                street: this.splitStreetAddress.street,
                zipCode: this.locationInfo.zipCode,
                stateOrProvince: this.locationInfo.state,
                returnUrl: applicationConfig.PIA_ADYEN_RETURN_URL,
                email: this.$store.getters.order.customer.emailAddress,
                IP: "127.0.0.1", // TODO
                firstName: this.$store.getters.order.customer.firstName,
                lastName: this.$store.getters.order.customer.lastName,
                idempotencyKey: this.getIdempotencyKey(),
            };
        },
    },

    computed: {
        piaType() {
            return this.$store.getters.order.payment.piaType;
        },

        workOrderNumberLastSixDigits() {
            const workOrderNumber = this.$store.getters.order.workOrderNumber ?? "";
            const tokens = workOrderNumber.split("-");
            const lastSegment = tokens[tokens.length - 1] ?? "";
            const numDigits = lastSegment.length;
            const numZeroes = 6 - numDigits;

            let result = "";
            for (let i = 0; i < numZeroes; i++) {
                result += "0";
            }

            result += lastSegment;

            return result;
        },

        locationInfo() {
            const serviceLocation = this.$store.getters.order.serviceLocation;
            const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
            if (isMobile) {
                return {
                    address: serviceLocation.address,
                    address2: serviceLocation.address2,
                    city: serviceLocation.city,
                    state: serviceLocation.state,
                    zipCode: serviceLocation.zipCode,
                    zipCodeCtu: serviceLocation.zipCodeCtu,
                };
            } else {
                const providerInfo = serviceLocation.provider.address;
                return {
                    address: providerInfo.streetAddress,
                    address2: "",
                    city: providerInfo.city,
                    state: providerInfo.state,
                    zipCode: providerInfo.zipCode,
                    zipCodeCtu: providerInfo.zipCodeCtu,
                };
            }
        },

        splitStreetAddress() {
            const address = this.locationInfo.address;
            const tokens = address.split(" ") ?? [""];
            const number = tokens[0];

            const street = tokens.slice(1).reduce((prev, next) => `${prev} ${next}`);

            return {
                number: number ?? "",
                street: street ?? "",
            };
        },

        sourceSystem() {
            return "FMG-2.0";
        },

        amountDue() {
            return getAmountDue(this.$store.getters.order.lineItems);
        },

        adyenPriceTotal() {
            return Math.round(this.amountDue * 100);
        },

        // Cart info
        damageInfo() {
            return this.$store.getters.damage;
        },
        isInsurance() {
            return this.$store.getters.payment.isInsurance;
        },
        isNoComp() {
            return this.$store.getters.policy.isNoComp;
        },
        isItac() {
            return this.$store.getters.policy.isItac;
        },
        showInsuranceCoverageAs() {
            if (!this.isInsurance) {
                return null;
            }

            if (this.isNoComp || this.isItac) {
                return coverageStatus.NOCOMP;
            } else {
                return this.$store.getters.payment.insuranceCoverage.coverageStatus;
            }
        },
        currentDeductible() {
            return this.$store.getters.policy.currentDeductible;
        },
        insuranceCompanyName() {
            return this.$store.getters.policy.insuranceCompanyName;
        },
        lineItems() {
            return deepClone(this.$store.getters.lineItems);
        },
    },

    mounted() {
        this.initializeAdyen();
    },

    components: {
        Form,
        funnelHeader,
        funnelSubHeader,
        navbar,
        cart,
        alert,
        textBlock,
    },
};
</script>
<style lang="scss">
#card-container {
    display: flex;
    & > #payment-container {
        flex-basis: 55%;
        margin-right: 9%;
    }
    & > #cart-container {
        flex-basis: 36%;
        margin-top: 2rem;
    }

    @include media-breakpoint-down(lg) {
        flex-direction: column;
        & > #payment-container {
            flex-basis: auto;
            margin-right: 0;
        }
        & > #cart-container {
            flex-basis: auto;
            margin: 0 0.5rem;
        }
    }

    .payment-disclaimer p {
        font-size: 0.875rem;
        line-height: 1.2;
    }
}
</style>
