<template>
    <Form>
        <div class="fade-on-route-transition">
            <div class="justify-content-center">
                <siteHeader cmsWidgetName="SiteHeaderWidget" />
            </div>
            <div class="iss-heritage-container-width payment-page-container">
                <div id="card-container" class="row">
                    <div class="col-lg-8">
                        <alert
                            v-if="hasPaymentFailureError"
                            name="payInAdvanceErrorAlert"
                            class="pay-in-advance-alert"
                            cmsWidgetName="PayInAdvanceErrorAlertWidget"
                            alertClass="alert-danger"
                            :isDismissible="false"
                            :isCollapsible="true"
                            :preventDefaultOnClick="true"
                            @textLinkClicked="paymentFailedPayLater" />
                        <div id="adyen-container"></div>
                    </div>
                    <div class="col-lg-4 col-md-10 col-xs-12">
                        <div class="order-summary-panel">
                            <div class="cart-title">{{ cartTitle }}</div>
                            <cartDropdown
                                ref="cart"
                                class="cart-dropdown-component"
                                :readOnly="true"
                                :showAsPaid="false"
                                recyclingModalCmsWidgetName="RecycleModal"
                                servicePackageTitleWidgetName="ServicePackageTitle" />
                        </div>
                    </div>
                </div>
                <div class="col-xs-12">
                    <siteFooter
                        class="footer-component"
                        ref="siteFooter"
                        cmsWidgetName="SiteFooterWidget"
                        :isForwardActionDisabled="true"
                        :isForwardButtonHidden="true"
                        @ForwardClicked="forwardButtonAction"
                        @backClicked="backButtonAction" />
                </div>
            </div>
        </div>
    </Form>
</template>
<script>
// Components
import siteHeader from '@/iss-components/site-header/site-header.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import alert from '@/ux-components/alert/alert.vue';
import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';

// Supporting files
import issPageValues from '@/router/router-constants/issPage-values.js';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper';
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { getCartTotal } from '@/helpers/cart-helper';
import { useMainStore } from '@/store';
import widgetFields from '@/constants/cms-widget-fields.js';
import { paymentMethods } from '@/constants/payment-method-constants.js';
import { AppointmentTypeStrings } from '@/constants/schedule-constants.js';
import queryStrings from '@/constants/query-strings';
import { formatAmountInDollars } from '@/helpers/text-helper';
import showIssLoadingModal from '@/helpers/loading-modal-helper';
import { submitWorkOrder } from '@/helpers/order-helper';
import submitType from '@/constants/submit-type';

import "@adyen/adyen-web/styles/adyen.css";
import { mapAdyenToIssPaymentMethod, mapIssToAdyenPaymentMethod } from '@/helpers/adyen-helper';
import { createAdyenCheckout } from "@/helpers/adyen-helper";
import { Dropin } from "@adyen/adyen-web/auto";
import applicationConfig from '@/constants/application-config';

export default {
    name: 'payment-page-adyen',
    components: {
        cartDropdown,
        siteHeader,
        siteFooter,
        Form,
        alert
    },
    mixins: [BaseFormMixin],
    async beforeRouteEnter(to, from, next) {
        const alertToDisplay = to.query[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT];

        // Call APIs
        const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);

        // Settle promises and get results
        const promiseResultMap = [
            {
                resultKey: 'cmsContent',
                promise: cmsContentPromise
            }
        ];

        // use resultMap to populate layout content.
        const resultMap = await settleAllPromises(promiseResultMap);

        next((vm) => {
            vm.setCmsContent(resultMap.cmsContent);
            vm.$nextTick(() => {
                if (vm.$refs.cart) {
                    const cartItems = vm.$refs.cart.cartItems;
                    vm.getPayInAdvanceLineItems(cartItems);
                }
                if (alertToDisplay) {
                    vm.hasPaymentFailureError = true;
                }
            });
        });
    },
    mounted() {
        showIssLoadingModal(true);
        this.initializeAdyen().finally(() => {
            showIssLoadingModal(false);
        });
    },
    data() {
        return {
            referralSequenceNumber: useMainStore().order.referralSequenceNumber,
            emailAddress: useMainStore().contactInfo.emailAddress,
            address1: this.getAddress1(),
            address2: this.getAddress2(),
            city: this.getCity(),
            state: this.getState(),
            zipCode: this.getZipCode(),
            firstName: useMainStore().contactInfo.firstName,
            lastName: useMainStore().contactInfo.lastName,
            phoneNumber: useMainStore().contactInfo.servicePhone,
            ctu: useMainStore().order.serviceLocation.provider?.address?.zipCodeCtu ?? useMainStore().order.serviceLocation.zipCodeCtu,
            referralCorrelationId: useMainStore().order.referralCorrelationId,
            workOrderNumber: this.getWorkOrderNumber(),
            invoiceNumber: this.getInvoiceNumber(),
            totalAmount: this.getAmountDue(),
            displayAmount: this.getDisplayAmountDue(),
            payInAdvanceLineItems: '',
            sessionId: '',
            dropinComponent: null,
            sourceSystem: 'ISS-NextGen',
            hasPaymentFailureError: false,
            adyenTimeout: null
        };
    },
    unmounted() {
        if (this.adyenTimeout) {
            clearTimeout(this.adyenTimeout);
        }
    },
    computed: {
        piaType() {
            return this.mainStore.payment.paymentMethod;
        },
        parentDomainName() {
            return window.location.hostname;
        },
        payInAdvanceResponseUrl() {
            const { protocol, host } = window.location;
            return `${protocol}//${host}/?issPage=${issPageValues.PAYMENT_RETURN_ADYEN}&src=iss-nextgen`;
        },
        payInAdvanceCancelUrl() {
            const { protocol, host } = window.location;
            return `${protocol}//${host}/?issPage=${issPageValues.PAYMENT_METHOD}&src=iss-nextgen`;
        },
        cartTitle() {
            return this.getCmsContent('OrderSummaryTextWidget', widgetFields.TEXT_BLOCK_WIDGET.TEXT);
        },
        switchPaymentText() {
            return this.getCmsContent('SwitchPaymentTextWidget', widgetFields.TEXT_BLOCK_WIDGET.TEXT);
        },
        submitPaymentText() {
            return this.getCmsContent('SiteFooterWidget', widgetFields.FOOTER_WIDGET.FORWARD_BUTTON_TEXT);
        },
        adyenPriceTotal() {
            return Math.round(this.totalAmount * 100);
        },
        workOrderNumberLastSixDigits() {
            const workOrderNumber = this.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;
        },
        splitStreetAddress() {
            const address = this.address1;
            const tokens = address.split(" ") ?? [""];
            const number = tokens[0];

            const street = tokens.slice(1).join(" ") ?? "";

            return {
                number: number ?? "",
                street: street ?? "",
            };
        }
    },
    methods: {
        arePagePrerequisitesValid() {
            // Line Items
            const packageReqs =
                !!useMainStore().order.lineItems.supportingItems;

            // Service Location
            const { serviceLocation } = useMainStore().order;
            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
                || serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP;

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

            // Insurance
            const isInsuranceSet =
                useMainStore().order.payment.isInsurance !== null;

            // Schedule
            const { schedule } = useMainStore().order;
            const scheduleReqs = !!(
                schedule.date
                && schedule.startTime
                && schedule.endTime
                && schedule.jobMaxMinutes
                && schedule.jobMinMinutes
            );

            // Contact Info
            const { contactInfo } = useMainStore();
            const contactInfoReqs = !!(
                contactInfo.firstName
                && contactInfo.lastName
                && contactInfo.servicePhone
                && contactInfo.emailAddress
            );

            const paymentMethodReqs = useMainStore().isPayInAdvance;

            return (
                packageReqs
                && serviceLocationReqs
                && isInsuranceSet
                && scheduleReqs
                && contactInfoReqs
                && paymentMethodReqs
            );
        },
        getWorkOrderNumber() {
            const { workOrderNumber } = useMainStore().order;
            if (workOrderNumber) {
                const items = workOrderNumber.split('-');
                if (items.length > 1) {
                    return items[1];
                }
            }
            return undefined;
        },
        getInvoiceNumber() {
            const { workOrderNumber } = useMainStore().order;
            if (workOrderNumber) {
                return workOrderNumber.replace('-', '');
            }
            return undefined;
        },
        getAddress1() {
            return (
                useMainStore().order.serviceLocation.address
                ?? useMainStore().order.serviceLocation.provider.address
                    .streetAddress
            );
        },
        getAddress2() {
            return useMainStore().order.serviceLocation.address2;
        },
        getCity() {
            return (
                useMainStore().order.serviceLocation.city
                ?? useMainStore().order.serviceLocation.provider.address.city
            );
        },
        getState() {
            return (
                useMainStore().order.serviceLocation.state
                ?? useMainStore().order.serviceLocation.provider.address.state
            );
        },
        getZipCode() {
            return (
                useMainStore().order.customer.address.zipCode
                ?? useMainStore().order.serviceLocation.zipCode
                ?? useMainStore().order.serviceLocation.provider.address.zipCode
            );
        },
        getPayInAdvanceLineItems(cartItems) {
            const { glassParts } = useMainStore().order.lineItems;
            const lineItems =
                glassParts === null
                    ? [
                        this.getPayInAdvanceFormattedLineItem('Labor', 0, 1),
                        this.getPayInAdvanceFormattedLineItem('Repair supplies', 0, 1)
                    ]
                    : [
                        this.getPayInAdvanceFormattedLineItem('Parts and labor', 0, 1)
                    ];

            cartItems.forEach((item) => {
                if (item.name !== null && item.category !== 'promos') {
                    lineItems.push(this.getPayInAdvanceFormattedLineItem(
                        item.name,
                        (item.salesTax + item.subTotal).toFixed(2),
                        1
                    ));
                }
            });

            this.payInAdvanceLineItems = lineItems.join('||');
        },
        getPayInAdvanceFormattedLineItem(itemName, price, quantity) {
            return `${itemName}|${price}|${quantity}`;
        },
        getAmountDue() {
            return getCartTotal(useMainStore().order);
        },
        getDisplayAmountDue() {
            return formatAmountInDollars(this.getAmountDue());
        },
        async paymentFailedPayLater() {
            this.showIssLoadingModal(true);
            this.mainStore.savePaymentMethodChoice(paymentMethods.PAY_AT_TIME_OF_SERVICE);
            await submitWorkOrder({ submitType: submitType.SAFELITE });
            this.$router.navigate(
                this.navigationScenarios.CLICKED_FORWARD,
                this.$route
            );
        },
        backButtonAction() {
            // page will not render with normal navigation due to navigating from the iframe
            window.location = this.payInAdvanceCancelUrl;
        },
        forwardButtonAction() {
            this.$router.navigate(
                this.navigationScenarios.CLICKED_FORWARD,
                this.$route
            );
        },
        async initializeAdyen() {
            const requestBody = this.getAdyenInitRequestInfo();
            
            const adyenResponse = await this.mainStore.initializeAdyenPayment(requestBody);

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

            this.sessionId = adyenResponse.sessionId;

            const checkout = await createAdyenCheckout({
                session: session,
                handlers: {
                    onPaymentCompleted: (result, component) => {
                        this.handleCompletedPayment(result);
                    },
                    onPaymentFailed: (result, component) => {
                        this.handleFailedPayment(result);
                    },
                    onError: (error, component) => {
                        this.handleError(error);
                    },
                },
                options: {
                    amount: this.adyenPriceTotal,
                },
            });

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

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

            if (this.adyenTimeout) {
                clearTimeout(this.adyenTimeout);
            }
            this.adyenTimeout = 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",
                        data: {
                            holderName: `${this.mainStore.contactInfo.firstName} ${this.mainStore.contactInfo.lastName}`,
                            billingAddress: {
                                postalCode: this.zipCode,
                                country: "US"
                            }
                        }
                    },
                },
            };

            const adyenPaymentType = mapIssToAdyenPaymentMethod(this.piaType);

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

            const dropin = new Dropin(checkout, configuration);

            this.dropinComponent = dropin;

            dropin.mount("#adyen-container");
        },
        getAdyenInitRequestInfo() {
            const request = {
                sourceSystem: this.sourceSystem,
                referralSequenceNumber: this.referralSequenceNumber,
                workOrderNumber: this.workOrderNumberLastSixDigits,
                zipCodeCtu: this.ctu,
                amount: this.adyenPriceTotal,
                city: this.city,
                houseNumberOrName: this.splitStreetAddress.number,
                street: this.splitStreetAddress.street,
                zipCode: this.zipCode,
                stateOrProvince: this.state,
                returnUrl: this.payInAdvanceResponseUrl,
                email: this.mainStore.contactInfo.emailAddress,
                firstName: this.mainStore.contactInfo.firstName,
                lastName: this.mainStore.contactInfo.lastName,
                idempotencyKey: this.getIdempotencyKey(),
            };
            return request;
        },
        async resetAdyenDropin() {
            this?.dropinComponent?.unmount();
            await this.initializeAdyen();
        },
        getIdempotencyKey() {
            const currentDateTime = new Date();
            const currentHour = currentDateTime.getUTCHours();
            const currentDate = currentDateTime.getUTCDate();
            const id = this?.mainStore?.order?.referralCorrelationId;
            const system = this.sourceSystem;
            const total = this.adyenPriceTotal;

            return `${id}-${system}-${currentDate}-${currentHour}-${total}`;
        },
        async handleCompletedPayment(result) {
            // Fetch session info
            const paymentSessionRequest = {
                zipCodeCtu: this.ctu,
                workOrderNumber: this.workOrderNumberLastSixDigits,
                sourceSystem: this.sourceSystem,
                sessionId: this.sessionId,
                sessionResult: result?.sessionResult,
            };

            showIssLoadingModal(true);

            const adyenResponse = await this.mainStore.getAdyenSessionResult(paymentSessionRequest);

            const ccToken = {
                subscriptionId: adyenResponse?.storedToken,
                expMonth: adyenResponse?.cardExpiryMonth,
                expYear: adyenResponse?.cardExpiryYear,
                cardType: adyenResponse?.cardType,
                billToPostalCode: this.zipCode,
                billToFirstName: this.mainStore.contactInfo.firstName,
                billToLastName: this.mainStore.contactInfo.lastName,
                referenceNumber: adyenResponse?.workOrderNumber,
                authCode: adyenResponse?.authCode,
                transactionId: adyenResponse?.transactionReference,
                transReferenceNumber: adyenResponse?.transactionReference,
                lastFour: adyenResponse?.last4DigitsOfCard,
            };

            const paymentMethodFromSession = adyenResponse?.paymentMethod;

            const paymentMethod = mapAdyenToIssPaymentMethod(paymentMethodFromSession);
            
            this.mainStore.savePaymentMethodChoice(paymentMethod);

            if (paymentMethod === paymentMethods.PAYPAL) {
                this.mainStore.updatePaypalToken(adyenResponse?.storedToken);
            } else {
                this.mainStore.updateCreditCardToken(ccToken);
            }
            
            this.mainStore.updateNextGenSettledAmount(this.totalAmount);

            await this.saveAndSubmitWorkOrder();
        },
        async handleFailedPayment(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) {
            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() {
            // Final work order submit after returning from pay in advance.
            await submitWorkOrder({ submitType: submitType.SAFELITE });

            this.$router.navigate(
                this.navigationScenarios.PAY_IN_ADVANCE_SUCCESS,
                this.$route
            );
        },
        showIssLoadingModal
    }
};
</script>
<style lang="scss" scoped>
.iss-heritage-container-width.payment-page-container {
    padding: 0 .9375rem;

    #card-container {
        padding-top: 1.375rem;
        margin: 0;
        > * {
            padding: 0 .9375rem;
        }
    }

    .order-summary-panel {
        margin-bottom: 1.375rem;
    }

    .cart-title {
        font-size: 1rem;
        font-weight: 500;
        margin-top: 1.25rem;
        margin-bottom: .625rem;
    }
    
    .cart-dropdown-component.cart-table {
        font-size: .8125rem;
    }

    .footer-component:deep(.footer) {
        margin: 1.25rem 0 0 0;
    }

    .pay-in-advance-alert {
        :deep(a) {
            font-weight: $font-weight-bold;
        }
    }
}

.fade-on-route-transition {
    height: unset;
}

.payment p {
    margin-bottom: 0.75rem;
}
</style>
