using Microsoft.AspNetCore.Mvc; using BCards.Web.Services; using BCards.Web.Models; using BCards.Web.ViewModels; using BCards.Web.Repositories; using System.Security.Claims; using BCards.Web.Attributes; namespace BCards.Web.Controllers; [ModeratorAuthorize] [Route("Moderation")] public class ModerationController : Controller { private readonly IModerationService _moderationService; private readonly IEmailService _emailService; private readonly IUserRepository _userRepository; private readonly ILogger _logger; public ModerationController( IModerationService moderationService, IEmailService emailService, IUserRepository userRepository, ILogger logger) { _moderationService = moderationService; _emailService = emailService; _userRepository = userRepository; _logger = logger; } [HttpGet("Dashboard")] public async Task Dashboard(int page = 1, int size = 20, string? filter = null) { var skip = (page - 1) * size; var pendingPages = await _moderationService.GetPendingModerationAsync(skip, size, filter); var stats = await _moderationService.GetModerationStatsAsync(); var viewModel = new ModerationDashboardViewModel { PendingPages = pendingPages.Select(p => new PendingPageViewModel { Id = p.Id, DisplayName = p.DisplayName, Category = p.Category, Slug = p.Slug, CreatedAt = p.CreatedAt, ModerationAttempts = p.ModerationAttempts, PlanType = p.PlanLimitations.PlanType.ToString(), IsSpecialModeration = p.PlanLimitations.SpecialModeration ?? false, PreviewUrl = !string.IsNullOrEmpty(p.PreviewToken) ? $"/page/{p.Category}/{p.Slug}?preview={p.PreviewToken}" : null }).ToList(), Stats = stats, CurrentPage = page, PageSize = size, HasNextPage = pendingPages.Count == size, CurrentFilter = filter ?? "all" }; return View(viewModel); } [HttpGet("Review/{id}")] public async Task Review(string id) { var page = await _moderationService.GetPageForModerationAsync(id); if (page == null) { TempData["Error"] = "Página não encontrada ou não está pendente de moderação."; return RedirectToAction("Dashboard"); } var user = await _userRepository.GetByIdAsync(page.UserId); if (user == null) { TempData["Error"] = "Usuário não encontrado."; return RedirectToAction("Dashboard"); } var viewModel = new ModerationReviewViewModel { Page = page, User = user, PreviewUrl = !string.IsNullOrEmpty(page.PreviewToken) ? $"/page/{page.Category}/{page.Slug}?preview={page.PreviewToken}" : null, ModerationCriteria = GetModerationCriteria() }; return View(viewModel); } [HttpPost("Approve/{id}")] [ValidateAntiForgeryToken] public async Task Approve(string id, string notes) { try { var page = await _moderationService.GetPageForModerationAsync(id); if (page == null) { TempData["Error"] = "Página não encontrada."; return RedirectToAction("Dashboard"); } var user = await _userRepository.GetByIdAsync(page.UserId); var moderatorId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "system"; await _moderationService.ApprovePageAsync(id, moderatorId, notes); if (user != null) { await _emailService.SendModerationStatusAsync( user.Email, user.Name, page.DisplayName, PageStatus.Active.ToString()); } TempData["Success"] = $"Página '{page.DisplayName}' aprovada com sucesso!"; return RedirectToAction("Dashboard"); } catch (Exception ex) { _logger.LogError(ex, "Error approving page {PageId}", id); TempData["Error"] = "Erro ao aprovar página."; return RedirectToAction("Review", new { id }); } } [HttpPost("Reject/{id}")] [ValidateAntiForgeryToken] public async Task Reject(string id, string reason, List issues) { try { var page = await _moderationService.GetPageForModerationAsync(id); if (page == null) { TempData["Error"] = "Página não encontrada."; return RedirectToAction("Dashboard"); } var user = await _userRepository.GetByIdAsync(page.UserId); var moderatorId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "system"; await _moderationService.RejectPageAsync(id, moderatorId, reason, issues); if (user != null) { await _emailService.SendModerationStatusAsync( user.Email, user.Name, page.DisplayName, "rejected", reason); } TempData["Success"] = $"Página '{page.DisplayName}' rejeitada."; return RedirectToAction("Dashboard"); } catch (Exception ex) { _logger.LogError(ex, "Error rejecting page {PageId}", id); TempData["Error"] = "Erro ao rejeitar página."; return RedirectToAction("Review", new { id }); } } [HttpGet("History")] public async Task History(int page = 1, int size = 20) { var skip = (page - 1) * size; var historyPages = await _moderationService.GetModerationHistoryAsync(skip, size); var viewModel = new ModerationHistoryViewModel { Pages = historyPages.Select(p => new ModerationPageViewModel { Id = p.Id, DisplayName = p.DisplayName, Category = p.Category, Slug = p.Slug, CreatedAt = p.CreatedAt, Status = p.Status.ToString(), ModerationAttempts = p.ModerationAttempts, PlanType = p.PlanLimitations.PlanType.ToString(), ApprovedAt = p.ApprovedAt, LastModerationEntry = p.ModerationHistory.LastOrDefault() }).ToList(), CurrentPage = page, PageSize = size, HasNextPage = historyPages.Count == size }; return View(viewModel); } private List GetModerationCriteria() { return new List { new() { Category = "Conteúdo Proibido", Items = new List { "Pornografia e conteúdo sexual explícito", "Drogas ilegais e substâncias controladas", "Armas e explosivos", "Atividades ilegais (fraudes, pirataria)", "Apostas e jogos de azar", "Criptomoedas e esquemas de pirâmide", "Conteúdo que promove violência ou ódio", "Spam e links suspeitos/maliciosos" }}, new() { Category = "Conteúdo Suspeito", Items = new List { "Excesso de anúncios (>30% dos links)", "Sites com pop-ups excessivos", "Links encurtados suspeitos", "Conteúdo que imita marcas conhecidas", "Produtos \"milagrosos\"" }}, new() { Category = "Verificações Técnicas", Items = new List { "Links funcionais (não quebrados)", "Sites com SSL válido", "Não redirecionamentos maliciosos" }} }; } }