using Fitness_core_BE.DTOs.Facility; using Fitness_core_BE.Helpers; using Fitness_core_BE.Infra.UoW; using Fitness_core_BE.Interfaces; using Fitness_core_BE.Interfaces.Services; using Fitness_core_BE.Models; using System.Text.Json; namespace Fitness_core_BE.Services { public class FacilityService : IFacilityService { private static readonly string[] ValidFacilityTypes = { "Gym", "Pool", "Court", "Class" }; private readonly IFacilityRepository _facilityRepository; private readonly IUnitOfWork _uow; private readonly UserRoleHelper _userRoleHelper; public FacilityService(IFacilityRepository facilityRepository, IUnitOfWork uow, UserRoleHelper userRoleHelper) { _facilityRepository = facilityRepository; _uow = uow; _userRoleHelper = userRoleHelper; } public async Task GetAllFacilities(object payload, HttpContext httpContext) { var data = DeserializePayload.Deserialize(payload); bool includeInactive = data.ContainsKey("includeInactive") && data["includeInactive"].GetBoolean(); var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext)); var facilities = await _facilityRepository.GetAllFacilities(clientId, includeInactive); return facilities.Select(MapToDto).ToList(); } public async Task CreateFacility(object payload, HttpContext httpContext) { await _uow.BeginAsync(); try { var data = DeserializePayload.Deserialize(payload); if (!data.ContainsKey("name")) throw new KeyNotFoundException("name is required"); if (!data.ContainsKey("facilityType")) throw new KeyNotFoundException("facilityType is required"); var facilityType = data["facilityType"].GetString() ?? ""; if (!ValidFacilityTypes.Contains(facilityType)) throw new ArgumentException($"facilityType must be one of: {string.Join(", ", ValidFacilityTypes)}"); var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext)); var facility = new Facility { FKClientId = clientId, Name = data["name"].GetString() ?? "", FacilityType = facilityType, Description = data.ContainsKey("description") ? data["description"].GetString() : null, DefaultCapacity = data.ContainsKey("defaultCapacity") ? data["defaultCapacity"].GetInt32() : 1 }; if (facility.DefaultCapacity < 1) throw new ArgumentException("defaultCapacity must be at least 1"); var result = await _facilityRepository.CreateFacility(facility); await _uow.CommitAsync(); return MapToDto(result); } catch { await _uow.RollbackAsync(); throw; } } public async Task UpdateFacility(object payload, HttpContext httpContext) { await _uow.BeginAsync(); try { var data = DeserializePayload.Deserialize(payload); if (!data.ContainsKey("facilityId")) throw new KeyNotFoundException("facilityId is required"); var facilityId = Guid.Parse(data["facilityId"].GetString()!); var existing = await _facilityRepository.GetFacilityById(facilityId) ?? throw new KeyNotFoundException("Facility not found"); if (data.ContainsKey("name")) existing.Name = data["name"].GetString() ?? existing.Name; if (data.ContainsKey("facilityType")) { var facilityType = data["facilityType"].GetString() ?? existing.FacilityType; if (!ValidFacilityTypes.Contains(facilityType)) throw new ArgumentException($"facilityType must be one of: {string.Join(", ", ValidFacilityTypes)}"); existing.FacilityType = facilityType; } if (data.ContainsKey("description")) existing.Description = data["description"].GetString(); if (data.ContainsKey("defaultCapacity")) { var capacity = data["defaultCapacity"].GetInt32(); if (capacity < 1) throw new ArgumentException("defaultCapacity must be at least 1"); existing.DefaultCapacity = capacity; } if (data.ContainsKey("isActive")) existing.IsActive = data["isActive"].GetBoolean(); var result = await _facilityRepository.UpdateFacility(existing); await _uow.CommitAsync(); return MapToDto(result); } catch { await _uow.RollbackAsync(); throw; } } public async Task SetFacilityStatus(object payload, HttpContext httpContext) { await _uow.BeginAsync(); try { var data = DeserializePayload.Deserialize(payload); var facilityId = Guid.Parse(data["facilityId"].GetString()!); var isActive = data["isActive"].GetBoolean(); var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext)); var facility = await _facilityRepository.SetFacilityActive(facilityId, clientId, isActive) ?? throw new KeyNotFoundException("Facility not found"); await _uow.CommitAsync(); return MapToDto(facility); } catch { await _uow.RollbackAsync(); throw; } } public async Task CreateFacilitySession(object payload, HttpContext httpContext) { await _uow.BeginAsync(); try { var data = DeserializePayload.Deserialize(payload); if (!data.ContainsKey("facilityId")) throw new KeyNotFoundException("facilityId is required"); if (!data.ContainsKey("startTime")) throw new KeyNotFoundException("startTime is required"); if (!data.ContainsKey("endTime")) throw new KeyNotFoundException("endTime is required"); var facilityId = Guid.Parse(data["facilityId"].GetString()!); var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext)); var facility = await _facilityRepository.GetFacilityById(facilityId); if (facility == null || facility.FKClientId != clientId) throw new KeyNotFoundException("Facility not found"); var startTime = DateTime.Parse(data["startTime"].GetString()!).ToUniversalTime(); var endTime = DateTime.Parse(data["endTime"].GetString()!).ToUniversalTime(); if (endTime <= startTime) throw new ArgumentException("endTime must be after startTime"); int? capacity = data.ContainsKey("capacity") && data["capacity"].ValueKind != JsonValueKind.Null ? data["capacity"].GetInt32() : null; if (capacity.HasValue && capacity.Value < 1) throw new ArgumentException("capacity must be at least 1 when set"); var session = new FacilitySession { FKFacilityId = facilityId, FKClientId = clientId, StartTime = startTime, EndTime = endTime, Capacity = capacity }; var result = await _facilityRepository.CreateFacilitySession(session); await _uow.CommitAsync(); return MapToDto(result, facility.Name, 0); } catch { await _uow.RollbackAsync(); throw; } } public async Task GetSessions(object payload, HttpContext httpContext) { var data = DeserializePayload.Deserialize(payload); Guid? facilityId = data.ContainsKey("facilityId") && data["facilityId"].ValueKind != JsonValueKind.Null ? Guid.Parse(data["facilityId"].GetString()!) : null; var fromUtc = data.ContainsKey("fromDate") && data["fromDate"].ValueKind != JsonValueKind.Null ? DateTime.Parse(data["fromDate"].GetString()!).ToUniversalTime() : DateTime.UtcNow.Date; var toUtc = data.ContainsKey("toDate") && data["toDate"].ValueKind != JsonValueKind.Null ? DateTime.Parse(data["toDate"].GetString()!).ToUniversalTime() : fromUtc.AddDays(7); var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext)); var sessions = await _facilityRepository.GetSessions(clientId, facilityId, fromUtc, toUtc); var result = new List(); foreach (var session in sessions) { var bookedCount = await _facilityRepository.GetActiveBookingCount(session.FacilitySessionId); result.Add(MapToDto(session, session.Facility?.Name, bookedCount)); } return result; } public async Task CancelFacilitySession(object payload, HttpContext httpContext) { await _uow.BeginAsync(); try { var data = DeserializePayload.Deserialize(payload); var facilitySessionId = Guid.Parse(data["facilitySessionId"].GetString()!); var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext)); var session = await _facilityRepository.CancelFacilitySession(facilitySessionId, clientId) ?? throw new KeyNotFoundException("Facility session not found"); await _uow.CommitAsync(); return MapToDto(session); } catch { await _uow.RollbackAsync(); throw; } } private static FacilityDto MapToDto(Facility facility) => new() { FacilityId = facility.FacilityId, Name = facility.Name, FacilityType = facility.FacilityType, Description = facility.Description, DefaultCapacity = facility.DefaultCapacity, IsActive = facility.IsActive, CreatedAt = facility.CreatedAt }; private static FacilitySessionDto MapToDto(FacilitySession session, string? facilityName = null, int bookedCount = 0) => new() { FacilitySessionId = session.FacilitySessionId, FacilityId = session.FKFacilityId, FacilityName = facilityName ?? session.Facility?.Name, StartTime = session.StartTime, EndTime = session.EndTime, Capacity = session.Capacity ?? session.Facility?.DefaultCapacity ?? 1, BookedCount = bookedCount, IsCancelled = session.IsCancelled }; } }