using Microsoft.AspNetCore.Mvc; using QRRapidoApp.Data; using QRRapidoApp.Models; using QRRapidoApp.Services; using MongoDB.Driver; using System.Security.Claims; namespace QRRapidoApp.Controllers { public class AdminController : Controller { private readonly MongoDbContext _context; private readonly ILogger _logger; private readonly IConfiguration _config; private readonly IUserService _userService; public AdminController(MongoDbContext context, ILogger logger, IConfiguration config, IUserService userService) { _context = context; _logger = logger; _config = config; _userService = userService; } private bool IsAdmin() { // 1. Check if authenticated if (User?.Identity?.IsAuthenticated != true) return false; // 2. Get User Email var userEmail = User.FindFirst(ClaimTypes.Email)?.Value; if (string.IsNullOrEmpty(userEmail)) { // Fallback: try to find user by ID to get email var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.IsNullOrEmpty(userId)) { var user = _userService.GetUserAsync(userId).Result; userEmail = user?.Email; } } if (string.IsNullOrEmpty(userEmail)) return false; // 3. Check against AllowedEmails list var allowedEmails = _config.GetSection("Admin:AllowedEmails").Get>() ?? new List(); return allowedEmails.Contains(userEmail, StringComparer.OrdinalIgnoreCase); } private bool IsLocalhost() { var remoteIp = HttpContext.Connection.RemoteIpAddress; return remoteIp != null && (remoteIp.ToString() == "127.0.0.1" || remoteIp.ToString() == "::1" || remoteIp.ToString() == "localhost"); } // --- View Actions --- [HttpGet("/Admin")] public async Task Index() { if (!IsAdmin()) return RedirectToAction("Index", "Home"); try { var orders = await _context.Orders .Find(o => o.Status == "Pending") .SortByDescending(o => o.CreatedAt) .ToListAsync(); return View(orders); } catch (Exception ex) { _logger.LogError(ex, "Error fetching pending orders for view"); return View(new List()); } } // --- API Endpoints --- [HttpGet("api/Admin/Orders/Pending")] public async Task GetPendingOrders() { if (!IsAdmin()) return Unauthorized("Access denied. Admin rights required."); try { var orders = await _context.Orders .Find(o => o.Status == "Pending") .SortByDescending(o => o.CreatedAt) .ToListAsync(); return Ok(orders); } catch (Exception ex) { _logger.LogError(ex, "Error fetching pending orders"); return StatusCode(500, new { error = "Internal server error" }); } } [HttpPost("api/Admin/Orders/{orderId}/Approve")] public async Task ApproveOrder(string orderId) { if (!IsAdmin()) return Unauthorized("Access denied. Admin rights required."); try { var adminEmail = User.FindFirst(ClaimTypes.Email)?.Value ?? "unknown_admin"; // 1. Get the order var order = await _context.Orders .Find(o => o.Id == orderId) .FirstOrDefaultAsync(); if (order == null) return NotFound("Order not found"); if (order.Status == "Paid") return BadRequest("Order already paid"); // 2. Update Order Status var updateOrder = Builders.Update .Set(o => o.Status, "Paid") .Set(o => o.PaidAt, DateTime.UtcNow) .Set(o => o.ApprovedBy, adminEmail); await _context.Orders.UpdateOneAsync(o => o.Id == orderId, updateOrder); // 3. Add Credits to User var userUpdate = Builders.Update.Inc(u => u.Credits, order.CreditsAmount); await _context.Users.UpdateOneAsync(u => u.Id == order.UserId, userUpdate); _logger.LogInformation($"Order {orderId} approved by {adminEmail}. {order.CreditsAmount} credits added to user {order.UserId}"); return Ok(new { success = true, message = "Order approved and credits added." }); } catch (Exception ex) { _logger.LogError(ex, $"Error approving order {orderId}"); return StatusCode(500, new { error = "Internal server error" }); } } [HttpPost("api/Admin/Orders/{orderId}/Reject")] public async Task RejectOrder(string orderId) { if (!IsAdmin()) return Unauthorized("Access denied. Admin rights required."); try { var adminEmail = User.FindFirst(ClaimTypes.Email)?.Value ?? "unknown_admin"; var update = Builders.Update .Set(o => o.Status, "Rejected") .Set(o => o.ApprovedBy, adminEmail); // Rejected by... var result = await _context.Orders.UpdateOneAsync(o => o.Id == orderId, update); if (result.ModifiedCount == 0) return NotFound("Order not found"); _logger.LogInformation($"Order {orderId} rejected by {adminEmail}"); return Ok(new { success = true, message = "Order rejected." }); } catch (Exception ex) { _logger.LogError(ex, $"Error rejecting order {orderId}"); return StatusCode(500, new { error = "Internal server error" }); } } // --- Legacy Localhost-Only Endpoints --- [HttpPost("api/Admin/SeedPlans")] public async Task SeedPlans([FromBody] List plans) { if (!IsLocalhost()) return Forbid("Localhost only"); try { foreach (var plan in plans) { var filter = Builders.Filter.Eq(p => p.Interval, plan.Interval); var options = new ReplaceOptions { IsUpsert = true }; await _context.Plans.ReplaceOneAsync(filter, plan, options); } return Ok(new { success = true, message = "Plans seeded" }); } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } [HttpGet("api/Admin/Plans")] public async Task GetPlans() { if (!IsLocalhost()) return Forbid("Localhost only"); var plans = await _context.Plans.Find(_ => true).ToListAsync(); return Ok(new { success = true, plans }); } [HttpDelete("api/Admin/Plans")] public async Task DeleteAllPlans() { if (!IsLocalhost()) return Forbid("Localhost only"); await _context.Plans.DeleteManyAsync(_ => true); return Ok(new { success = true }); } } }