using FluentAssertions;
using NSubstitute;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Common;
using SIGCM2.Application.ProductTypes.List;
using SIGCM2.Domain.Entities;

namespace SIGCM2.Application.Tests.ProductTypes.List;

public class ListProductTypesQueryHandlerTests
{
    private readonly IProductTypeRepository _repo = Substitute.For<IProductTypeRepository>();
    private readonly ListProductTypesQueryHandler _handler;

    public ListProductTypesQueryHandlerTests()
    {
        _handler = new ListProductTypesQueryHandler(_repo);
    }

    private static ProductType MakeProductType(int id, string nombre, bool isActive = true) =>
        new(id, nombre, false, false, false, false, false, null, null, null, null,
            isActive, DateTime.UtcNow, null);

    private static PagedResult<ProductType> PagedOf(List<ProductType> items, int page = 1, int pageSize = 20, int? total = null) =>
        new(items, page, pageSize, total ?? items.Count);

    // ── Happy path ───────────────────────────────────────────────────────────

    [Fact]
    public async Task Handle_DefaultQuery_Returns20Active()
    {
        var items = Enumerable.Range(1, 5).Select(i => MakeProductType(i, $"Tipo{i}")).ToList();
        _repo.GetPagedAsync(Arg.Any<ProductTypesQuery>(), Arg.Any<CancellationToken>())
             .Returns(PagedOf(items));

        var result = await _handler.Handle(new ListProductTypesQuery());

        result.Items.Should().HaveCount(5);
    }

    [Fact]
    public async Task Handle_PageLessThan1_ClampsToOne()
    {
        _repo.GetPagedAsync(Arg.Any<ProductTypesQuery>(), Arg.Any<CancellationToken>())
             .Returns(PagedOf([]));

        await _handler.Handle(new ListProductTypesQuery(Page: 0));

        await _repo.Received(1).GetPagedAsync(
            Arg.Is<ProductTypesQuery>(q => q.Page == 1),
            Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_PageSizeOver100_ClampsTo100()
    {
        _repo.GetPagedAsync(Arg.Any<ProductTypesQuery>(), Arg.Any<CancellationToken>())
             .Returns(PagedOf([]));

        await _handler.Handle(new ListProductTypesQuery(PageSize: 200));

        await _repo.Received(1).GetPagedAsync(
            Arg.Is<ProductTypesQuery>(q => q.PageSize == 100),
            Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_ActivoFalse_ReturnsInactives()
    {
        var inactive = new List<ProductType> { MakeProductType(99, "Inactivo", false) };
        _repo.GetPagedAsync(Arg.Any<ProductTypesQuery>(), Arg.Any<CancellationToken>())
             .Returns(PagedOf(inactive));

        var result = await _handler.Handle(new ListProductTypesQuery(Activo: false));

        await _repo.Received(1).GetPagedAsync(
            Arg.Is<ProductTypesQuery>(q => q.Activo == false),
            Arg.Any<CancellationToken>());
        result.Items.Should().HaveCount(1);
    }

    [Fact]
    public async Task Handle_SearchFilter_PassesThroughToRepo()
    {
        _repo.GetPagedAsync(Arg.Any<ProductTypesQuery>(), Arg.Any<CancellationToken>())
             .Returns(PagedOf([]));

        await _handler.Handle(new ListProductTypesQuery(Search: "clasif"));

        await _repo.Received(1).GetPagedAsync(
            Arg.Is<ProductTypesQuery>(q => q.Search == "clasif"),
            Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_PagedResult_ReturnsItemsAndTotal()
    {
        var items = Enumerable.Range(1, 5).Select(i => MakeProductType(i, $"Tipo{i}")).ToList();
        _repo.GetPagedAsync(Arg.Any<ProductTypesQuery>(), Arg.Any<CancellationToken>())
             .Returns(new PagedResult<ProductType>(items, 2, 5, 15));

        var result = await _handler.Handle(new ListProductTypesQuery(Page: 2, PageSize: 5));

        result.Total.Should().Be(15);
        result.Page.Should().Be(2);
        result.PageSize.Should().Be(5);
        result.Items.Should().HaveCount(5);
    }
}
