using System.ComponentModel.DataAnnotations;
using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Controllers.Api.Requests;
using ErsatzTV.Core;
using ErsatzTV.Core.Api.SmartCollections;
using ErsatzTV.Extensions;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace ErsatzTV.Controllers.Api;

[ApiController]
public class SmartCollectionController(IMediator mediator) : ControllerBase
{
    [HttpGet("/api/v1/smart-collections")]
    [Tags("Smart Collections")]
    [EndpointSummary("Get all smart collections")]
    [EndpointGroupName("general")]
    [ProducesResponseType(typeof(List<SmartCollectionResponseModel>), StatusCodes.Status200OK)]
    public async Task<List<SmartCollectionResponseModel>> GetAll(CancellationToken cancellationToken) =>
        await mediator.Send(new GetAllSmartCollectionsForApi(), cancellationToken);

    [HttpGet("/api/v1/smart-collections/{id:int}", Name = "GetSmartCollectionById")]
    [Tags("Smart Collections")]
    [EndpointSummary("Get a smart collection by id")]
    [EndpointGroupName("general")]
    [ProducesResponseType(typeof(SmartCollectionResponseModel), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetById(int id, CancellationToken cancellationToken)
    {
        Option<SmartCollectionViewModel> result =
            await mediator.Send(new GetSmartCollectionById(id), cancellationToken);
        return result.Map(ProjectToResponseModel).ToGetResult();
    }

    [HttpPost("/api/v1/smart-collections")]
    [Tags("Smart Collections")]
    [EndpointSummary("Create a smart collection")]
    [EndpointGroupName("general")]
    [ProducesResponseType(typeof(SmartCollectionResponseModel), StatusCodes.Status201Created)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
    public async Task<IActionResult> Create(
        [Required][FromBody] CreateSmartCollectionRequest request,
        CancellationToken cancellationToken)
    {
        Either<BaseError, SmartCollectionViewModel> result =
            await mediator.Send(request.ToCommand(), cancellationToken);
        return result.ToCreatedResult(vm => $"/api/v1/smart-collections/{vm.Id}", ProjectToResponseModel);
    }

    [HttpPut("/api/v1/smart-collections/{id:int}")]
    [Tags("Smart Collections")]
    [EndpointSummary("Update a smart collection")]
    [EndpointGroupName("general")]
    [ProducesResponseType(typeof(SmartCollectionResponseModel), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
    public async Task<IActionResult> Update(
        int id,
        [Required][FromBody] UpdateSmartCollectionRequest request,
        CancellationToken cancellationToken)
    {
        Either<BaseError, UpdateSmartCollectionResult> result =
            await mediator.Send(request.ToCommand(id), cancellationToken);
        return await result.Match(
            Left: error => Task.FromResult(error.ToErrorResult()),
            Right: async _ =>
            {
                Option<SmartCollectionViewModel> smartCollection =
                    await mediator.Send(new GetSmartCollectionById(id), cancellationToken);
                return smartCollection.Match(
                    Some: vm => (IActionResult)new OkObjectResult(ProjectToResponseModel(vm)),
                    None: () => ApiResults.NotFoundProblem());
            });
    }

    [HttpDelete("/api/v1/smart-collections/{id:int}")]
    [Tags("Smart Collections")]
    [EndpointSummary("Delete a smart collection")]
    [EndpointGroupName("general")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
    public async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
    {
        Either<BaseError, Unit> result = await mediator.Send(new DeleteSmartCollection(id), cancellationToken);
        return result.ToDeletedResult();
    }

    private static SmartCollectionResponseModel ProjectToResponseModel(SmartCollectionViewModel vm) =>
        new(vm.Id, vm.Name, vm.Query);
}
