import { logger } from "../logger/index.mjs";
import { GenericError } from "../utils/errors/ErrorsClass.mjs";
import { getOrganizationById, updatePlan } from "./organization.service.mjs";
import { getUserById } from "./user.service.mjs";
import { sendEmail } from "./email.service.mjs";
import { retrievePlanInformation } from "../utils/plan.mjs";
import Stripe$1 from "stripe";

//#region src/services/subscription.service.ts
const addOrUpdateSubscription = async (subscriptionId, priceId, customerId, userId, organization, status) => {
	const stripe = new Stripe$1(process.env.STRIPE_SECRET_KEY);
	const user = await getUserById(userId);
	if (!user) throw new GenericError("USER_NOT_FOUND", { userId });
	if (String(user.customerId) !== customerId) {
		user.customerId = customerId;
		await user.save();
	}
	const planInfo = retrievePlanInformation(priceId);
	const subscriptions = await stripe.subscriptions.list({
		customer: customerId,
		status: "active",
		limit: 1
	});
	if (subscriptions.data.length >= 1) {
		const otherSubscriptionArray = subscriptions.data.filter((subscription) => subscription.id !== subscriptionId);
		for (const subscription of otherSubscriptionArray) await stripe.subscriptions.cancel(subscription.id);
	}
	const updatedOrganization = await updatePlan(organization, {
		creatorId: user.id,
		priceId,
		customerId,
		subscriptionId,
		type: planInfo.type,
		period: planInfo.period,
		status
	});
	if (!updatedOrganization) throw new GenericError("ORGANIZATION_UPDATE_FAILED", { organizationId: organization.id });
	logger.info(`Plan updated for organization ${organization.id} - ${planInfo.type} - ${planInfo.period}`);
	return updatedOrganization.plan ?? null;
};
const cancelSubscription = async (subscriptionId, organizationId) => {
	const organization = await getOrganizationById(organizationId);
	if (!organization) throw new GenericError("ORGANIZATION_NOT_FOUND", { subscriptionId });
	if (!subscriptionId) throw new GenericError("NO_SUBSCRIPTION_ID_PROVIDED");
	if (!organization.plan) throw new GenericError("ORGANIZATION_PLAN_NOT_FOUND", {
		subscriptionId,
		organizationId: organization.id
	});
	const updatedOrganization = await updatePlan(organization, { status: "canceled" });
	if (!updatedOrganization) throw new GenericError("ORGANIZATION_UPDATE_FAILED", { organizationId: organization.id });
	logger.info(`Cancelled plan for organization ${updatedOrganization.id} - ${updatedOrganization.plan?.type} - ${updatedOrganization.plan?.period}`);
	return updatedOrganization.plan ?? null;
};
const changeSubscriptionStatus = async (subscriptionId, status, userId, organizationId) => {
	const organization = await getOrganizationById(organizationId);
	if (!organization) throw new GenericError("ORGANIZATION_NOT_FOUND", {
		userId,
		subscriptionId
	});
	if (!organization.plan) throw new GenericError("ORGANIZATION_PLAN_NOT_FOUND", {
		userId,
		subscriptionId,
		organizationId: organization.id
	});
	const updatedOrganization = await updatePlan(organization, {
		status,
		subscriptionId
	});
	if (!updatedOrganization) throw new GenericError("ORGANIZATION_UPDATE_FAILED", { organizationId: organization.id });
	const user = await getUserById(userId);
	if (!user) throw new GenericError("USER_NOT_FOUND", {
		userId,
		subscriptionId
	});
	logger.info(`Updated plan status for organization ${organization.id} - Status: ${status}`);
	const emailData = {
		to: user.email,
		username: user.name,
		email: user.email,
		planName: organization.plan.type,
		date: (/* @__PURE__ */ new Date()).toLocaleDateString(),
		link: `${process.env.CLIENT_URL}/dashboard`
	};
	switch (status) {
		case "active":
			await sendEmail({
				...emailData,
				type: "subscriptionPaymentSuccess",
				organizationName: organization.name,
				subscriptionStartDate: emailData.date,
				manageSubscriptionLink: emailData.link
			});
			break;
		case "canceled":
			await sendEmail({
				...emailData,
				type: "subscriptionPaymentCancellation",
				organizationName: organization.name,
				cancellationDate: emailData.date,
				reactivateLink: emailData.link
			});
			break;
		case "incomplete":
			await sendEmail({
				...emailData,
				type: "subscriptionPaymentError",
				organizationName: organization.name,
				errorDate: emailData.date,
				retryPaymentLink: emailData.link
			});
			break;
		default: logger.warn(`Unhandled subscription status: ${status}`);
	}
	return updatedOrganization.plan ?? null;
};
const getCouponId = async (promoCode) => {
	const stripe = new Stripe$1(process.env.STRIPE_SECRET_KEY);
	try {
		const matchingCoupon = (await stripe.coupons.list()).data.find((coupon) => coupon.name === promoCode);
		return matchingCoupon ? matchingCoupon.id : null;
	} catch (error) {
		console.error("Error retrieving coupon:", error);
		return null;
	}
};
const getPricing = async (priceIds, promoCode) => {
	const stripe = new Stripe$1(process.env.STRIPE_SECRET_KEY);
	try {
		const pricePromises = priceIds.map((priceId) => stripe.prices.retrieve(priceId));
		const prices = await Promise.all(pricePromises);
		const totalAmount = prices.reduce((sum, price) => sum + (price.unit_amount ?? 0), 0);
		let discountAmount = 0;
		let discountType = null;
		if (promoCode) {
			const matchingCoupons = (await stripe.coupons.list()).data.find((coupon) => coupon.name === promoCode);
			if (matchingCoupons) {
				if (matchingCoupons.amount_off) {
					discountAmount = matchingCoupons.amount_off;
					discountType = "amount";
				} else if (matchingCoupons.percent_off) {
					discountAmount = matchingCoupons.percent_off;
					discountType = "percentage";
				}
			}
		}
		const results = {};
		for (const price of prices) {
			if (!price.id || !price.unit_amount) continue;
			const originalTotal = price.unit_amount;
			let appliedDiscount = 0;
			let finalTotal = originalTotal;
			if (discountType === "percentage" && discountAmount > 0) {
				appliedDiscount = originalTotal * discountAmount / 100;
				finalTotal = originalTotal - appliedDiscount;
			} else if (discountType === "amount" && totalAmount > 0 && discountAmount > 0) {
				const proportion = originalTotal / totalAmount;
				appliedDiscount = discountAmount * proportion;
				finalTotal = originalTotal - appliedDiscount;
			}
			finalTotal = Math.max(finalTotal, 0);
			results[price.id] = {
				originalTotal,
				discountApplied: appliedDiscount,
				discountType,
				finalTotal,
				currency: price.currency
			};
		}
		return results;
	} catch (error) {
		console.error("Error calculating pricing per priceId:", error);
		throw new Error("Failed to calculate pricing breakdown.");
	}
};

//#endregion
export { addOrUpdateSubscription, cancelSubscription, changeSubscriptionStatus, getCouponId, getPricing };
//# sourceMappingURL=subscription.service.mjs.map