using Fitness_core_BE.Infra.UoW; using Fitness_core_BE.Interfaces; using Fitness_core_BE.Interfaces.Services; using Fitness_core_BE.Models; using Fitness_core_BE.Helpers; namespace Fitness_core_BE.Services.Chat { public class ChatService : IChatService { private const int MAX_MESSAGE_LENGTH = 100000; private readonly IChatRepo _repository; private readonly IUnitOfWork _uow; private readonly IHttpContextAccessor _httpContextAccessor; public ChatService(IChatRepo repository, IUnitOfWork uow, IHttpContextAccessor httpContextAccessor) { _repository = repository; _uow = uow; _httpContextAccessor = httpContextAccessor; } public async Task GetOrCreateChatAsync(object payload) { var httpContext = _httpContextAccessor.HttpContext; var data = DeserializePayload.Deserialize(payload); Guid currentUserId= DataExtractor.GetUserIdFromContext(httpContext); Guid otherUserId = DataExtractor.GetRequiredGuid(data, "otherUserId"); if (currentUserId == otherUserId) throw new InvalidOperationException("Cannot create chat with yourself."); await _uow.BeginAsync(); try { var existingChat = await _repository.GetChatBetweenUsersAsync(currentUserId, otherUserId); if (existingChat != null && existingChat.IsAcitve) { await _uow.CommitAsync(); return existingChat; } var chat = new Models.Chat { Id = Guid.NewGuid(), FKSenderId = currentUserId, FKReceiverId = otherUserId }; await _repository.CreateChatAsync(chat); await _uow.CommitAsync(); return chat; } catch { await _uow.RollbackAsync(); throw; } } public async Task SendMessageAsync(object payload) { var data = DeserializePayload.Deserialize(payload); Guid chatId = DataExtractor.GetRequiredGuid(data, "chatId"); Guid senderId = DataExtractor.GetRequiredGuid(data, "senderId"); string message = DataExtractor.GetRequiredString(data, "message"); string type = DataExtractor.GetRequiredString(data, "type"); if (string.IsNullOrWhiteSpace(message)) throw new ArgumentException("Message cannot be empty."); message = message.Trim(); if (message.Length > MAX_MESSAGE_LENGTH) throw new ArgumentException($"Message exceeds {MAX_MESSAGE_LENGTH} characters."); await _uow.BeginAsync(); try { var chat = await _repository.GetChatByIdAsync(chatId); if (chat == null || !chat.IsAcitve) throw new KeyNotFoundException("Chat not found or inactive."); if (senderId != chat.FKSenderId && senderId != chat.FKReceiverId) throw new UnauthorizedAccessException("User not part of this chat."); var chatMessage = new ChatMessages { Id = Guid.NewGuid(), FKChatId = chatId, FKSenderId = senderId, Message = message, Type = type, CreatedAt = DateTime.UtcNow, IsAcitve = true }; await _repository.AddMessageAsync(chatMessage); await _uow.CommitAsync(); return chatMessage; } catch { await _uow.RollbackAsync(); throw; } } public async Task GetMessagesAsync(object payload) { var data = DeserializePayload.Deserialize(payload); Guid chatId = DataExtractor.GetRequiredGuid(data, "chatId"); Guid userId = DataExtractor.GetRequiredGuid(data, "userId"); int skip = DataExtractor.GetRequiredInt(data, "skip"); int take = DataExtractor.GetRequiredInt(data, "take"); if (take <= 0 || take > 100) take = 30; if (!await CanUserAccessChatAsync(chatId, userId)) throw new UnauthorizedAccessException("User not allowed to view this chat."); return await _repository.GetMessagesAsync(chatId, skip, take); } public async Task CanUserAccessChatAsync(Guid chatId, Guid userId) { return await _repository.IsUserInChatAsync(chatId, userId); } } }