using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using foodmarket.IntegrationTests.Support;
using Xunit;

namespace foodmarket.IntegrationTests;

[Collection(ApiCollection.Name)]
public class AbcReportTests
{
    private readonly ApiFactory _factory;
    public AbcReportTests(ApiFactory factory) => _factory = factory;
    private static string RandomBarcode()
        => string.Concat(Enumerable.Range(0, 13).Select(_ => Random.Shared.Next(0, 10)));

    /// <summary>3 товара с явно разной долей выручки → классы A/B/C по Парето.</summary>
    [Fact]
    public async Task Pareto_distribution_A_B_C()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"abc-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var pBig   = await api.CreateProductAsync(refs, $"BIG-{Guid.NewGuid():N}", 100m, RandomBarcode());
        var pMed   = await api.CreateProductAsync(refs, $"MED-{Guid.NewGuid():N}", 100m, RandomBarcode());
        var pSmall = await api.CreateProductAsync(refs, $"SML-{Guid.NewGuid():N}", 100m, RandomBarcode());

        // Подкормим склад.
        foreach (var pid in new[] { pBig, pMed, pSmall })
        {
            var e = await api.Http.PostAsJsonAsync("/api/inventory/enters", new {
                date = DateTime.UtcNow, storeId = refs.StoreId, currencyId = refs.CurrencyId,
                notes = "seed", lines = new[] { new { productId = pid, quantity = 100m, unitCost = 10m } } });
            e.EnsureSuccessStatusCode();
            var eid = (await e.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
            (await api.Http.PostAsJsonAsync($"/api/inventory/enters/{eid}/post", new { })).EnsureSuccessStatusCode();
        }

        // Продажи: BIG = 800, MED = 150, SML = 50 → итого 1000.
        // A = накопительно 80% (BIG), B = 95% (+ MED = 95%), C = остаток (SML).
        async Task Sale(string pid, decimal qty, decimal price)
        {
            var resp = await api.Http.PostAsJsonAsync("/api/sales/retail", new {
                date = DateTime.UtcNow, storeId = refs.StoreId, retailPointId = (string?)null,
                customerId = (string?)null, currencyId = refs.CurrencyId,
                payment = 0, paidCash = qty * price, paidCard = 0m,
                lines = new[] { new { productId = pid, quantity = qty, unitPrice = price, discount = 0m, vatPercent = 12m } },
                notes = "sale" });
            resp.EnsureSuccessStatusCode();
            var sid = (await resp.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
            (await api.Http.PostAsJsonAsync($"/api/sales/retail/{sid}/post", new { })).EnsureSuccessStatusCode();
        }
        await Sale(pBig, 8m, 100m);
        await Sale(pMed, 15m, 10m);
        await Sale(pSmall, 5m, 10m);

        var rep = await api.GetJsonAsync("/api/reports/abc?metric=revenue");
        var arr = rep.EnumerateArray().ToList();
        arr.Should().HaveCountGreaterOrEqualTo(3);

        var big = arr.First(x => x.GetProperty("productId").GetString() == pBig);
        var med = arr.First(x => x.GetProperty("productId").GetString() == pMed);
        var small = arr.First(x => x.GetProperty("productId").GetString() == pSmall);
        big.GetProperty("abcClass").GetString().Should().Be("A");
        med.GetProperty("abcClass").GetString().Should().Be("B");
        small.GetProperty("abcClass").GetString().Should().Be("C");

        // Накопительная доля C должна быть 100%.
        small.GetProperty("cumulativeShare").GetDecimal().Should().BeApproximately(100m, 0.5m);
    }

    [Fact]
    public async Task Empty_period_returns_empty()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"abc-emp-{Guid.NewGuid():N}");
        var pastFrom = DateTime.UtcNow.AddYears(-5).ToString("o");
        var pastTo = DateTime.UtcNow.AddYears(-4).ToString("o");
        var rep = await api.GetJsonAsync($"/api/reports/abc?from={Uri.EscapeDataString(pastFrom)}&to={Uri.EscapeDataString(pastTo)}");
        rep.EnumerateArray().Should().BeEmpty();
    }

    [Fact]
    public async Task Profit_metric_orders_by_margin()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"abc-prft-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var supplierId = await api.CreateCounterpartyAsync($"S-{Guid.NewGuid():N}");
        // pA: 5 шт по 100 — высокая выручка, но низкая маржа (cost 80).
        // pB: 2 шт по 100 — низкая выручка, но высокая маржа (cost 10).
        var pA = await api.CreateProductAsync(refs, $"A-{Guid.NewGuid():N}", 100m, RandomBarcode());
        var pB = await api.CreateProductAsync(refs, $"B-{Guid.NewGuid():N}", 100m, RandomBarcode());
        async Task Supply(string pid, decimal qty, decimal cost)
        {
            var s = await api.Http.PostAsJsonAsync("/api/purchases/supplies", new {
                date = DateTime.UtcNow, supplierId, storeId = refs.StoreId, currencyId = refs.CurrencyId,
                notes = "in", lines = new[] { new { productId = pid, quantity = qty, unitPrice = cost } } });
            s.EnsureSuccessStatusCode();
            var sid = (await s.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
            (await api.Http.PostAsJsonAsync($"/api/purchases/supplies/{sid}/post", new { })).EnsureSuccessStatusCode();
        }
        await Supply(pA, 10m, 80m);
        await Supply(pB, 10m, 10m);

        async Task Sale(string pid, decimal qty, decimal price)
        {
            var resp = await api.Http.PostAsJsonAsync("/api/sales/retail", new {
                date = DateTime.UtcNow, storeId = refs.StoreId, retailPointId = (string?)null,
                customerId = (string?)null, currencyId = refs.CurrencyId,
                payment = 0, paidCash = qty * price, paidCard = 0m,
                lines = new[] { new { productId = pid, quantity = qty, unitPrice = price, discount = 0m, vatPercent = 12m } },
                notes = "sale" });
            resp.EnsureSuccessStatusCode();
            var sid = (await resp.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
            (await api.Http.PostAsJsonAsync($"/api/sales/retail/{sid}/post", new { })).EnsureSuccessStatusCode();
        }
        await Sale(pA, 5m, 100m);  // profit = 500 − 5*80 = 100
        await Sale(pB, 2m, 100m);  // profit = 200 − 2*10 = 180

        // По revenue: A=500 > B=200, A первый (rank 1).
        var byRev = await api.GetJsonAsync("/api/reports/abc?metric=revenue");
        byRev.EnumerateArray().First().GetProperty("productId").GetString().Should().Be(pA);

        // По profit: B=180 > A=100, B первый (rank 1).
        var byProf = await api.GetJsonAsync("/api/reports/abc?metric=profit");
        byProf.EnumerateArray().First().GetProperty("productId").GetString().Should().Be(pB);
    }

    [Fact]
    public async Task Tenant_isolation_abc()
    {
        var a = new ApiActor(_factory.CreateClient());
        var b = new ApiActor(_factory.CreateClient());
        await a.SignupAndLoginAsync($"abc-iso-a-{Guid.NewGuid():N}");
        await b.SignupAndLoginAsync($"abc-iso-b-{Guid.NewGuid():N}");
        var refsA = await a.LoadRefsAsync();
        var pA = await a.CreateProductAsync(refsA, $"PA-{Guid.NewGuid():N}", 100m, RandomBarcode());

        var e = await a.Http.PostAsJsonAsync("/api/inventory/enters", new {
            date = DateTime.UtcNow, storeId = refsA.StoreId, currencyId = refsA.CurrencyId,
            notes = "seed", lines = new[] { new { productId = pA, quantity = 5m, unitCost = 10m } } });
        e.EnsureSuccessStatusCode();
        var eid = (await e.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await a.Http.PostAsJsonAsync($"/api/inventory/enters/{eid}/post", new { })).EnsureSuccessStatusCode();
        var sale = await a.Http.PostAsJsonAsync("/api/sales/retail", new {
            date = DateTime.UtcNow, storeId = refsA.StoreId, retailPointId = (string?)null,
            customerId = (string?)null, currencyId = refsA.CurrencyId, payment = 0, paidCash = 100m, paidCard = 0m,
            lines = new[] { new { productId = pA, quantity = 1m, unitPrice = 100m, discount = 0m, vatPercent = 12m } },
            notes = "s" });
        sale.EnsureSuccessStatusCode();
        var sid = (await sale.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await a.Http.PostAsJsonAsync($"/api/sales/retail/{sid}/post", new { })).EnsureSuccessStatusCode();

        var repB = await b.GetJsonAsync("/api/reports/abc");
        repB.EnumerateArray().Should().NotContain(x => x.GetProperty("productId").GetString() == pA);
    }
}
