using LiteCqrs;
using TeleWave.Api.Common;
using TeleWave.Application.Programming.Templates.Junctions;
using TeleWave.Domain.Programming;
using TeleWave.Infrastructure.Identity;

namespace TeleWave.Api.Endpoints;

/// <summary>
/// Шаблоны стыков канала: что играет между программами. Как и правка сетки, эфира не двигают —
/// помечают шаблон канала изменённым, а хвост пересобирается применением.
/// </summary>
public static class JunctionEndpoints
{
    public static IEndpointRouteBuilder MapJunctionEndpoints(this IEndpointRouteBuilder app)
    {
        var admin = app.MapGroup("/api/admin")
            .WithTags("Admin.Junctions")
            .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin));

        admin
            .MapGet("/channels/{channelId:guid}/junctions", List)
            .Produces<IReadOnlyList<JunctionTemplateDto>>();
        admin
            .MapPost("/channels/{channelId:guid}/junctions", Create)
            .Produces<CreatedIdResponse>(StatusCodes.Status201Created);
        admin
            .MapPut("/junctions/{junctionId:guid}", Rename)
            .Produces(StatusCodes.Status204NoContent);
        admin
            .MapDelete("/junctions/{junctionId:guid}", Delete)
            .Produces(StatusCodes.Status204NoContent);

        admin
            .MapPost("/junctions/{junctionId:guid}/elements", AddElement)
            .Produces<CreatedIdResponse>(StatusCodes.Status201Created);
        admin
            .MapPut("/junctions/{junctionId:guid}/elements/{elementId:guid}", UpdateElement)
            .Produces(StatusCodes.Status204NoContent);
        admin
            .MapDelete("/junctions/{junctionId:guid}/elements/{elementId:guid}", RemoveElement)
            .Produces(StatusCodes.Status204NoContent);
        admin
            .MapPut("/junctions/{junctionId:guid}/order", Reorder)
            .Produces(StatusCodes.Status204NoContent);

        return app;
    }

    private static async Task<IResult> List(
        Guid channelId,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(new ListJunctionsQuery(channelId), cancellationToken);
        return Results.Ok(result);
    }

    private static async Task<IResult> Create(
        Guid channelId,
        JunctionNameBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new CreateJunctionCommand(channelId, body.Name),
            cancellationToken
        );
        return result.IsSuccess
            ? Results.Created(
                $"/api/admin/junctions/{result.Value}",
                new CreatedIdResponse(result.Value)
            )
            : result.ToHttpResult();
    }

    private static async Task<IResult> Rename(
        Guid junctionId,
        JunctionNameBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new RenameJunctionCommand(junctionId, body.Name),
            cancellationToken
        );
        return result.ToHttpResult();
    }

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

    private static async Task<IResult> AddElement(
        Guid junctionId,
        JunctionElementKindBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new AddJunctionElementCommand(junctionId, body.Kind),
            cancellationToken
        );
        return result.IsSuccess
            ? Results.Created(
                $"/api/admin/junctions/{junctionId}",
                new CreatedIdResponse(result.Value)
            )
            : result.ToHttpResult();
    }

    private static async Task<IResult> UpdateElement(
        Guid junctionId,
        Guid elementId,
        JunctionElementInput input,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new UpdateJunctionElementCommand(junctionId, elementId, input),
            cancellationToken
        );
        return result.ToHttpResult();
    }

    private static async Task<IResult> RemoveElement(
        Guid junctionId,
        Guid elementId,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new RemoveJunctionElementCommand(junctionId, elementId),
            cancellationToken
        );
        return result.ToHttpResult();
    }

    private static async Task<IResult> Reorder(
        Guid junctionId,
        ReorderJunctionBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new ReorderJunctionCommand(junctionId, body.ElementIdsInOrder),
            cancellationToken
        );
        return result.ToHttpResult();
    }
}

public sealed record JunctionNameBody(string Name);

public sealed record JunctionElementKindBody(JunctionElementKind Kind);

public sealed record ReorderJunctionBody(IReadOnlyList<Guid> ElementIdsInOrder);
