2026-01-29 13:43:44 -03:00
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using MotoresArgentinosV2.Infrastructure.Data;
|
|
|
|
|
using MotoresArgentinosV2.Core.Entities;
|
|
|
|
|
using MotoresArgentinosV2.Core.Interfaces;
|
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
|
|
|
|
|
namespace MotoresArgentinosV2.API.Controllers;
|
|
|
|
|
|
|
|
|
|
[Authorize]
|
|
|
|
|
[ApiController]
|
|
|
|
|
[Route("api/[controller]")]
|
|
|
|
|
public class ChatController : ControllerBase
|
|
|
|
|
{
|
|
|
|
|
private readonly MotoresV2DbContext _context;
|
|
|
|
|
private readonly INotificationService _notificationService;
|
2026-03-12 13:52:33 -03:00
|
|
|
private readonly INotificationPreferenceService _prefService;
|
|
|
|
|
private readonly string _frontendUrl;
|
2026-01-29 13:43:44 -03:00
|
|
|
|
2026-03-12 13:52:33 -03:00
|
|
|
public ChatController(
|
|
|
|
|
MotoresV2DbContext context,
|
|
|
|
|
INotificationService notificationService,
|
|
|
|
|
INotificationPreferenceService prefService,
|
|
|
|
|
IConfiguration config)
|
2026-01-29 13:43:44 -03:00
|
|
|
{
|
|
|
|
|
_context = context;
|
|
|
|
|
_notificationService = notificationService;
|
2026-03-12 13:52:33 -03:00
|
|
|
_prefService = prefService;
|
|
|
|
|
_frontendUrl = config["AppSettings:FrontendUrl"]?.Split(',')[0].Trim() ?? "http://localhost:5173";
|
2026-01-29 13:43:44 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("send")]
|
|
|
|
|
public async Task<IActionResult> SendMessage([FromBody] ChatMessage msg)
|
|
|
|
|
{
|
|
|
|
|
// 1. Guardar Mensaje
|
|
|
|
|
msg.SentAt = DateTime.UtcNow;
|
|
|
|
|
msg.IsRead = false;
|
|
|
|
|
|
|
|
|
|
_context.ChatMessages.Add(msg);
|
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
// 2. Notificar por Email (Con manejo de errores silencioso)
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var receiver = await _context.Users.FindAsync(msg.ReceiverID);
|
|
|
|
|
var sender = await _context.Users.FindAsync(msg.SenderID);
|
|
|
|
|
|
|
|
|
|
if (receiver != null && !string.IsNullOrEmpty(receiver.Email))
|
|
|
|
|
{
|
2026-03-12 13:52:33 -03:00
|
|
|
// Solo enviar correo si la preferencia "mensajes" está habilitada
|
|
|
|
|
if (await _prefService.IsEnabledAsync(receiver.UserID, NotificationCategory.Mensajes))
|
2026-01-29 13:43:44 -03:00
|
|
|
{
|
2026-03-12 13:52:33 -03:00
|
|
|
// LÓGICA DE NOMBRE DE REMITENTE
|
|
|
|
|
string senderDisplayName;
|
2026-01-29 13:43:44 -03:00
|
|
|
|
2026-03-12 13:52:33 -03:00
|
|
|
if (sender != null && sender.UserType == 3) // 3 = ADMIN
|
|
|
|
|
{
|
|
|
|
|
// Caso: Moderador escribe a Usuario
|
|
|
|
|
senderDisplayName = "Un moderador de Motores Argentinos";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Caso: Usuario responde a Moderador
|
|
|
|
|
string name = sender?.UserName ?? "Un usuario";
|
|
|
|
|
senderDisplayName = $"El usuario {name}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generamos el token de baja para la categoría "mensajes"
|
|
|
|
|
var rawToken = await _prefService.GetOrCreateUnsubscribeTokenAsync(receiver.UserID, NotificationCategory.Mensajes);
|
|
|
|
|
var unsubscribeUrl = $"{_frontendUrl}/baja/procesar?token={Uri.EscapeDataString(rawToken)}";
|
|
|
|
|
|
|
|
|
|
await _notificationService.SendChatNotificationEmailAsync(
|
|
|
|
|
receiver.Email,
|
|
|
|
|
senderDisplayName, // Pasamos el nombre formateado
|
|
|
|
|
msg.MessageText,
|
|
|
|
|
msg.AdID,
|
|
|
|
|
unsubscribeUrl); // Se incluye URL de baja en el footer
|
|
|
|
|
}
|
2026-01-29 13:43:44 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
// Loguear error sin romper el flujo del chat
|
|
|
|
|
Console.WriteLine($"Error enviando notificación de chat: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Ok(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("inbox/{userId}")]
|
|
|
|
|
public async Task<IActionResult> GetInbox(int userId)
|
|
|
|
|
{
|
|
|
|
|
// Obtener todas las conversaciones donde el usuario es remitente o destinatario
|
2026-02-26 20:17:52 -03:00
|
|
|
// Pero filtramos los que pertenecen a avisos eliminados (StatusID != 9)
|
2026-01-29 13:43:44 -03:00
|
|
|
var messages = await _context.ChatMessages
|
|
|
|
|
.Where(m => m.SenderID == userId || m.ReceiverID == userId)
|
2026-02-26 20:17:52 -03:00
|
|
|
.Join(_context.Ads, m => m.AdID, a => a.AdID, (m, a) => new { m, a })
|
|
|
|
|
.Where(x => x.a.StatusID != (int)AdStatusEnum.Deleted)
|
|
|
|
|
.Select(x => x.m)
|
2026-01-29 13:43:44 -03:00
|
|
|
.OrderByDescending(m => m.SentAt)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
return Ok(messages);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("conversation/{adId}/{user1Id}/{user2Id}")]
|
|
|
|
|
public async Task<IActionResult> GetConversation(int adId, int user1Id, int user2Id)
|
|
|
|
|
{
|
|
|
|
|
var messages = await _context.ChatMessages
|
|
|
|
|
.Where(m => m.AdID == adId &&
|
|
|
|
|
((m.SenderID == user1Id && m.ReceiverID == user2Id) ||
|
|
|
|
|
(m.SenderID == user2Id && m.ReceiverID == user1Id)))
|
|
|
|
|
.OrderBy(m => m.SentAt)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
return Ok(messages);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("mark-read/{messageId}")]
|
|
|
|
|
public async Task<IActionResult> MarkRead(int messageId)
|
|
|
|
|
{
|
|
|
|
|
var msg = await _context.ChatMessages.FindAsync(messageId);
|
|
|
|
|
if (msg == null) return NotFound();
|
|
|
|
|
|
|
|
|
|
msg.IsRead = true;
|
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
|
return Ok();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("unread-count/{userId}")]
|
|
|
|
|
public async Task<IActionResult> GetUnreadCount(int userId)
|
|
|
|
|
{
|
|
|
|
|
// Seguridad: Asegurarse de que el usuario que consulta es el dueño de los mensajes o un admin.
|
|
|
|
|
var currentUserId = int.Parse(User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value ?? "0");
|
|
|
|
|
var isAdmin = User.IsInRole("Admin");
|
|
|
|
|
|
|
|
|
|
if (currentUserId != userId && !isAdmin)
|
|
|
|
|
{
|
|
|
|
|
return Forbid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var count = await _context.ChatMessages
|
2026-02-26 20:17:52 -03:00
|
|
|
.Join(_context.Ads, m => m.AdID, a => a.AdID, (m, a) => new { m, a })
|
|
|
|
|
.CountAsync(x => x.m.ReceiverID == userId && !x.m.IsRead && x.a.StatusID != (int)AdStatusEnum.Deleted);
|
2026-01-29 13:43:44 -03:00
|
|
|
|
|
|
|
|
return Ok(new { count });
|
|
|
|
|
}
|
|
|
|
|
}
|