using ASTRAIN.Api.Data; using ASTRAIN.Api.Services; using ASTRAIN.Shared.Dtos; using ASTRAIN.Shared.Models; using ASTRAIN.Shared.Requests; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; namespace ASTRAIN.Api.Endpoints; /// /// Provides routine-related endpoints. /// internal static class RoutineEndpoints { /// /// Registers routine routes under the provided route group. /// /// The API route group. /// The same route group for chaining. public static RouteGroupBuilder MapRoutineEndpoints(this RouteGroupBuilder group) { group.MapGet("/users/{userId}/routines", async (string userId, AppDbContext db, IConfiguration config) => { var populateSampleData = config.GetValue("SampleData:Enabled", false); var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData); var routines = await db.Routines .Include(r => r.Exercises) .ThenInclude(re => re.Exercise) .Where(r => r.UserId == user.Id) .OrderBy(r => r.Name) .ToListAsync(); var payload = routines.Select(r => new RoutineDto( r.Id, r.Name, r.Exercises .OrderBy(re => re.Order) .Select(re => new RoutineExerciseDto(re.ExerciseId, re.Exercise?.Name ?? string.Empty, re.Order)) .ToList() )); return Results.Ok(payload); }) .WithSummary("List routines") .WithDescription("Returns all routines for the specified user."); group.MapGet("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, AppDbContext db, IConfiguration config) => { var populateSampleData = config.GetValue("SampleData:Enabled", false); var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData); var routine = await db.Routines .Include(r => r.Exercises) .ThenInclude(re => re.Exercise) .FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id); if (routine is null) { return Results.NotFound(); } var payload = new RoutineDto( routine.Id, routine.Name, routine.Exercises .OrderBy(re => re.Order) .Select(re => new RoutineExerciseDto(re.ExerciseId, re.Exercise?.Name ?? string.Empty, re.Order)) .ToList()); return Results.Ok(payload); }) .WithSummary("Get routine") .WithDescription("Returns a specific routine and its exercises."); group.MapPost("/users/{userId}/routines", async (string userId, RoutineUpsertRequest request, AppDbContext db, IConfiguration config) => { var populateSampleData = config.GetValue("SampleData:Enabled", false); var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData); if (string.IsNullOrWhiteSpace(request.Name)) { return Results.BadRequest("Name is required."); } var routine = new Routine { Name = request.Name.Trim(), UserId = user.Id }; var exercises = await db.Exercises .Where(e => e.UserId == user.Id && request.ExerciseIds.Contains(e.Id)) .ToListAsync(); routine.Exercises = request.ExerciseIds .Select((exerciseId, index) => new RoutineExercise { ExerciseId = exerciseId, Order = index }) .ToList(); db.Routines.Add(routine); await db.SaveChangesAsync(); var dto = new RoutineDto( routine.Id, routine.Name, routine.Exercises .Select((re, index) => new RoutineExerciseDto(re.ExerciseId, exercises.FirstOrDefault(e => e.Id == re.ExerciseId)?.Name ?? string.Empty, index)) .ToList()); return Results.Ok(dto); }) .WithSummary("Create routine") .WithDescription("Creates a routine and associates exercises with it."); group.MapPut("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, RoutineUpsertRequest request, AppDbContext db, IConfiguration config) => { var populateSampleData = config.GetValue("SampleData:Enabled", false); var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData); var routine = await db.Routines .Include(r => r.Exercises) .FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id); if (routine is null) { return Results.NotFound(); } if (string.IsNullOrWhiteSpace(request.Name)) { return Results.BadRequest("Name is required."); } routine.Name = request.Name.Trim(); routine.Exercises.Clear(); foreach (var exerciseId in request.ExerciseIds) { routine.Exercises.Add(new RoutineExercise { ExerciseId = exerciseId, Order = routine.Exercises.Count }); } await db.SaveChangesAsync(); var exercises = await db.Exercises .Where(e => e.UserId == user.Id && request.ExerciseIds.Contains(e.Id)) .ToListAsync(); var dto = new RoutineDto( routine.Id, routine.Name, routine.Exercises .OrderBy(re => re.Order) .Select(re => new RoutineExerciseDto(re.ExerciseId, exercises.FirstOrDefault(e => e.Id == re.ExerciseId)?.Name ?? string.Empty, re.Order)) .ToList()); return Results.Ok(dto); }) .WithSummary("Update routine") .WithDescription("Updates routine metadata and exercise ordering."); group.MapDelete("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, AppDbContext db, IConfiguration config) => { var populateSampleData = config.GetValue("SampleData:Enabled", false); var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData); var routine = await db.Routines.FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id); if (routine is null) { return Results.NotFound(); } db.Routines.Remove(routine); await db.SaveChangesAsync(); return Results.NoContent(); }) .WithSummary("Delete routine") .WithDescription("Deletes a routine and its associated data for the specified user."); return group; } }