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/junctions")
            .WithTags("Admin.Junctions")
            .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin));

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

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

        return app;
    }

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

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

    private static async Task<IResult> Update(
        Guid junctionId,
        UpdateJunctionBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new UpdateJunctionCommand(junctionId, body.Name, body.MaxTotalSeconds),
            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.Order),
            cancellationToken
        );
        return result.ToHttpResult();
    }
}

public sealed record JunctionNameBody(string Name);

public sealed record UpdateJunctionBody(string Name, int? MaxTotalSeconds);

public sealed record JunctionElementKindBody(JunctionElementKind Kind);

/// <summary>Порядок врезок вместе с их развилками — перетаскивание меняет и то, и другое разом.</summary>
public sealed record ReorderJunctionBody(IReadOnlyList<JunctionElementOrder> Order);
