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

    /// <summary>Реконструкция остатков на «сейчас» совпадает с текущим Stock.</summary>
    [Fact]
    public async Task Today_matches_current_stock()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"strpt-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var p1 = await api.CreateProductAsync(refs, $"P-{Guid.NewGuid():N}", 100m, RandomBarcode());

        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 = p1, quantity = 7m, unitCost = 30m } } });
        e.EnsureSuccessStatusCode();
        var eid = (await e.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/inventory/enters/{eid}/post", new { })).EnsureSuccessStatusCode();

        var rep = await api.GetJsonAsync("/api/reports/stock");
        var rows = rep.EnumerateArray().ToList();
        var r = rows.First(x => x.GetProperty("productId").GetString() == p1);
        r.GetProperty("quantity").GetDecimal().Should().Be(7m);
        r.GetProperty("cost").GetDecimal().Should().Be(30m);
        r.GetProperty("value").GetDecimal().Should().Be(210m);
    }

    /// <summary>Запрос на дату до первой операции — пусто.</summary>
    [Fact]
    public async Task Date_before_first_movement_returns_empty()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"strpt-pre-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var p1 = await api.CreateProductAsync(refs, $"P-{Guid.NewGuid():N}", 100m, RandomBarcode());

        // Принять товар прямо сейчас.
        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 = p1, quantity = 3m, 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();

        // Запрос на год назад — ничего.
        var pastDate = DateTime.UtcNow.AddYears(-1).ToString("o");
        var rep = await api.GetJsonAsync($"/api/reports/stock?date={Uri.EscapeDataString(pastDate)}");
        var rows = rep.EnumerateArray().ToList();
        rows.Should().NotContain(x => x.GetProperty("productId").GetString() == p1);
    }

    /// <summary>Дата в будущем — отчёт = текущий остаток.</summary>
    [Fact]
    public async Task Future_date_equals_current()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"strpt-fut-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var p1 = await api.CreateProductAsync(refs, $"P-{Guid.NewGuid():N}", 100m, RandomBarcode());

        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 = p1, quantity = 5m, unitCost = 20m } } });
        e.EnsureSuccessStatusCode();
        var eid = (await e.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/inventory/enters/{eid}/post", new { })).EnsureSuccessStatusCode();

        var future = DateTime.UtcNow.AddYears(1).ToString("o");
        var rep = await api.GetJsonAsync($"/api/reports/stock?date={Uri.EscapeDataString(future)}");
        var r = rep.EnumerateArray().First(x => x.GetProperty("productId").GetString() == p1);
        r.GetProperty("quantity").GetDecimal().Should().Be(5m);
    }

    /// <summary>Снимок «вчера» — без учёта сегодняшней операции.</summary>
    [Fact]
    public async Task Date_before_operation_excludes_it()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"strpt-bef-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var p1 = await api.CreateProductAsync(refs, $"P-{Guid.NewGuid():N}", 100m, RandomBarcode());

        // Принять с future-датой (так контроллер использует входящую дату).
        var date = DateTime.UtcNow.AddDays(2);
        var e = await api.Http.PostAsJsonAsync("/api/inventory/enters", new {
            date, storeId = refs.StoreId, currencyId = refs.CurrencyId,
            notes = "future-supply", lines = new[] { new { productId = p1, quantity = 4m, 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();

        // На сейчас (раньше OccurredAt = date) — товара ещё нет.
        var now = DateTime.UtcNow.ToString("o");
        var rep = await api.GetJsonAsync($"/api/reports/stock?date={Uri.EscapeDataString(now)}");
        var rows = rep.EnumerateArray().ToList();
        rows.Should().NotContain(x => x.GetProperty("productId").GetString() == p1);

        // На дату ПОСЛЕ операции — товар появился.
        var after = date.AddDays(1).ToString("o");
        var rep2 = await api.GetJsonAsync($"/api/reports/stock?date={Uri.EscapeDataString(after)}");
        var r = rep2.EnumerateArray().First(x => x.GetProperty("productId").GetString() == p1);
        r.GetProperty("quantity").GetDecimal().Should().Be(4m);
    }

    [Fact]
    public async Task Tenant_isolation_stock_report()
    {
        var a = new ApiActor(_factory.CreateClient());
        var b = new ApiActor(_factory.CreateClient());
        await a.SignupAndLoginAsync($"strpt-iso-a-{Guid.NewGuid():N}");
        await b.SignupAndLoginAsync($"strpt-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 = 1m, unitCost = 5m } } });
        e.EnsureSuccessStatusCode();
        var eid = (await e.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await a.Http.PostAsJsonAsync($"/api/inventory/enters/{eid}/post", new { })).EnsureSuccessStatusCode();

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