using MongoDB.Driver; using QRRapidoApp.Data; using QRRapidoApp.Models; using QRRapidoApp.Models.ViewModels; using System.Text.Json; namespace QRRapidoApp.Services { public class UserService : IUserService { private readonly MongoDbContext _context; private readonly IConfiguration _config; private readonly ILogger _logger; public UserService(MongoDbContext context, IConfiguration config, ILogger logger) { _context = context; _config = config; _logger = logger; } public async Task GetUserAsync(string userId) { try { if (_context.Users == null) return null; // Development mode without MongoDB User? userData = null; if (!String.IsNullOrEmpty(userId)) { userData = await _context.Users.Find(u => u.Id == userId).FirstOrDefaultAsync(); } return userData ?? new User(); } catch (Exception ex) { _logger.LogError(ex, $"Error getting user {userId}: {ex.Message}"); return null; } } public async Task GetUserByEmailAsync(string email) { try { if (_context.Users == null) return null; // Development mode without MongoDB return await _context.Users.Find(u => u.Email == email).FirstOrDefaultAsync(); } catch (Exception ex) { _logger.LogError(ex, $"Error getting user by email {email}: {ex.Message}"); return null; } } public async Task GetUserByProviderAsync(string provider, string providerId) { try { return await _context.Users .Find(u => u.Provider == provider && u.ProviderId == providerId) .FirstOrDefaultAsync(); } catch (Exception ex) { _logger.LogError(ex, $"Error getting user by provider {provider}:{providerId}: {ex.Message}"); return null; } } public async Task CreateUserAsync(string email, string name, string provider, string providerId) { var user = new User { Email = email, Name = name, Provider = provider, ProviderId = providerId, CreatedAt = DateTime.UtcNow, LastLoginAt = DateTime.UtcNow, PreferredLanguage = "pt-BR", DailyQRCount = 0, LastQRDate = DateTime.UtcNow.Date, TotalQRGenerated = 0 }; await _context.Users.InsertOneAsync(user); _logger.LogInformation($"Created new user: {email} via {provider}"); return user; } public async Task UpdateLastLoginAsync(string userId) { try { var update = Builders.Update .Set(u => u.LastLoginAt, DateTime.UtcNow); await _context.Users.UpdateOneAsync(u => u.Id == userId, update); } catch (Exception ex) { _logger.LogError(ex, $"Error updating last login for user {userId}: {ex.Message}"); } } public async Task UpdateUserAsync(User user) { try { var result = await _context.Users.ReplaceOneAsync(u => u.Id == user.Id, user); return result.ModifiedCount > 0; } catch (Exception ex) { _logger.LogError(ex, $"Error updating user {user.Id}: {ex.Message}"); return false; } } public async Task GetDailyQRCountAsync(string? userId) { if (string.IsNullOrEmpty(userId)) return 0; // Anonymous users tracked separately try { var user = await GetUserAsync(userId); if (user == null) return 0; // Reset count if it's a new day if (user.LastQRDate.Date < DateTime.UtcNow.Date) { user.DailyQRCount = 0; user.LastQRDate = DateTime.UtcNow.Date; await UpdateUserAsync(user); } return user.DailyQRCount; } catch (Exception ex) { _logger.LogError(ex, $"Error getting daily QR count for user {userId}: {ex.Message}"); return 0; } } public async Task IncrementDailyQRCountAsync(string userId) { try { var user = await GetUserAsync(userId); if (user == null) return 0; // Reset count if it's a new day if (user.LastQRDate.Date < DateTime.UtcNow.Date) { user.DailyQRCount = 1; user.LastQRDate = DateTime.UtcNow.Date; } else { user.DailyQRCount++; } user.TotalQRGenerated++; await UpdateUserAsync(user); // Premium and logged users have unlimited QR codes return int.MaxValue; } catch (Exception ex) { _logger.LogError(ex, $"Error incrementing daily QR count for user {userId}: {ex.Message}"); return 0; } } public async Task GetRemainingQRCountAsync(string userId) { try { var user = await GetUserAsync(userId); if (user == null) return 0; // Premium users have unlimited if (user.IsPremium) return int.MaxValue; // Logged users (non-premium) have unlimited return int.MaxValue; } catch (Exception ex) { _logger.LogError(ex, $"Error getting remaining QR count for user {userId}: {ex.Message}"); return 0; } } public async Task CanGenerateQRAsync(string? userId, bool isPremium) { // Premium users have unlimited QR codes if (isPremium) return true; // Logged users (non-premium) have unlimited QR codes if (!string.IsNullOrEmpty(userId)) return true; // Anonymous users have 3 QR codes per day var dailyCount = await GetDailyQRCountAsync(userId); var limit = 3; return dailyCount < limit; } public async Task SaveQRToHistoryAsync(string? userId, QRGenerationResult qrResult) { try { var qrHistory = new QRCodeHistory { UserId = userId, Type = qrResult.RequestSettings?.Type ?? "unknown", Content = qrResult.RequestSettings?.Content ?? "", QRCodeBase64 = qrResult.QRCodeBase64, CustomizationSettings = JsonSerializer.Serialize(qrResult.RequestSettings), CreatedAt = DateTime.UtcNow, Language = qrResult.RequestSettings?.Language ?? "pt-BR", Size = qrResult.RequestSettings?.Size ?? 300, GenerationTimeMs = qrResult.GenerationTimeMs, FromCache = qrResult.FromCache, IsActive = true, LastAccessedAt = DateTime.UtcNow }; await _context.QRCodeHistory.InsertOneAsync(qrHistory); // Update user's QR history IDs if logged in if (!string.IsNullOrEmpty(userId)) { var update = Builders.Update .Push(u => u.QRHistoryIds, qrHistory.Id); await _context.Users.UpdateOneAsync(u => u.Id == userId, update); } } catch (Exception ex) { _logger.LogError(ex, $"Error saving QR to history: {ex.Message}"); } } public async Task> GetUserQRHistoryAsync(string userId, int limit = 50) { try { return await _context.QRCodeHistory .Find(q => q.UserId == userId && q.IsActive) .SortByDescending(q => q.CreatedAt) .Limit(limit) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, $"Error getting QR history for user {userId}: {ex.Message}"); return new List(); } } public async Task GetQRDataAsync(string qrId) { try { return await _context.QRCodeHistory .Find(q => q.Id == qrId && q.IsActive) .FirstOrDefaultAsync(); } catch (Exception ex) { _logger.LogError(ex, $"Error getting QR data {qrId}: {ex.Message}"); return null; } } public async Task DeleteQRFromHistoryAsync(string userId, string qrId) { try { // First verify that the QR code belongs to the user var qrCode = await _context.QRCodeHistory .Find(q => q.Id == qrId && q.UserId == userId && q.IsActive) .FirstOrDefaultAsync(); if (qrCode == null) { _logger.LogWarning($"QR code not found or doesn't belong to user - QRId: {qrId}, UserId: {userId}"); return false; } // Soft delete: mark as inactive instead of permanently deleting var update = Builders.Update.Set(q => q.IsActive, false); var result = await _context.QRCodeHistory.UpdateOneAsync( q => q.Id == qrId && q.UserId == userId, update); return result.ModifiedCount > 0; } catch (Exception ex) { _logger.LogError(ex, $"Error deleting QR from history - QRId: {qrId}, UserId: {userId}: {ex.Message}"); return false; } } public async Task GetQRCountThisMonthAsync(string userId) { try { var startOfMonth = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1); var endOfMonth = startOfMonth.AddMonths(1); var count = await _context.QRCodeHistory .CountDocumentsAsync(q => q.UserId == userId && q.CreatedAt >= startOfMonth && q.CreatedAt < endOfMonth); return (int)count; } catch (Exception ex) { _logger.LogError(ex, $"Error getting monthly QR count for user {userId}: {ex.Message}"); return 0; } } // MÉTODO REMOVIDO: ExtendAdFreeTimeAsync - não é mais necessário public async Task GetUserEmailAsync(string userId) { var user = await GetUserAsync(userId); return user?.Email ?? string.Empty; } public async Task MarkPremiumCancelledAsync(string userId, DateTime cancelledAt) { try { if (_context.Users == null) return; // Development mode without MongoDB var update = Builders.Update .Set(u => u.IsPremium, false) .Set(u => u.PremiumCancelledAt, cancelledAt) .Set(u => u.PremiumExpiresAt, null); await _context.Users.UpdateOneAsync(u => u.Id == userId, update); _logger.LogInformation($"Marked premium as cancelled for user {userId} at {cancelledAt}"); } catch (Exception ex) { _logger.LogError(ex, $"Error marking premium cancelled for user {userId}: {ex.Message}"); } } public async Task> GetUsersForHistoryCleanupAsync(DateTime cutoffDate) { try { if (_context.Users == null) return new List(); // Development mode without MongoDB return await _context.Users .Find(u => u.PremiumCancelledAt != null && u.PremiumCancelledAt < cutoffDate && u.QRHistoryIds.Count > 0) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, $"Error getting users for history cleanup: {ex.Message}"); return new List(); } } public async Task DeleteUserHistoryAsync(string userId) { try { if (_context.Users == null || _context.QRCodeHistory == null) return; // Development mode without MongoDB var user = await GetUserAsync(userId); if (user?.QRHistoryIds?.Any() == true) { // Remover histórico de QR codes await _context.QRCodeHistory.DeleteManyAsync(qr => user.QRHistoryIds.Contains(qr.Id)); // Limpar lista de histórico do usuário var update = Builders.Update.Set(u => u.QRHistoryIds, new List()); await _context.Users.UpdateOneAsync(u => u.Id == userId, update); _logger.LogInformation($"Deleted history for user {userId}"); } } catch (Exception ex) { _logger.LogError(ex, $"Error deleting history for user {userId}: {ex.Message}"); } } public async Task ActivatePremiumStatus(string userId, string stripeSubscriptionId, DateTime expiryDate) { var update = Builders.Update .Set(u => u.IsPremium, true) .Set(u => u.StripeSubscriptionId, stripeSubscriptionId) .Set(u => u.PremiumExpiresAt, expiryDate) .Unset(u => u.PremiumCancelledAt); await _context.Users.UpdateOneAsync(u => u.Id == userId, update); _logger.LogInformation($"Activated premium for user {userId}"); } public async Task DeactivatePremiumStatus(string stripeSubscriptionId) { var update = Builders.Update .Set(u => u.IsPremium, false) .Set(u => u.PremiumCancelledAt, DateTime.UtcNow); await _context.Users.UpdateOneAsync(u => u.StripeSubscriptionId == stripeSubscriptionId, update); _logger.LogInformation($"Deactivated premium for subscription {stripeSubscriptionId}"); } public async Task GetUserByStripeCustomerIdAsync(string customerId) { return await _context.Users.Find(u => u.StripeCustomerId == customerId).FirstOrDefaultAsync(); } public async Task UpdateUserStripeCustomerIdAsync(string userId, string stripeCustomerId) { var update = Builders.Update.Set(u => u.StripeCustomerId, stripeCustomerId); await _context.Users.UpdateOneAsync(u => u.Id == userId, update); } } }