"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.confirmTopUpOrder = exports.refundStripe = exports.stripeWebhook = exports.purchaseTimeslot = exports.createOrderTopUpStripe = void 0;
const functions = require("firebase-functions");
const stripe_1 = require("stripe");
const collections_1 = require("./collections");
const constants_1 = require("./constants");
const firebase_admin_1 = require("firebase-admin");
const payment_functions_1 = require("./payment-functions");
const notification_function_1 = require("./notification-function");
const collections_2 = require("./collections");
const timeslot_functions_1 = require("./timeslot-functions");
const topup_order_model_1 = require("./model/topup_order_model");
const custom_error_1 = require("./utils/custom_error");
const user_wallet_functions_1 = require("./user-wallet-functions");
var PaymentType;
(function (PaymentType) {
    PaymentType["TopUp"] = "topup";
    PaymentType["TimeSlot"] = "timeslot";
})(PaymentType || (PaymentType = {}));
const stripe = new stripe_1.default(process.env.STRIPE_SECRET_KEY, {
    apiVersion: "2020-08-27",
});
/**
 *
 * function when client topup their wallet, by default we use Stripe payment gateway
 * you can change this to your payment gateway by following the stripe structure,
 * adjust it with your own payment gateway
 */
exports.createOrderTopUpStripe = functions.https.onCall(async (request, response) => {
    try {
        const { amount, uid } = request;
        if (amount <= 0) {
            throw new functions.https.HttpsError("invalid-argument", "the amount must be greater than 0");
        }
        /// Create order data in database
        const order = await collections_1.topUpOrderCol.add({
            amount: amount,
            createdAt: new Date(),
            userId: uid,
            currency: constants_1.CURRENCY,
            status: topup_order_model_1.TopUpOrderStatus.created,
            paymentMethod: payment_functions_1.PaymentMethod.Stripe,
        });
        const paymentIntent = await stripe.paymentIntents.create({
            amount: amount * 100,
            currency: constants_1.CURRENCY,
            payment_method_types: ["card"],
            metadata: {
                type: PaymentType.TopUp,
                uid: uid,
                orderId: order.id,
            },
        });
        if (paymentIntent) {
            await collections_1.topUpOrderCol.doc(order.id).update({
                stripePaymentId: paymentIntent.id,
            });
            return { clientSecret: paymentIntent.client_secret };
        }
        else {
            await collections_1.topUpOrderCol.doc(order.id).update({
                status: topup_order_model_1.TopUpOrderStatus.failed,
            });
            throw new functions.https.HttpsError("internal", "failed to create Stripe payment intent");
        }
    }
    catch (error) {
        throw error;
    }
});
/**
 *
 * function when client purchase timeslot, by default we use Stripe payment gateway
 * you can change this to your payment gateway by following the stripe structure of purchaing the timeslot
 */
exports.purchaseTimeslot = functions.https.onCall(async (request, response) => {
    try {
        const choosedTimeSlot = (await collections_1.timeSlotCol.doc(request.timeSlotId).get()).data();
        if (!choosedTimeSlot) {
            throw "choosed timeslot is not found";
        }
        const paymentIntent = await stripe.paymentIntents.create({
            amount: choosedTimeSlot.price * 100,
            currency: constants_1.CURRENCY,
            payment_method_types: ["card"],
            metadata: {
                type: PaymentType.TimeSlot,
                uid: request.userId,
            },
        });
        await collections_1.orderCol.add({
            createdAt: firebase_admin_1.firestore.Timestamp.fromDate(new Date()),
            timeSlotId: request.timeSlotId,
            userId: request.userId,
            charged: false,
            stripePaymentId: paymentIntent.id,
            status: collections_1.OrderStatus.notPay,
            paymentMethod: payment_functions_1.PaymentMethod.Stripe,
        });
        return paymentIntent.client_secret;
    }
    catch (error) {
        throw error;
    }
});
/**
 * this function is for stripe webhook, when client successfully purchase the timeslot, stripe will call this webhook
 * make sure you have setup the stripe webhook by following this tutorial {@link https://halodoctor.netlify.app/docs/payment-gateway/stripe-webook}
 */
exports.stripeWebhook = functions.https.onRequest(async (request, response) => {
    var _a, _b;
    let event;
    try {
        if (process.env.STRIPE_WEBHOOK_SECRET == undefined) {
            throw new Error("Stripe webhook secret is undefined, please check .env file");
        }
        const stripeWebhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
        event = stripe.webhooks.constructEvent(request.rawBody, request.headers["stripe-signature"], stripeWebhookSecret);
    }
    catch (error) {
        console.error("Webhook signature verification failed");
        response.sendStatus(400);
        return;
    }
    switch (event.type) {
        case "payment_intent.succeeded":
            try {
                const amount = request.body.data.object.amount_received / 100;
                const currency = request.body.data.object.currency;
                const userId = request.body.data.object.metadata.uid;
                const type = request.body.data.object.metadata.type;
                if (type == PaymentType.TopUp) {
                    const confirmTopUpParam = {
                        stripePaymentId: request.body.data.object.id,
                        currency: currency,
                        orderId: request.body.data.object.metadata.orderId,
                    };
                    await (0, exports.confirmTopUpOrder)(confirmTopUpParam);
                    const walletHistory = {
                        type: "TopUp",
                        initialAmount: 0,
                        finalAmount: 0,
                        transactionAmount: amount,
                        createdAt: new Date(),
                        userId: userId,
                        status: "Success",
                        description: "Top Up Success",
                    };
                    await (0, user_wallet_functions_1.addHistoryWalletUser)(userId, walletHistory);
                    response.send();
                    return;
                }
                let order = await collections_1.orderCol
                    .where("stripePaymentId", "==", request.body.data.object.id)
                    .get();
                let orderRef = order.docs[0];
                orderRef.ref.update({
                    charged: true,
                    amount: amount,
                    status: payment_functions_1.PaymentStatus.PaymentSuccess,
                    currency: currency,
                });
                //Get user info who book this timeslot
                let bookByWho = (await collections_1.usersCol.doc(orderRef.data().userId).get()).data();
                const timeSlot = await collections_1.timeSlotCol
                    .doc(orderRef.data().timeSlotId)
                    .get();
                const timeSlotData = timeSlot.data();
                if (!timeSlotData) {
                    throw "selected timeslot is null or undefined";
                }
                const doctor = await collections_1.doctorCol.doc(timeSlotData.doctorId).get();
                timeSlot.ref.update({
                    charged: true,
                    available: false,
                    bookByWho: {
                        userId: orderRef.data().userId,
                        displayName: bookByWho === null || bookByWho === void 0 ? void 0 : bookByWho.displayName,
                        photoUrl: (bookByWho === null || bookByWho === void 0 ? void 0 : bookByWho.photoUrl) ? bookByWho === null || bookByWho === void 0 ? void 0 : bookByWho.photoUrl : "",
                    },
                    status: "booked",
                    doctor: {
                        doctorName: (_a = doctor.data()) === null || _a === void 0 ? void 0 : _a.doctorName,
                        doctorPicture: (_b = doctor.data()) === null || _b === void 0 ? void 0 : _b.doctorPicture,
                    },
                    purchaseTime: firebase_admin_1.firestore.Timestamp.fromDate(new Date()),
                });
                await (0, notification_function_1.orderedTimeslotNotification)(doctor.id);
                await (0, notification_function_1.paymentSuccessNotification)(orderRef.data().userId, orderRef.id);
                console.log("payment success");
                break;
            }
            catch (error) {
                console.log("error " + error);
            }
        case "payment_intent.canceled":
            console.log("failed payment ");
            break;
        // ... handle other event types
        default:
            console.log(`Unhandled event type ${event.type}`);
    }
    // Return a 200 response to acknowledge receipt of the event
    response.send();
});
/**
by default we use stripe refund system if you use other payment gateway you need to change this function to
you can just follow the structure of the function
*/
exports.refundStripe = functions.https.onCall(async (request, response) => {
    try {
        const { timeSlotId } = request;
        const orderRef = await collections_1.orderCol
            .where("timeSlotId", "==", request.timeSlotId)
            .get();
        const order = orderRef.docs[0];
        const refund = await stripe.refunds.create({
            payment_intent: order.data().stripePaymentId,
        });
        if (refund.status === "succeeded") {
            let refundData = await collections_2.refundCol.add({
                createdAt: firebase_admin_1.firestore.Timestamp.fromDate(new Date()),
                timeSlotId: request.timeSlotId,
                paymentId: order.data().stripePaymentId,
                status: refund.status,
                amount: refund.amount,
                refundId: refund.id,
                currency: refund.currency,
            });
            await (0, timeslot_functions_1.refundTimeslot)(timeSlotId, refundData.id);
            console.log("refund success : " + JSON.stringify(refund));
        }
        else {
            throw "refund failed";
        }
    }
    catch (err) {
        throw err;
    }
});
const confirmTopUpOrder = async (param) => {
    var _a, _b, _c, _d, _e, _f;
    try {
        let orderRef = collections_1.topUpOrderCol.doc(param.orderId);
        let order = await orderRef.get();
        ///Update the Top Up Order
        if (order.exists) {
            await orderRef.update({
                status: topup_order_model_1.TopUpOrderStatus.paid,
                updatedAt: new Date(),
            });
            ///Add balance to user account
            if ((_a = order.data()) === null || _a === void 0 ? void 0 : _a.userId) {
                await (0, payment_functions_1.topUpUserWallet)((_b = order.data()) === null || _b === void 0 ? void 0 : _b.userId, (_c = order.data()) === null || _c === void 0 ? void 0 : _c.amount);
            }
        }
        console.log("Topu Successful, Order id : " + param.orderId);
    }
    catch (error) {
        if (error instanceof custom_error_1.CustomError && error.code === "not-found") {
            let orderRef = collections_1.topUpOrderCol.doc(param.orderId);
            let order = await orderRef.get();
            ///Update the Top Up Order
            if (order.exists) {
                ///Add balance to user account
                if ((_d = order.data()) === null || _d === void 0 ? void 0 : _d.userId) {
                    await (0, user_wallet_functions_1.createWalletDataForUser)(order.data().userId);
                    await (0, payment_functions_1.topUpUserWallet)((_e = order.data()) === null || _e === void 0 ? void 0 : _e.userId, (_f = order.data()) === null || _f === void 0 ? void 0 : _f.amount);
                }
            }
        }
        throw error;
    }
};
exports.confirmTopUpOrder = confirmTopUpOrder;
//# sourceMappingURL=stripe-functions.js.map