using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using WorkClub.Api.Services;
using WorkClub.Application.Shifts.DTOs;

namespace WorkClub.Api.Endpoints.Shifts;

public static class ShiftEndpoints
{
    public static void MapShiftEndpoints(this IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/api/shifts");

        group.MapGet("", GetShifts)
            .RequireAuthorization("RequireMember")
            .WithName("GetShifts");

        group.MapGet("{id:guid}", GetShift)
            .RequireAuthorization("RequireMember")
            .WithName("GetShift");

        group.MapPost("", CreateShift)
            .RequireAuthorization("RequireManager")
            .WithName("CreateShift");

        group.MapPut("{id:guid}", UpdateShift)
            .RequireAuthorization("RequireManager")
            .WithName("UpdateShift");

        group.MapDelete("{id:guid}", DeleteShift)
            .RequireAuthorization("RequireManager")
            .WithName("DeleteShift");

        group.MapPost("{id:guid}/signup", SignUpForShift)
            .RequireAuthorization("RequireMember")
            .WithName("SignUpForShift");

        group.MapDelete("{id:guid}/signup", CancelSignup)
            .RequireAuthorization("RequireMember")
            .WithName("CancelSignup");
    }

    private static async Task<Ok<ShiftListDto>> GetShifts(
        ShiftService shiftService,
        HttpContext httpContext,
        [FromQuery] DateTimeOffset? from = null,
        [FromQuery] DateTimeOffset? to = null,
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20)
    {
        var externalUserId = httpContext.User.FindFirst("sub")?.Value;
        var result = await shiftService.GetShiftsAsync(from, to, page, pageSize, externalUserId);
        return TypedResults.Ok(result);
    }

    private static async Task<Results<Ok<ShiftDetailDto>, NotFound>> GetShift(
        Guid id,
        ShiftService shiftService,
        HttpContext httpContext)
    {
        var externalUserId = httpContext.User.FindFirst("sub")?.Value;
        var result = await shiftService.GetShiftByIdAsync(id, externalUserId);

        if (result == null)
            return TypedResults.NotFound();

        return TypedResults.Ok(result);
    }

    private static async Task<Results<Created<ShiftDetailDto>, BadRequest<string>>> CreateShift(
        CreateShiftRequest request,
        ShiftService shiftService,
        HttpContext httpContext)
    {
        var userIdClaim = httpContext.User.FindFirst("sub")?.Value;
        if (string.IsNullOrEmpty(userIdClaim) || !Guid.TryParse(userIdClaim, out var createdById))
        {
            return TypedResults.BadRequest("Invalid user ID");
        }

        var (shift, error) = await shiftService.CreateShiftAsync(request, createdById);

        if (error != null || shift == null)
            return TypedResults.BadRequest(error ?? "Failed to create shift");

        return TypedResults.Created($"/api/shifts/{shift.Id}", shift);
    }

    private static async Task<Results<Ok<ShiftDetailDto>, NotFound, Conflict<string>>> UpdateShift(
        Guid id,
        UpdateShiftRequest request,
        ShiftService shiftService,
        HttpContext httpContext)
    {
        var externalUserId = httpContext.User.FindFirst("sub")?.Value;
        var (shift, error, isConflict) = await shiftService.UpdateShiftAsync(id, request, externalUserId);

        if (error != null)
        {
            if (error == "Shift not found")
                return TypedResults.NotFound();

            if (isConflict)
                return TypedResults.Conflict(error);
        }

        return TypedResults.Ok(shift!);
    }

    private static async Task<Results<NoContent, NotFound>> DeleteShift(
        Guid id,
        ShiftService shiftService)
    {
        var deleted = await shiftService.DeleteShiftAsync(id);

        if (!deleted)
            return TypedResults.NotFound();

        return TypedResults.NoContent();
    }

    private static async Task<Results<Ok, NotFound, UnprocessableEntity<string>, Conflict<string>>> SignUpForShift(
        Guid id,
        ShiftService shiftService,
        HttpContext httpContext)
    {
        var externalUserId = httpContext.User.FindFirst("sub")?.Value;
        if (string.IsNullOrEmpty(externalUserId))
        {
            return TypedResults.UnprocessableEntity("Invalid user ID");
        }

        var (success, error, isConflict) = await shiftService.SignUpForShiftAsync(id, externalUserId);

        if (!success)
        {
            if (error == "Shift not found" || error == "Member not found")
                return TypedResults.NotFound();

            if (error == "Cannot sign up for past shifts")
                return TypedResults.UnprocessableEntity(error);

            if (isConflict)
                return TypedResults.Conflict(error!);
        }

        return TypedResults.Ok();
    }

    private static async Task<Results<Ok, NotFound, UnprocessableEntity<string>>> CancelSignup(
        Guid id,
        ShiftService shiftService,
        HttpContext httpContext)
    {
        var externalUserId = httpContext.User.FindFirst("sub")?.Value;
        if (string.IsNullOrEmpty(externalUserId))
        {
            return TypedResults.UnprocessableEntity("Invalid user ID");
        }

        var (success, error) = await shiftService.CancelSignupAsync(id, externalUserId);

        if (!success)
        {
            if (error == "Sign-up not found" || error == "Member not found")
                return TypedResults.NotFound();

            return TypedResults.UnprocessableEntity(error!);
        }

        return TypedResults.Ok();
    }
}
