using LiteCqrs;
using TeleWave.Api.Common;
using TeleWave.Application.Library.Collections;
using TeleWave.Application.Library.Collections.AddCollectionShow;
using TeleWave.Application.Library.Collections.AddShowsToCollection;
using TeleWave.Application.Library.Collections.CreateCollection;
using TeleWave.Application.Library.Collections.DeleteCollection;
using TeleWave.Application.Library.Collections.FillCollectionPosters;
using TeleWave.Application.Library.Collections.GetCollection;
using TeleWave.Application.Library.Collections.ListCollections;
using TeleWave.Application.Library.Collections.RemoveCollectionShow;
using TeleWave.Application.Library.Collections.ReorderCollection;
using TeleWave.Application.Library.Collections.SetCollectionPoster;
using TeleWave.Application.Library.Collections.Suggest;
using TeleWave.Application.Library.Collections.UpdateCollection;
using TeleWave.Infrastructure.Identity;

namespace TeleWave.Api.Endpoints;

public static class CollectionEndpoints
{
    public static IEndpointRouteBuilder MapCollectionEndpoints(this IEndpointRouteBuilder app)
    {
        var admin = app.MapGroup("/api/admin/collections")
            .WithTags("Admin.Collections")
            .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin));

        admin.MapGet("", ListCollections).Produces<IReadOnlyList<CollectionSummaryDto>>();
        admin.MapGet("/{id:guid}", GetCollection).Produces<CollectionDto>();
        admin
            .MapPost("", CreateCollection)
            .Produces<CreatedIdResponse>(StatusCodes.Status201Created);
        admin.MapPut("/{id:guid}", UpdateCollection).Produces(StatusCodes.Status204NoContent);
        admin.MapDelete("/{id:guid}", DeleteCollection).Produces(StatusCodes.Status204NoContent);
        admin.MapPost("/{id:guid}/shows", AddShow).Produces(StatusCodes.Status204NoContent);
        admin
            .MapDelete("/{id:guid}/shows/{showId:guid}", RemoveShow)
            .Produces(StatusCodes.Status204NoContent);
        admin.MapPut("/{id:guid}/order", Reorder).Produces(StatusCodes.Status204NoContent);
        admin.MapPut("/{id:guid}/poster-image", SetPoster).Produces(StatusCodes.Status204NoContent);

        // Сборка франшиз: массовое добавление отмеченных шоу и предложения по библиотеке.
        admin.MapPost("/bulk/shows", AddShows).Produces<AddShowsToCollectionResultDto>();
        // Постеры по частям: без списка — всем коллекциям, у которых постера ещё нет.
        admin.MapPost("/bulk/posters", FillPosters).Produces<int>();
        admin
            .MapGet("/suggestions", Suggestions)
            .Produces<IReadOnlyList<CollectionSuggestionDto>>();
        admin
            .MapPost("/suggestions", CreateFromSuggestion)
            .Produces<CreatedIdResponse>(StatusCodes.Status201Created);
        // Дополнение уже собранной франшизы: коллекция определяется тем же ключом предложения.
        admin.MapPost("/suggestions/add", AddFromSuggestion).Produces<int>();

        return app;
    }

    private static async Task<IResult> FillPosters(
        FillCollectionPostersBody? body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new FillCollectionPostersCommand(body?.CollectionIds),
            cancellationToken
        );
        return result.ToHttpResult();
    }

    private static async Task<IResult> AddShows(
        AddShowsToCollectionBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new AddShowsToCollectionCommand(
                body.ShowIds,
                body.CollectionId,
                body.NewCollectionName
            ),
            cancellationToken
        );
        return result.ToHttpResult();
    }

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

    private static async Task<IResult> CreateFromSuggestion(
        CreateCollectionFromSuggestionBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new CreateCollectionFromSuggestionCommand(body.Key),
            cancellationToken
        );
        return result.IsSuccess
            ? Results.Created(
                $"/api/admin/collections/{result.Value}",
                new CreatedIdResponse(result.Value)
            )
            : result.ToHttpResult();
    }

    private static async Task<IResult> AddFromSuggestion(
        CreateCollectionFromSuggestionBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(new AddSuggestedShowsCommand(body.Key), cancellationToken);
        return result.ToHttpResult();
    }

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

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

    private static async Task<IResult> CreateCollection(
        CreateCollectionCommand command,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(command, cancellationToken);
        return result.IsSuccess
            ? Results.Created(
                $"/api/admin/collections/{result.Value}",
                new CreatedIdResponse(result.Value)
            )
            : result.ToHttpResult();
    }

    private static async Task<IResult> UpdateCollection(
        Guid id,
        UpdateCollectionBody body,
        ISender sender,
        CancellationToken cancellationToken
    )
    {
        var result = await sender.Send(
            new UpdateCollectionCommand(id, body.Name, body.Description),
            cancellationToken
        );
        return result.ToHttpResult();
    }

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

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

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

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

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

public sealed record UpdateCollectionBody(string Name, string? Description);

public sealed record CollectionShowBody(Guid ShowId);

public sealed record ReorderCollectionBody(IReadOnlyList<Guid> ShowIdsInOrder);

public sealed record CollectionPosterBody(Guid? ImageId);

/// <summary>Куда складывать: существующая коллекция либо новая по имени.</summary>
public sealed record AddShowsToCollectionBody(
    IReadOnlyList<Guid> ShowIds,
    Guid? CollectionId = null,
    string? NewCollectionName = null
);

public sealed record CreateCollectionFromSuggestionBody(string Key);

public sealed record FillCollectionPostersBody(IReadOnlyList<Guid>? CollectionIds);
