using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using QRRapidoApp.Models; using QRRapidoApp.Services; using System.Security.Claims; namespace QRRapidoApp.Controllers { [Authorize] [Route("Developer")] [Route("es-PY/Developer")] [Route("es/Developer")] [Route("en/Developer")] public class DeveloperController : Controller { private readonly IUserService _userService; private readonly StripeService _stripeService; private readonly ILogger _logger; public DeveloperController( IUserService userService, StripeService stripeService, ILogger logger) { _userService = userService; _stripeService = stripeService; _logger = logger; } [HttpGet("")] public async Task Index() { var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) return Unauthorized(); var user = await _userService.GetUserAsync(userId); if (user == null) return NotFound(); ViewBag.Culture = GetCulture(); return View(user); } [HttpPost("GenerateKey")] [ValidateAntiForgeryToken] public async Task GenerateKey(string keyName) { var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) return Unauthorized(); if (string.IsNullOrWhiteSpace(keyName) || keyName.Length > 50) { TempData["Error"] = "Nome da chave inválido (máx. 50 caracteres)."; return Redirect(GetDevUrl()); } var user = await _userService.GetUserAsync(userId); if (user == null) return NotFound(); if (user.ApiKeys.Count(k => k.IsActive) >= 5) { TempData["Error"] = "Limite de 5 chaves ativas atingido. Revogue uma antes de criar outra."; return Redirect(GetDevUrl()); } var (rawKey, prefix) = await _userService.GenerateApiKeyAsync(userId, keyName.Trim()); _logger.LogInformation("API key '{Prefix}' generated for user {UserId}", prefix, userId); TempData["NewKey"] = rawKey; TempData["NewKeyName"] = keyName.Trim(); return RedirectToAction(nameof(Index)); } [HttpGet("Pricing")] public async Task Pricing() { var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; User? user = null; if (!string.IsNullOrEmpty(userId)) user = await _userService.GetUserAsync(userId); ViewBag.CurrentTier = user?.ApiSubscription?.EffectiveTier ?? ApiPlanTier.Free; ViewBag.Culture = GetCulture(); return View(); } [HttpPost("SubscribeApi")] [ValidateAntiForgeryToken] public async Task SubscribeApi(string planTier) { var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) return Unauthorized(); if (!Enum.TryParse(planTier, out var tier) || tier == ApiPlanTier.Free || tier == ApiPlanTier.Enterprise) { TempData["Error"] = "Plano inválido selecionado."; return Redirect(GetDevUrl("Pricing")); } try { var baseUrl = $"{Request.Scheme}://{Request.Host}"; var checkoutUrl = await _stripeService.CreateApiSubscriptionCheckoutAsync(userId, tier, baseUrl); return Redirect(checkoutUrl); } catch (Exception ex) { _logger.LogError(ex, "Error creating API subscription checkout for user {UserId}", userId); TempData["Error"] = "Erro ao criar sessão de pagamento. Tente novamente."; return Redirect(GetDevUrl("Pricing")); } } [HttpPost("RevokeKey")] [ValidateAntiForgeryToken] public async Task RevokeKey(string prefix) { var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) return Unauthorized(); if (string.IsNullOrWhiteSpace(prefix)) { TempData["Error"] = "Chave inválida."; return Redirect(GetDevUrl()); } var revoked = await _userService.RevokeApiKeyAsync(userId, prefix); if (revoked) { _logger.LogInformation("API key '{Prefix}' revoked by user {UserId}", prefix, userId); TempData["Success"] = "Chave revogada com sucesso."; } else { TempData["Error"] = "Chave não encontrada ou já inativa."; } return RedirectToAction(nameof(Index)); } private string GetCulture() { var path = Request.Path.Value ?? ""; if (path.StartsWith("/es-PY", StringComparison.OrdinalIgnoreCase)) return "es-PY"; if (path.StartsWith("/es/", StringComparison.OrdinalIgnoreCase) || string.Equals(path, "/es", StringComparison.OrdinalIgnoreCase)) return "es"; if (path.StartsWith("/en/", StringComparison.OrdinalIgnoreCase) || string.Equals(path, "/en", StringComparison.OrdinalIgnoreCase)) return "en"; return "pt-BR"; } private string GetDevUrl(string action = "") { var base_ = GetCulture() switch { "es-PY" => "/es-PY/Developer", "es" => "/es/Developer", "en" => "/en/Developer", _ => "/Developer" }; return string.IsNullOrEmpty(action) ? base_ : $"{base_}/{action}"; } } }