using Microsoft.AspNetCore.Mvc; using QRRapidoApp.Models.ViewModels; using QRRapidoApp.Services; using System.Diagnostics; using System.Security.Claims; using System.Text; using Microsoft.Extensions.Localization; namespace QRRapidoApp.Controllers { [ApiController] [Route("api/[controller]")] public class QRController : ControllerBase { private readonly IQRCodeService _qrService; private readonly IUserService _userService; private readonly AdDisplayService _adService; private readonly ILogger _logger; private readonly IStringLocalizer _localizer; public QRController(IQRCodeService qrService, IUserService userService, AdDisplayService adService, ILogger logger, IStringLocalizer localizer) { _qrService = qrService; _userService = userService; _adService = adService; _logger = logger; _localizer = localizer; } [HttpPost("GenerateRapid")] public async Task GenerateRapid([FromBody] QRGenerationRequest request) { var stopwatch = Stopwatch.StartNew(); var requestId = Guid.NewGuid().ToString("N")[..8]; var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; var isAuthenticated = User?.Identity?.IsAuthenticated ?? false; using (_logger.BeginScope(new Dictionary { ["RequestId"] = requestId, ["UserId"] = userId ?? "anonymous", ["IsAuthenticated"] = isAuthenticated, ["QRType"] = request.Type ?? "unknown", ["ContentLength"] = request.Content?.Length ?? 0, ["QRGeneration"] = true })) { _logger.LogInformation("QR generation request started - Type: {QRType}, ContentLength: {ContentLength}, User: {UserType}", request.Type, request.Content?.Length ?? 0, isAuthenticated ? "authenticated" : "anonymous"); try { // Quick validations if (string.IsNullOrWhiteSpace(request.Content)) { _logger.LogWarning("QR generation failed - empty content provided"); return BadRequest(new { error = _localizer["RequiredContent"], success = false }); } if (request.Content.Length > 4000) // Limit to maintain speed { _logger.LogWarning("QR generation failed - content too long: {ContentLength} characters", request.Content.Length); return BadRequest(new { error = _localizer["ContentTooLong"], success = false }); } // Check user status var user = await _userService.GetUserAsync(userId); // Validate premium features if (!string.IsNullOrEmpty(request.CornerStyle) && request.CornerStyle != "square" && user?.IsPremium != true) { _logger.LogWarning("Custom corner style attempted by non-premium user - UserId: {UserId}, CornerStyle: {CornerStyle}", userId ?? "anonymous", request.CornerStyle); return BadRequest(new { error = _localizer["PremiumCornerStyleRequired"], requiresPremium = true, success = false }); } // Rate limiting for free users var rateLimitPassed = await CheckRateLimitAsync(userId, user); if (!rateLimitPassed) { _logger.LogWarning("QR generation rate limited - User: {UserId}, IsPremium: {IsPremium}", userId ?? "anonymous", user?.IsPremium ?? false); return StatusCode(429, new { error = _localizer["RateLimitReached"], upgradeUrl = "/Premium/Upgrade", success = false }); } // Configure optimizations based on user request.IsPremium = user?.IsPremium == true; request.OptimizeForSpeed = true; _logger.LogDebug("Generating QR code - IsPremium: {IsPremium}, OptimizeForSpeed: {OptimizeForSpeed}", request.IsPremium, request.OptimizeForSpeed); // Generate QR code var generationStopwatch = Stopwatch.StartNew(); var result = await _qrService.GenerateRapidAsync(request); generationStopwatch.Stop(); if (!result.Success) { _logger.LogError("QR generation failed - Error: {ErrorMessage}, GenerationTime: {GenerationTimeMs}ms", result.ErrorMessage, generationStopwatch.ElapsedMilliseconds); return StatusCode(500, new { error = result.ErrorMessage, success = false }); } _logger.LogInformation("QR code generated successfully - GenerationTime: {GenerationTimeMs}ms, FromCache: {FromCache}, Size: {Size}px", generationStopwatch.ElapsedMilliseconds, result.FromCache, request.Size); // Update counter for all logged users if (userId != null) { if (request.IsPremium) { result.RemainingQRs = int.MaxValue; // Premium users have unlimited // Still increment the count for statistics await _userService.IncrementDailyQRCountAsync(userId); } else { var remaining = await _userService.IncrementDailyQRCountAsync(userId); result.RemainingQRs = remaining; _logger.LogDebug("Updated QR count for free user - Remaining: {RemainingQRs}", remaining); } } // Save to history if user is logged in (fire and forget) if (userId != null) { _ = Task.Run(async () => { try { await _userService.SaveQRToHistoryAsync(userId, result); _logger.LogDebug("QR code saved to history successfully for user {UserId}", userId); } catch (Exception ex) { _logger.LogError(ex, "Error saving QR to history for user {UserId}", userId); } }); } stopwatch.Stop(); var totalTimeMs = stopwatch.ElapsedMilliseconds; // Performance logging with structured data using (_logger.BeginScope(new Dictionary { ["TotalRequestTimeMs"] = totalTimeMs, ["QRGenerationTimeMs"] = generationStopwatch.ElapsedMilliseconds, ["ServiceGenerationTimeMs"] = result.GenerationTimeMs, ["FromCache"] = result.FromCache, ["UserType"] = request.IsPremium ? "premium" : "free", ["QRSize"] = request.Size, ["Success"] = true })) { var performanceStatus = totalTimeMs switch { < 500 => "excellent", < 1000 => "good", < 2000 => "acceptable", _ => "slow" }; _logger.LogInformation("QR generation completed - TotalTime: {TotalTimeMs}ms, ServiceTime: {ServiceTimeMs}ms, Performance: {PerformanceStatus}, Cache: {FromCache}", totalTimeMs, result.GenerationTimeMs, performanceStatus, result.FromCache); } return Ok(result); } catch (Exception ex) { stopwatch.Stop(); _logger.LogError(ex, "QR generation failed with exception - RequestTime: {RequestTimeMs}ms, UserId: {UserId}", stopwatch.ElapsedMilliseconds, userId ?? "anonymous"); return StatusCode(500, new { error = "Erro interno do servidor", success = false }); } } } [HttpGet("Download/{qrId}")] public async Task Download(string qrId, string format = "png") { var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; var stopwatch = Stopwatch.StartNew(); using (_logger.BeginScope(new Dictionary { ["QRId"] = qrId, ["Format"] = format.ToLower(), ["UserId"] = userId ?? "anonymous", ["QRDownload"] = true })) { _logger.LogInformation("QR download requested - QRId: {QRId}, Format: {Format}", qrId, format); try { var qrData = await _userService.GetQRDataAsync(qrId); if (qrData == null) { _logger.LogWarning("QR download failed - QR code not found: {QRId}", qrId); return NotFound(); } var contentType = format.ToLower() switch { "svg" => "image/svg+xml", "pdf" => "application/pdf", _ => "image/png" }; var fileName = $"qrrapido-{DateTime.Now:yyyyMMdd-HHmmss}.{format}"; _logger.LogDebug("Converting QR to format - QRId: {QRId}, Format: {Format}, Size: {Size}", qrId, format, qrData.Size); byte[] fileContent; if (format.ToLower() == "svg") { fileContent = await _qrService.ConvertToSvgAsync(qrData.QRCodeBase64); } else if (format.ToLower() == "pdf") { fileContent = await _qrService.ConvertToPdfAsync(qrData.QRCodeBase64, qrData.Size); } else { fileContent = Convert.FromBase64String(qrData.QRCodeBase64); } stopwatch.Stop(); _logger.LogInformation("QR download completed - QRId: {QRId}, Format: {Format}, Size: {FileSize} bytes, ProcessingTime: {ProcessingTimeMs}ms", qrId, format, fileContent.Length, stopwatch.ElapsedMilliseconds); return File(fileContent, contentType, fileName); } catch (Exception ex) { stopwatch.Stop(); _logger.LogError(ex, "QR download failed - QRId: {QRId}, Format: {Format}, ProcessingTime: {ProcessingTimeMs}ms", qrId, format, stopwatch.ElapsedMilliseconds); return StatusCode(500); } } } [HttpPost("SaveToHistory")] public async Task SaveToHistory([FromBody] SaveToHistoryRequest request) { var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; using (_logger.BeginScope(new Dictionary { ["QRId"] = request.QrId, ["UserId"] = userId ?? "anonymous", ["SaveToHistory"] = true })) { _logger.LogInformation("Save to history requested - QRId: {QRId}", request.QrId); try { if (string.IsNullOrEmpty(userId)) { _logger.LogWarning("Save to history failed - user not authenticated"); return Unauthorized(); } var qrData = await _userService.GetQRDataAsync(request.QrId); if (qrData == null) { _logger.LogWarning("Save to history failed - QR code not found: {QRId}", request.QrId); return NotFound(); } // QR is already saved when generated, just return success _logger.LogInformation("QR code already saved in history - QRId: {QRId}", request.QrId); return Ok(new { success = true, message = "QR Code salvo no histórico!" }); } catch (Exception ex) { _logger.LogError(ex, "Save to history failed - QRId: {QRId}", request.QrId); return StatusCode(500, new { error = "Erro ao salvar no histórico." }); } } } [HttpPost("GenerateRapidWithLogo")] public async Task GenerateRapidWithLogo([FromForm] QRGenerationRequest request, IFormFile? logo) { var stopwatch = Stopwatch.StartNew(); var requestId = Guid.NewGuid().ToString("N")[..8]; var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; var isAuthenticated = User?.Identity?.IsAuthenticated ?? false; // DEBUG: Log detalhado dos parâmetros recebidos _logger.LogInformation("🔍 [DEBUG] GenerateRapidWithLogo called - RequestId: {RequestId}, ApplyLogoColorization: {ApplyLogoColorization}, LogoSizePercent: {LogoSizePercent}, HasLogo: {HasLogo}", requestId, request.ApplyLogoColorization, request.LogoSizePercent, request.HasLogo); using (_logger.BeginScope(new Dictionary { ["RequestId"] = requestId, ["UserId"] = userId ?? "anonymous", ["IsAuthenticated"] = isAuthenticated, ["QRType"] = request.Type ?? "unknown", ["ContentLength"] = request.Content?.Length ?? 0, ["QRGeneration"] = true, ["HasLogo"] = logo != null })) { _logger.LogInformation("QR generation with logo request started - Type: {QRType}, ContentLength: {ContentLength}, HasLogo: {HasLogo}", request.Type, request.Content?.Length ?? 0, logo != null); try { // Quick validations if (string.IsNullOrWhiteSpace(request.Content)) { _logger.LogWarning("QR generation failed - empty content provided"); return BadRequest(new { error = _localizer["RequiredContent"], success = false }); } if (request.Content.Length > 4000) { _logger.LogWarning("QR generation failed - content too long: {ContentLength} characters", request.Content.Length); return BadRequest(new { error = _localizer["ContentTooLong"], success = false }); } // Check user status var user = await _userService.GetUserAsync(userId); // Validate premium status for logo feature if (user?.IsPremium != true) { _logger.LogWarning("Logo upload attempted by non-premium user - UserId: {UserId}", userId ?? "anonymous"); return BadRequest(new { error = _localizer["PremiumLogoRequired"], requiresPremium = true, success = false }); } // Validate premium corner styles if (!string.IsNullOrEmpty(request.CornerStyle) && request.CornerStyle != "square") { _logger.LogInformation("Premium user using custom corner style - UserId: {UserId}, CornerStyle: {CornerStyle}", userId, request.CornerStyle); } // Process logo upload if provided if (logo != null && logo.Length > 0) { // Validate file size (2MB max) if (logo.Length > 2 * 1024 * 1024) { _logger.LogWarning("Logo upload failed - file too large: {FileSize} bytes", logo.Length); return BadRequest(new { error = _localizer["LogoTooLarge"], success = false }); } // Validate file format var allowedTypes = new[] { "image/png", "image/jpeg", "image/jpg" }; if (!allowedTypes.Contains(logo.ContentType?.ToLower())) { _logger.LogWarning("Logo upload failed - invalid format: {ContentType}", logo.ContentType); return BadRequest(new { error = _localizer["InvalidLogoFormat"], success = false }); } try { // Convert file to byte array using var memoryStream = new MemoryStream(); await logo.CopyToAsync(memoryStream); request.Logo = memoryStream.ToArray(); request.HasLogo = true; _logger.LogInformation("Logo processed successfully - Size: {LogoSize} bytes, Format: {ContentType}, SizePercent: {SizePercent}%, Colorized: {Colorized}", logo.Length, logo.ContentType, request.LogoSizePercent ?? 20, request.ApplyLogoColorization); } catch (Exception ex) { _logger.LogError(ex, "Error processing logo file"); return BadRequest(new { error = _localizer["ErrorProcessingLogo"], success = false }); } } // Rate limiting for free users (premium users get unlimited) var rateLimitPassed = await CheckRateLimitAsync(userId, user); if (!rateLimitPassed) { _logger.LogWarning("QR generation rate limited - User: {UserId}, IsPremium: {IsPremium}", userId ?? "anonymous", user?.IsPremium ?? false); return StatusCode(429, new { error = _localizer["RateLimitReached"], upgradeUrl = "/Premium/Upgrade", success = false }); } // Configure optimizations based on user request.IsPremium = user?.IsPremium == true; request.OptimizeForSpeed = true; _logger.LogDebug("Generating QR code with logo - IsPremium: {IsPremium}, HasLogo: {HasLogo}, LogoSize: {LogoSize}%, Colorized: {Colorized}", request.IsPremium, request.HasLogo, request.LogoSizePercent ?? 20, request.ApplyLogoColorization); // Generate QR code var generationStopwatch = Stopwatch.StartNew(); var result = await _qrService.GenerateRapidAsync(request); generationStopwatch.Stop(); if (!result.Success) { _logger.LogError("QR generation failed - Error: {ErrorMessage}, GenerationTime: {GenerationTimeMs}ms", result.ErrorMessage, generationStopwatch.ElapsedMilliseconds); return StatusCode(500, new { error = result.ErrorMessage, success = false }); } _logger.LogInformation("QR code with logo generated successfully - GenerationTime: {GenerationTimeMs}ms, FromCache: {FromCache}, HasLogo: {HasLogo}, Base64Length: {Base64Length}", generationStopwatch.ElapsedMilliseconds, result.FromCache, request.HasLogo, result.QRCodeBase64?.Length ?? 0); // Save to history if user is logged in (fire and forget) if (userId != null) { _ = Task.Run(async () => { try { await _userService.SaveQRToHistoryAsync(userId, result); _logger.LogDebug("QR code saved to history successfully for user {UserId}", userId); } catch (Exception ex) { _logger.LogError(ex, "Error saving QR to history for user {UserId}", userId); } }); } stopwatch.Stop(); return Ok(result); } catch (Exception ex) { stopwatch.Stop(); _logger.LogError(ex, "QR generation with logo failed with exception - RequestTime: {RequestTimeMs}ms, UserId: {UserId}", stopwatch.ElapsedMilliseconds, userId ?? "anonymous"); return StatusCode(500, new { error = "Erro interno do servidor", success = false }); } } } [HttpGet("History")] public async Task GetHistory(int limit = 20) { try { var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { return Unauthorized(); } var history = await _userService.GetUserQRHistoryAsync(userId, limit); return Ok(history); } catch (Exception ex) { _logger.LogError(ex, "Error getting QR history"); return StatusCode(500); } } [HttpGet("GetUserStats")] public async Task GetUserStats() { try { var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { return Unauthorized(); } var user = await _userService.GetUserAsync(userId); var isPremium = user?.IsPremium ?? false; // For logged users (premium or not), return -1 to indicate unlimited // For consistency with the frontend logic var remainingCount = -1; // Unlimited for all logged users return Ok(new { remainingCount = remainingCount, isPremium = isPremium, isUnlimited = true }); } catch (Exception ex) { _logger.LogError(ex, "Error getting user stats"); return StatusCode(500); } } [HttpDelete("History/{qrId}")] public async Task DeleteFromHistory(string qrId) { try { var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { return Unauthorized(); } var success = await _userService.DeleteQRFromHistoryAsync(userId, qrId); if (success) { _logger.LogInformation("QR code deleted from history - QRId: {QRId}, UserId: {UserId}", qrId, userId); return Ok(new { success = true, message = _localizer["QRCodeDeleted"].Value }); } else { return NotFound(new { success = false, message = _localizer["ErrorDeletingQR"].Value }); } } catch (Exception ex) { _logger.LogError(ex, "Error deleting QR from history - QRId: {QRId}", qrId); return StatusCode(500, new { success = false, message = _localizer["ErrorDeletingQR"].Value }); } } private async Task CheckRateLimitAsync(string? userId, Models.User? user) { // Premium users have unlimited QR codes if (user?.IsPremium == true) return true; // Logged users (non-premium) have unlimited QR codes if (userId != null) return true; // Anonymous users have 3 QR codes per day var dailyLimit = 3; var currentCount = await _userService.GetDailyQRCountAsync(userId); return currentCount < dailyLimit; } } public class SaveToHistoryRequest { public string QrId { get; set; } = string.Empty; } }