using FluentAssertions;
using NSubstitute;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Common;
using SIGCM2.Application.Pricing.ChargeableChars;
using SIGCM2.Application.Pricing.ChargeableChars.List;
using SIGCM2.Domain.Pricing.ChargeableChars;

namespace SIGCM2.Application.Tests.Pricing.ChargeableChars;

/// <summary>
/// PRC-001 — ListChargeableCharConfigQueryHandler tests.
/// Covers: happy path with items, empty page, projection to DTO, pagination metadata.
/// </summary>
public class ListChargeableCharConfigHandlerTests
{
    private readonly IChargeableCharConfigRepository _repo = Substitute.For<IChargeableCharConfigRepository>();
    private readonly ListChargeableCharConfigQueryHandler _handler;

    private static readonly DateOnly Today = new(2026, 4, 20);

    private static ChargeableCharConfig MakeConfig(long id, string symbol, decimal price) =>
        ChargeableCharConfig.Rehydrate(id, null, symbol, ChargeableCharCategories.Currency, price, Today, null, true);

    public ListChargeableCharConfigHandlerTests()
    {
        _handler = new ListChargeableCharConfigQueryHandler(_repo);
    }

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

    [Fact]
    public async Task Handle_WithItems_ReturnsPagedDtos()
    {
        var items = new List<ChargeableCharConfig>
        {
            MakeConfig(1, "$", 1.0m),
            MakeConfig(2, "%", 0.5m),
        };

        _repo.ListAsync(null, true, 0, 20, Arg.Any<CancellationToken>())
            .Returns(items);
        _repo.CountAsync(null, true, Arg.Any<CancellationToken>())
            .Returns(2);

        var query = new ListChargeableCharConfigQuery(ProductTypeId: null, ActiveOnly: true, Page: 1, PageSize: 20);
        var result = await _handler.Handle(query);

        result.Items.Should().HaveCount(2);
        result.Total.Should().Be(2);
        result.Page.Should().Be(1);
        result.PageSize.Should().Be(20);
    }

    [Fact]
    public async Task Handle_WithItems_ProjectsToDto()
    {
        var items = new List<ChargeableCharConfig> { MakeConfig(5, "$", 1.5m) };

        _repo.ListAsync(Arg.Any<long?>(), Arg.Any<bool>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
            .Returns(items);
        _repo.CountAsync(Arg.Any<long?>(), Arg.Any<bool>(), Arg.Any<CancellationToken>())
            .Returns(1);

        var query = new ListChargeableCharConfigQuery(null, true, 1, 20);
        var result = await _handler.Handle(query);

        var dto = result.Items[0];
        dto.Id.Should().Be(5);
        dto.Symbol.Should().Be("$");
        dto.PricePerUnit.Should().Be(1.5m);
        dto.ValidFrom.Should().Be(Today);
        dto.IsActive.Should().BeTrue();
    }

    [Fact]
    public async Task Handle_EmptyPage_ReturnsEmptyList()
    {
        _repo.ListAsync(Arg.Any<long?>(), Arg.Any<bool>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
            .Returns(new List<ChargeableCharConfig>());
        _repo.CountAsync(Arg.Any<long?>(), Arg.Any<bool>(), Arg.Any<CancellationToken>())
            .Returns(0);

        var query = new ListChargeableCharConfigQuery(null, true, 1, 20);
        var result = await _handler.Handle(query);

        result.Items.Should().BeEmpty();
        result.Total.Should().Be(0);
    }

    [Fact]
    public async Task Handle_SkipIsComputed_FromPageAndPageSize()
    {
        // Page 3, PageSize 10 → skip = 20
        _repo.ListAsync(null, false, 20, 10, Arg.Any<CancellationToken>())
            .Returns(new List<ChargeableCharConfig>());
        _repo.CountAsync(null, false, Arg.Any<CancellationToken>())
            .Returns(0);

        var query = new ListChargeableCharConfigQuery(null, false, 3, 10);
        await _handler.Handle(query);

        await _repo.Received(1).ListAsync(null, false, 20, 10, Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_FiltersByProductTypeId_WhenProvided()
    {
        _repo.ListAsync(7L, true, 0, 20, Arg.Any<CancellationToken>())
            .Returns(new List<ChargeableCharConfig>());
        _repo.CountAsync(7L, true, Arg.Any<CancellationToken>())
            .Returns(0);

        var query = new ListChargeableCharConfigQuery(7L, true, 1, 20);
        await _handler.Handle(query);

        await _repo.Received(1).ListAsync(7L, true, 0, 20, Arg.Any<CancellationToken>());
    }
}
