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 ProfitReportTests
{
    private readonly ApiFactory _factory;
    public ProfitReportTests(ApiFactory factory) => _factory = factory;
    private static string RandomBarcode()
        => string.Concat(Enumerable.Range(0, 13).Select(_ => Random.Shared.Next(0, 10)));

    /// <summary>Купили 10 шт по 50 (cost после Supply post = 50), продали 4 шт по
    /// 100. Прибыль = 400 − 200 = 200, маржа = 50%.</summary>
    [Fact]
    public async Task Profit_simple_supply_sale()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"prft-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var supplierId = await api.CreateCounterpartyAsync($"S-{Guid.NewGuid():N}");
        var p1 = await api.CreateProductAsync(refs, $"P-{Guid.NewGuid():N}", 100m, RandomBarcode());

        // Приёмка: 10 шт по 50.
        var sup = await api.Http.PostAsJsonAsync("/api/purchases/supplies", new {
            date = DateTime.UtcNow, supplierId, storeId = refs.StoreId, currencyId = refs.CurrencyId,
            notes = "init", lines = new[] { new { productId = p1, quantity = 10m, unitPrice = 50m } } });
        sup.EnsureSuccessStatusCode();
        var supId = (await sup.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/purchases/supplies/{supId}/post", new { })).EnsureSuccessStatusCode();

        // Продажа: 4 шт по 100.
        var sale = 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 = 400m, paidCard = 0m,
            lines = new[] { new { productId = p1, quantity = 4m, unitPrice = 100m, discount = 0m, vatPercent = 12m } },
            notes = "sale" });
        sale.EnsureSuccessStatusCode();
        var sid = (await sale.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/sales/retail/{sid}/post", new { })).EnsureSuccessStatusCode();

        var rep = await api.GetJsonAsync("/api/reports/profit?groupBy=product");
        var r = rep.EnumerateArray().First(x => x.GetProperty("key").GetString() == p1);
        r.GetProperty("revenue").GetDecimal().Should().Be(400m);
        r.GetProperty("cost").GetDecimal().Should().Be(200m);
        r.GetProperty("profit").GetDecimal().Should().Be(200m);
        r.GetProperty("marginPercent").GetDecimal().Should().Be(50m);
    }

    /// <summary>Защита от деления на ноль: при нулевой выручке (продажи=0) margin=0,
    /// а не NaN/Infinity. Проверяем что хотя бы пустой набор возвращается без 500.</summary>
    [Fact]
    public async Task Empty_period_returns_no_rows_no_division_by_zero()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"prft-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/profit?groupBy=product&from={Uri.EscapeDataString(pastFrom)}&to={Uri.EscapeDataString(pastTo)}");
        rep.EnumerateArray().Should().BeEmpty();
    }

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

        var sup = await a.Http.PostAsJsonAsync("/api/purchases/supplies", new {
            date = DateTime.UtcNow, supplierId = supplierA, storeId = refsA.StoreId, currencyId = refsA.CurrencyId,
            notes = "init", lines = new[] { new { productId = pA, quantity = 5m, unitPrice = 20m } } });
        sup.EnsureSuccessStatusCode();
        var supId = (await sup.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await a.Http.PostAsJsonAsync($"/api/purchases/supplies/{supId}/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 = "sale" });
        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/profit?groupBy=product");
        repB.EnumerateArray().Should().NotContain(x => x.GetProperty("key").GetString() == pA);
    }
}
