"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BillingService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const subscription_entity_1 = require("../entities/subscription.entity");
const invoice_entity_1 = require("../entities/invoice.entity");
const payment_method_entity_1 = require("../entities/payment-method.entity");
let BillingService = class BillingService {
    constructor(subscriptionRepo, invoiceRepo, paymentMethodRepo) {
        this.subscriptionRepo = subscriptionRepo;
        this.invoiceRepo = invoiceRepo;
        this.paymentMethodRepo = paymentMethodRepo;
    }
    async createSubscription(dto) {
        const now = new Date();
        const periodEnd = new Date(now);
        periodEnd.setMonth(periodEnd.getMonth() + 1);
        const subscription = this.subscriptionRepo.create({
            tenant_id: dto.tenant_id,
            plan_id: dto.plan_id,
            status: dto.trial_end ? subscription_entity_1.SubscriptionStatus.TRIAL : subscription_entity_1.SubscriptionStatus.ACTIVE,
            current_period_start: now,
            current_period_end: periodEnd,
            trial_end: dto.trial_end ? new Date(dto.trial_end) : null,
            payment_provider: dto.payment_provider,
        });
        return this.subscriptionRepo.save(subscription);
    }
    async getSubscription(tenantId) {
        return this.subscriptionRepo.findOne({
            where: { tenant_id: tenantId },
            order: { created_at: 'DESC' },
        });
    }
    async updateSubscription(tenantId, dto) {
        const subscription = await this.getSubscription(tenantId);
        if (!subscription) {
            throw new common_1.NotFoundException('Subscription not found');
        }
        Object.assign(subscription, dto);
        return this.subscriptionRepo.save(subscription);
    }
    async cancelSubscription(tenantId, dto) {
        const subscription = await this.getSubscription(tenantId);
        if (!subscription) {
            throw new common_1.NotFoundException('Subscription not found');
        }
        subscription.cancelled_at = new Date();
        if (dto.immediately) {
            subscription.status = subscription_entity_1.SubscriptionStatus.CANCELLED;
        }
        if (dto.reason) {
            subscription.metadata = {
                ...subscription.metadata,
                cancellation_reason: dto.reason,
            };
        }
        return this.subscriptionRepo.save(subscription);
    }
    async changePlan(tenantId, newPlanId) {
        const subscription = await this.getSubscription(tenantId);
        if (!subscription) {
            throw new common_1.NotFoundException('Subscription not found');
        }
        subscription.plan_id = newPlanId;
        subscription.metadata = {
            ...subscription.metadata,
            plan_changed_at: new Date().toISOString(),
        };
        return this.subscriptionRepo.save(subscription);
    }
    async renewSubscription(tenantId) {
        const subscription = await this.getSubscription(tenantId);
        if (!subscription) {
            throw new common_1.NotFoundException('Subscription not found');
        }
        const now = new Date();
        const newPeriodEnd = new Date(subscription.current_period_end);
        newPeriodEnd.setMonth(newPeriodEnd.getMonth() + 1);
        subscription.current_period_start = subscription.current_period_end;
        subscription.current_period_end = newPeriodEnd;
        subscription.status = subscription_entity_1.SubscriptionStatus.ACTIVE;
        return this.subscriptionRepo.save(subscription);
    }
    async createInvoice(tenantId, subscriptionId, lineItems) {
        const invoiceNumber = await this.generateInvoiceNumber();
        const items = lineItems.map((item) => ({
            ...item,
            amount: item.quantity * item.unit_price,
        }));
        const subtotal = items.reduce((sum, item) => sum + item.amount, 0);
        const tax = subtotal * 0.16;
        const total = subtotal + tax;
        const dueDate = new Date();
        dueDate.setDate(dueDate.getDate() + 15);
        const invoice = this.invoiceRepo.create({
            tenant_id: tenantId,
            subscription_id: subscriptionId,
            invoice_number: invoiceNumber,
            status: invoice_entity_1.InvoiceStatus.OPEN,
            subtotal,
            tax,
            total,
            due_date: dueDate,
            line_items: items,
        });
        return this.invoiceRepo.save(invoice);
    }
    async getInvoices(tenantId, options) {
        const page = options?.page || 1;
        const limit = options?.limit || 10;
        const [data, total] = await this.invoiceRepo.findAndCount({
            where: { tenant_id: tenantId },
            order: { created_at: 'DESC' },
            skip: (page - 1) * limit,
            take: limit,
        });
        return { data, total, page, limit };
    }
    async getInvoice(invoiceId, tenantId) {
        const invoice = await this.invoiceRepo.findOne({
            where: { id: invoiceId, tenant_id: tenantId },
        });
        if (!invoice) {
            throw new common_1.NotFoundException('Invoice not found');
        }
        return invoice;
    }
    async markInvoicePaid(invoiceId, tenantId) {
        const invoice = await this.getInvoice(invoiceId, tenantId);
        invoice.status = invoice_entity_1.InvoiceStatus.PAID;
        invoice.paid_at = new Date();
        return this.invoiceRepo.save(invoice);
    }
    async voidInvoice(invoiceId, tenantId) {
        const invoice = await this.getInvoice(invoiceId, tenantId);
        if (invoice.status === invoice_entity_1.InvoiceStatus.PAID) {
            throw new common_1.BadRequestException('Cannot void a paid invoice');
        }
        invoice.status = invoice_entity_1.InvoiceStatus.VOID;
        return this.invoiceRepo.save(invoice);
    }
    async generateInvoiceNumber() {
        const year = new Date().getFullYear();
        const month = String(new Date().getMonth() + 1).padStart(2, '0');
        const count = await this.invoiceRepo.count();
        const sequence = String(count + 1).padStart(6, '0');
        return `INV-${year}${month}-${sequence}`;
    }
    async addPaymentMethod(tenantId, dto) {
        if (dto.is_default) {
            await this.paymentMethodRepo.update({ tenant_id: tenantId, is_default: true }, { is_default: false });
        }
        const paymentMethod = this.paymentMethodRepo.create({
            tenant_id: tenantId,
            ...dto,
        });
        return this.paymentMethodRepo.save(paymentMethod);
    }
    async getPaymentMethods(tenantId) {
        return this.paymentMethodRepo.find({
            where: { tenant_id: tenantId, is_active: true },
            order: { is_default: 'DESC', created_at: 'DESC' },
        });
    }
    async getDefaultPaymentMethod(tenantId) {
        return this.paymentMethodRepo.findOne({
            where: { tenant_id: tenantId, is_default: true, is_active: true },
        });
    }
    async setDefaultPaymentMethod(paymentMethodId, tenantId) {
        const paymentMethod = await this.paymentMethodRepo.findOne({
            where: { id: paymentMethodId, tenant_id: tenantId },
        });
        if (!paymentMethod) {
            throw new common_1.NotFoundException('Payment method not found');
        }
        await this.paymentMethodRepo.update({ tenant_id: tenantId, is_default: true }, { is_default: false });
        paymentMethod.is_default = true;
        return this.paymentMethodRepo.save(paymentMethod);
    }
    async removePaymentMethod(paymentMethodId, tenantId) {
        const paymentMethod = await this.paymentMethodRepo.findOne({
            where: { id: paymentMethodId, tenant_id: tenantId },
        });
        if (!paymentMethod) {
            throw new common_1.NotFoundException('Payment method not found');
        }
        if (paymentMethod.is_default) {
            throw new common_1.BadRequestException('Cannot remove default payment method');
        }
        paymentMethod.is_active = false;
        await this.paymentMethodRepo.save(paymentMethod);
    }
    async getBillingSummary(tenantId) {
        const subscription = await this.getSubscription(tenantId);
        const defaultPaymentMethod = await this.getDefaultPaymentMethod(tenantId);
        const pendingInvoices = await this.invoiceRepo.find({
            where: { tenant_id: tenantId, status: invoice_entity_1.InvoiceStatus.OPEN },
        });
        const totalDue = pendingInvoices.reduce((sum, inv) => sum + Number(inv.total), 0);
        return {
            subscription,
            defaultPaymentMethod,
            pendingInvoices: pendingInvoices.length,
            totalDue,
        };
    }
    async checkSubscriptionStatus(tenantId) {
        const subscription = await this.getSubscription(tenantId);
        if (!subscription) {
            return { isActive: false, daysRemaining: 0, status: subscription_entity_1.SubscriptionStatus.EXPIRED };
        }
        const now = new Date();
        const periodEnd = new Date(subscription.current_period_end);
        const daysRemaining = Math.ceil((periodEnd.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
        const isActive = [
            subscription_entity_1.SubscriptionStatus.ACTIVE,
            subscription_entity_1.SubscriptionStatus.TRIAL,
        ].includes(subscription.status);
        return {
            isActive,
            daysRemaining: Math.max(0, daysRemaining),
            status: subscription.status,
        };
    }
};
exports.BillingService = BillingService;
exports.BillingService = BillingService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(subscription_entity_1.Subscription)),
    __param(1, (0, typeorm_1.InjectRepository)(invoice_entity_1.Invoice)),
    __param(2, (0, typeorm_1.InjectRepository)(payment_method_entity_1.PaymentMethod)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository])
], BillingService);
//# sourceMappingURL=billing.service.js.map