import { Injectable, NotFoundException, BadRequestException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, DataSource } from 'typeorm'; import { CreditBalance } from './entities/credit-balance.entity'; import { CreditTransaction, TransactionType, } from './entities/credit-transaction.entity'; import { CreditPackage } from './entities/credit-package.entity'; @Injectable() export class CreditsService { constructor( @InjectRepository(CreditBalance) private readonly balanceRepository: Repository, @InjectRepository(CreditTransaction) private readonly transactionRepository: Repository, @InjectRepository(CreditPackage) private readonly packageRepository: Repository, private readonly dataSource: DataSource, ) {} async getBalance(userId: string): Promise { let balance = await this.balanceRepository.findOne({ where: { userId } }); if (!balance) { // Create initial balance for new users balance = this.balanceRepository.create({ userId, balance: 0, totalPurchased: 0, totalConsumed: 0, totalFromReferrals: 0, }); await this.balanceRepository.save(balance); } return balance; } async hasMinimumCredits(userId: string, minimum: number): Promise { const balance = await this.getBalance(userId); return balance.balance >= minimum; } async addCredits( userId: string, amount: number, type: TransactionType, description: string, referenceId?: string, referenceType?: string, ): Promise { return this.dataSource.transaction(async (manager) => { const balanceRepo = manager.getRepository(CreditBalance); const transactionRepo = manager.getRepository(CreditTransaction); // Get or create balance let balance = await balanceRepo.findOne({ where: { userId } }); if (!balance) { balance = balanceRepo.create({ userId, balance: 0, totalPurchased: 0, totalConsumed: 0, totalFromReferrals: 0, }); } // Update balance balance.balance += amount; if (type === TransactionType.PURCHASE) { balance.totalPurchased += amount; } else if (type === TransactionType.REFERRAL_BONUS) { balance.totalFromReferrals += amount; } await balanceRepo.save(balance); // Create transaction record const transaction = transactionRepo.create({ userId, type, amount, balanceAfter: balance.balance, description, referenceId, referenceType, }); return transactionRepo.save(transaction); }); } async consume( userId: string, amount: number, description: string, referenceId?: string, referenceType?: string, ): Promise { return this.dataSource.transaction(async (manager) => { const balanceRepo = manager.getRepository(CreditBalance); const transactionRepo = manager.getRepository(CreditTransaction); const balance = await balanceRepo.findOne({ where: { userId } }); if (!balance || balance.balance < amount) { throw new BadRequestException('Creditos insuficientes'); } // Deduct credits balance.balance -= amount; balance.totalConsumed += amount; await balanceRepo.save(balance); // Create transaction record const transaction = transactionRepo.create({ userId, type: TransactionType.CONSUMPTION, amount: -amount, balanceAfter: balance.balance, description, referenceId, referenceType, }); return transactionRepo.save(transaction); }); } async getTransactions( userId: string, page = 1, limit = 20, ): Promise<{ transactions: CreditTransaction[]; total: number }> { const [transactions, total] = await this.transactionRepository.findAndCount( { where: { userId }, order: { createdAt: 'DESC' }, skip: (page - 1) * limit, take: limit, }, ); return { transactions, total }; } async getPackages(): Promise { return this.packageRepository.find({ where: { isActive: true }, order: { sortOrder: 'ASC' }, }); } async getPackageById(packageId: string): Promise { const pkg = await this.packageRepository.findOne({ where: { id: packageId, isActive: true }, }); if (!pkg) { throw new NotFoundException('Paquete no encontrado'); } return pkg; } async grantWelcomeCredits(userId: string): Promise { const welcomeCredits = 50; // Free credits for new users await this.addCredits( userId, welcomeCredits, TransactionType.PROMO, 'Creditos de bienvenida', ); } async grantReferralBonus( referrerId: string, referredId: string, referralId: string, ): Promise { const referrerBonus = 100; const referredBonus = 50; // Give bonus to referrer await this.addCredits( referrerId, referrerBonus, TransactionType.REFERRAL_BONUS, 'Bonus por referido', referralId, 'referral', ); // Give bonus to referred user await this.addCredits( referredId, referredBonus, TransactionType.REFERRAL_BONUS, 'Bonus por usar codigo de referido', referralId, 'referral', ); } }