using LiteCqrs;
using TeleWave.Api.Common;
using TeleWave.Application.Admin.Users.BlockUser;
using TeleWave.Application.Admin.Users.CreateUser;
using TeleWave.Application.Admin.Users.DeleteUser;
using TeleWave.Application.Admin.Users.ListUsers;
using TeleWave.Application.Admin.Users.ResetPassword;
using TeleWave.Application.Admin.Users.UnblockUser;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
using TeleWave.Infrastructure.Identity;

namespace TeleWave.Api.Endpoints;

public static class AdminUserEndpoints
{
    public static IEndpointRouteBuilder MapAdminUserEndpoints(this IEndpointRouteBuilder app)
    {
        var admin = app.MapGroup("/api/admin/users")
            .WithTags("Admin.Users")
            .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin));

        admin.MapGet("", ListUsers).Produces<PagedList<UserSummaryDto>>();
        admin.MapPost("", CreateUser).Produces<CreatedIdResponse>(StatusCodes.Status201Created);
        admin.MapPost("/{id:guid}/block", BlockUser).Produces(StatusCodes.Status204NoContent);
        admin.MapPost("/{id:guid}/unblock", UnblockUser).Produces(StatusCodes.Status204NoContent);
        admin
            .MapPost("/{id:guid}/password", ResetPassword)
            .Produces(StatusCodes.Status204NoContent);
        admin.MapDelete("/{id:guid}", DeleteUser).Produces(StatusCodes.Status204NoContent);

        return app;
    }

    private static async Task<IResult> ListUsers(
        [AsParameters] ListUsersFilter filter,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new ListUsersQuery(
                filter.Page is > 0 ? filter.Page.Value : 1,
                filter.PageSize is > 0 ? filter.PageSize.Value : 20,
                filter.Search,
                filter.RoleId,
                filter.IsBlocked,
                filter.Sort,
                filter.Desc ?? false
            ),
            cancellationToken
        );
        return Results.Ok(result);
    }

    private static async Task<IResult> CreateUser(
        CreateUserBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new CreateUserCommand(body.UserName, body.Password, body.RoleId),
            cancellationToken
        );
        return result.IsSuccess
            ? Results.Created(
                $"/api/admin/users/{result.Value}",
                new CreatedIdResponse(result.Value)
            )
            : result.ToHttpResult();
    }

    private static async Task<IResult> BlockUser(
        Guid id,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(new BlockUserCommand(id), cancellationToken);
        return result.ToHttpResult();
    }

    private static async Task<IResult> UnblockUser(
        Guid id,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(new UnblockUserCommand(id), cancellationToken);
        return result.ToHttpResult();
    }

    private static async Task<IResult> ResetPassword(
        Guid id,
        ResetPasswordBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new ResetUserPasswordCommand(id, body.NewPassword),
            cancellationToken
        );
        return result.ToHttpResult();
    }

    private static async Task<IResult> DeleteUser(
        Guid id,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(new DeleteUserCommand(id), cancellationToken);
        return result.ToHttpResult();
    }
}

/// <summary>
/// Фильтр и страница списка пользователей. Все поля nullable намеренно: обязательный параметр в query
/// превращает запрос без него в 400 ещё до хендлера — ошибку в таком виде отладить тяжело. Умолчания
/// подставляет сам хендлер, а не объявление: при <c>AsParameters</c> обязательность определяется
/// nullable-типом, а не наличием значения по умолчанию в конструкторе.
/// </summary>
public sealed record ListUsersFilter(
    int? Page,
    int? PageSize,
    string? Search,
    Guid? RoleId,
    bool? IsBlocked,
    string? Sort,
    bool? Desc
);

public sealed record CreateUserBody(string UserName, string Password, Guid RoleId);

public sealed record ResetPasswordBody(string NewPassword);
