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;

/// <summary>
/// Provides routine-related endpoints.
/// </summary>
internal static class RoutineEndpoints
{
    /// <summary>
    /// Registers routine routes under the provided route group.
    /// </summary>
    /// <param name="group">The API route group.</param>
    /// <returns>The same route group for chaining.</returns>
    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;
    }
}
