using FluentAssertions;
using foodmarket.Application.Purchases.Commands;
using foodmarket.Application.Sales.Commands;
using foodmarket.Application.Sales.Queries;
using Xunit;

namespace foodmarket.UnitTests;

public class GetSalesReportHandlerTests
{
    private readonly GetSalesReportHandler _h = new();

    [Fact]
    public async Task Groups_by_day_sums_revenue()
    {
        var d1 = new DateTime(2026, 5, 28, 10, 0, 0, DateTimeKind.Utc);
        var d2 = new DateTime(2026, 5, 29, 11, 0, 0, DateTimeKind.Utc);
        var lines = new[]
        {
            new FlatSale(Guid.NewGuid(), d1, Guid.NewGuid(), "A", 100m, 1),
            new FlatSale(Guid.NewGuid(), d1, Guid.NewGuid(), "B", 200m, 1),
            new FlatSale(Guid.NewGuid(), d2, Guid.NewGuid(), "A", 50m, 1),
        };
        var rows = await _h.Handle(new GetSalesReportQuery(lines, "period:day"), CancellationToken.None);
        rows.Should().HaveCount(2);
        rows.First(r => r.Key == "2026-05-28").Revenue.Should().Be(300m);
        rows.First(r => r.Key == "2026-05-29").Revenue.Should().Be(50m);
    }

    [Fact]
    public async Task Groups_by_product()
    {
        var pa = Guid.NewGuid();
        var pb = Guid.NewGuid();
        var lines = new[]
        {
            new FlatSale(Guid.NewGuid(), DateTime.UtcNow, pa, "A", 100m, 1),
            new FlatSale(Guid.NewGuid(), DateTime.UtcNow, pa, "A", 50m, 1),
            new FlatSale(Guid.NewGuid(), DateTime.UtcNow, pb, "B", 300m, 2),
        };
        var rows = await _h.Handle(new GetSalesReportQuery(lines, "product"), CancellationToken.None);
        rows.Should().HaveCount(2);
        rows.First(r => r.Key == pb.ToString()).Revenue.Should().Be(300m);
        rows.First(r => r.Key == pa.ToString()).Revenue.Should().Be(150m);
    }
}

public class CreateSupplyHandlerTests
{
    [Fact]
    public async Task Computes_total_calls_writer()
    {
        var writer = new FakeWriter();
        var h = new CreateSupplyHandler(writer);
        var cmd = new CreateSupplyCommand(
            Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(),
            DateTime.UtcNow, "test",
            new[]
            {
                new CreateSupplyLine(Guid.NewGuid(), 2m, 100m),
                new CreateSupplyLine(Guid.NewGuid(), 3m, 50m),
            });
        var result = await h.Handle(cmd, CancellationToken.None);
        result.Total.Should().Be(350m);  // 2*100 + 3*50
        result.Number.Should().Be("П-2026-000001");
        writer.LastCreated.Should().NotBeNull();
        writer.LastCreated!.Value.lines.Should().HaveCount(2);
    }

    private sealed class FakeWriter : ISupplyWriter
    {
        public (Guid id, decimal total, IReadOnlyList<CreateSupplyLine> lines)? LastCreated;

        public Task<string> NextNumberAsync(int year, CancellationToken ct)
            => Task.FromResult($"П-{year}-000001");

        public Task<Guid> CreateAsync(Guid supplierId, Guid storeId, Guid currencyId,
            DateTime date, string? notes, string number,
            IReadOnlyList<CreateSupplyLine> lines, decimal total, CancellationToken ct)
        {
            var id = Guid.NewGuid();
            LastCreated = (id, total, lines);
            return Task.FromResult(id);
        }
    }
}

public class PostRetailSaleHandlerTests
{
    [Fact]
    public async Task Insufficient_payment_returns_error()
    {
        var poster = new FakePoster();
        var h = new PostRetailSaleHandler(poster);
        var result = await h.Handle(new PostRetailSaleCommand(
            Guid.NewGuid(), PaidCash: 100m, PaidCard: 0m, Total: 500m,
            Lines: new[] { new PostSaleLine(Guid.NewGuid(), 1m, 500m) }),
            CancellationToken.None);
        result.Posted.Should().BeFalse();
        result.ErrorField.Should().Be("PaidCash");
        poster.Called.Should().BeFalse();
    }

    [Fact]
    public async Task Empty_lines_returns_error()
    {
        var poster = new FakePoster();
        var h = new PostRetailSaleHandler(poster);
        var result = await h.Handle(new PostRetailSaleCommand(
            Guid.NewGuid(), 100m, 0m, 100m, Array.Empty<PostSaleLine>()),
            CancellationToken.None);
        result.Posted.Should().BeFalse();
        result.ErrorField.Should().Be("lines");
    }

    [Fact]
    public async Task Sufficient_payment_calls_poster()
    {
        var poster = new FakePoster();
        var h = new PostRetailSaleHandler(poster);
        var result = await h.Handle(new PostRetailSaleCommand(
            Guid.NewGuid(), 500m, 0m, 500m,
            new[] { new PostSaleLine(Guid.NewGuid(), 1m, 500m) }),
            CancellationToken.None);
        result.Posted.Should().BeTrue();
        poster.Called.Should().BeTrue();
    }

    private sealed class FakePoster : IRetailSalePoster
    {
        public bool Called { get; private set; }
        public Task PostAsync(Guid saleId, IReadOnlyList<PostSaleLine> lines, CancellationToken ct)
        {
            Called = true;
            return Task.CompletedTask;
        }
    }
}
