using Fitness_core_BE.DTOs.Facility; using Fitness_core_BE.Helpers; using Fitness_core_BE.Interfaces.Services; using Fitness_core_BE.Models.IOs; using Microsoft.AspNetCore.Mvc; using static Fitness_core_BE.Helpers.JwtMiddleware; using static Fitness_core_BE.Helpers.ApiResponseHelper; namespace Fitness_core_BE.Controllers { // Recurring class definitions + their occurrences -- the Classes tab's entry point. Gated on // "memberships" (same as FacilityController's own mutations) since class setup is staff/ // membership configuration, not a booking-time read. The existing GET api/client/facilities/ // sessions* ("bookings"-gated) stays separate for the Booking Dashboard calendar. [ApiController] [Route("api/client/facility-classes")] public class FacilityClassController(IClassService classService, IFacilityService facilityService) : ControllerBase { private readonly IClassService _classService = classService; private readonly IFacilityService _facilityService = facilityService; [CustomMiddleware] [TenantResolution] [RequiresPermission("memberships")] [HttpGet("")] public async Task>> GetFacilityClasses([FromQuery] bool includeInactive = false) => Response(await _classService.GetFacilityClasses(new { includeInactive }, HttpContext)); [CustomMiddleware] [TenantResolution] [RequiresPermission("memberships")] [HttpPost("")] public async Task>> CreateFacilityClass([FromBody] CreateFacilityClassRequest request) => Response(await _classService.CreateFacilityClass(new { facilityId = request.FacilityId, name = request.Name, description = request.Description, recurrenceType = request.RecurrenceType, startTimeOfDay = request.StartTimeOfDay, durationMinutes = request.DurationMinutes, weekDaysMask = request.WeekDaysMask, monthDays = request.MonthDays, seriesStartDate = request.SeriesStartDate?.ToString("o"), seriesEndDate = request.SeriesEndDate?.ToString("o"), capacity = request.Capacity, isUnlimitedCapacity = request.IsUnlimitedCapacity, customOccurrences = request.CustomOccurrences?.Select(o => new { startTime = o.StartTime.ToString("o"), durationMinutes = o.DurationMinutes }) }, HttpContext)); [CustomMiddleware] [TenantResolution] [RequiresPermission("memberships")] [HttpPut("{id:guid}")] public async Task>> UpdateFacilityClass(Guid id, [FromBody] UpdateFacilityClassRequest request) { var payload = new Dictionary { ["facilityClassId"] = id }; if (request.Name != null) payload["name"] = request.Name; if (request.Description != null) payload["description"] = request.Description; if (request.RecurrenceType != null) payload["recurrenceType"] = request.RecurrenceType; if (request.StartTimeOfDay != null) payload["startTimeOfDay"] = request.StartTimeOfDay; if (request.DurationMinutes.HasValue) payload["durationMinutes"] = request.DurationMinutes.Value; if (request.WeekDaysMask.HasValue) payload["weekDaysMask"] = request.WeekDaysMask.Value; if (request.MonthDays != null) payload["monthDays"] = request.MonthDays; if (request.SeriesStartDate.HasValue) payload["seriesStartDate"] = request.SeriesStartDate.Value.ToString("o"); if (request.SeriesEndDate.HasValue) payload["seriesEndDate"] = request.SeriesEndDate.Value.ToString("o"); if (request.Capacity.HasValue) payload["capacity"] = request.Capacity.Value; if (request.IsUnlimitedCapacity.HasValue) payload["isUnlimitedCapacity"] = request.IsUnlimitedCapacity.Value; if (request.IsActive.HasValue) payload["isActive"] = request.IsActive.Value; if (request.AddCustomOccurrences != null) payload["addCustomOccurrences"] = request.AddCustomOccurrences.Select(o => new { startTime = o.StartTime.ToString("o"), durationMinutes = o.DurationMinutes }); return Response(await _classService.UpdateFacilityClass(payload, HttpContext)); } [CustomMiddleware] [TenantResolution] [RequiresPermission("memberships")] [HttpGet("{id:guid}/occurrences")] public async Task>> GetOccurrences(Guid id, [FromQuery] bool includeCancelled = false) => Response(await _classService.GetOccurrences(new { facilityClassId = id, includeCancelled }, HttpContext)); [CustomMiddleware] [TenantResolution] [RequiresPermission("memberships")] [HttpPost("{id:guid}/occurrences/{occurrenceId:guid}/not-held")] public async Task>> MarkOccurrenceNotHeld(Guid id, Guid occurrenceId, [FromBody] MarkNotHeldRequest? request) => Response(await _facilityService.CancelFacilitySession(new { facilitySessionId = occurrenceId, reason = request?.Reason }, HttpContext)); [CustomMiddleware] [TenantResolution] [RequiresPermission("memberships")] [HttpGet("scheduling-conflicts")] public async Task>> GetSchedulingConflicts() => Response(await _classService.GetSchedulingConflicts(new { }, HttpContext)); } }