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 EnterPostUnpostTests
{
    private readonly ApiFactory _factory;
    public EnterPostUnpostTests(ApiFactory factory) => _factory = factory;

    private static string RandomBarcode()
        => string.Concat(Enumerable.Range(0, 13).Select(_ => Random.Shared.Next(0, 10)));

    [Fact]
    public async Task Posting_enter_raises_stock_unpost_reverts()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"enter-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var productId = await api.CreateProductAsync(refs, $"Prod-{Guid.NewGuid():N}", 200m, RandomBarcode());

        // Draft enter: 7 шт по 50 (балансовая цена).
        var draftResp = await api.Http.PostAsJsonAsync("/api/inventory/enters", new
        {
            date = DateTime.UtcNow,
            storeId = refs.StoreId,
            currencyId = refs.CurrencyId,
            notes = "начальный остаток",
            lines = new[] { new { productId, quantity = 7m, unitCost = 50m } },
        });
        draftResp.EnsureSuccessStatusCode();
        var enterId = (await draftResp.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();

        // Стартовый остаток 0.
        (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(0m);

        // Post → остаток 7. Cost не пересчитывается — оприходование не имеет «закупочной цены».
        using var post = await api.Http.PostAsJsonAsync($"/api/inventory/enters/{enterId}/post", new { });
        post.IsSuccessStatusCode.Should().BeTrue($"post вернул {(int)post.StatusCode}: {await post.Content.ReadAsStringAsync()}");
        (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(7m);

        // Unpost → остаток обратно 0.
        using var unpost = await api.Http.PostAsJsonAsync($"/api/inventory/enters/{enterId}/unpost", new { });
        unpost.IsSuccessStatusCode.Should().BeTrue($"unpost вернул {(int)unpost.StatusCode}: {await unpost.Content.ReadAsStringAsync()}");
        (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(0m);
    }

    [Fact]
    public async Task Double_post_is_rejected()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"enter-dbl-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var productId = await api.CreateProductAsync(refs, $"Prod-{Guid.NewGuid():N}", 100m, RandomBarcode());

        var draftResp = await api.Http.PostAsJsonAsync("/api/inventory/enters", new
        {
            date = DateTime.UtcNow,
            storeId = refs.StoreId,
            currencyId = refs.CurrencyId,
            notes = "dbl",
            lines = new[] { new { productId, quantity = 2m, unitCost = 30m } },
        });
        draftResp.EnsureSuccessStatusCode();
        var enterId = (await draftResp.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();

        (await api.Http.PostAsJsonAsync($"/api/inventory/enters/{enterId}/post", new { }))
            .IsSuccessStatusCode.Should().BeTrue();
        using var second = await api.Http.PostAsJsonAsync($"/api/inventory/enters/{enterId}/post", new { });
        ((int)second.StatusCode).Should().Be(409);
    }

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

        var resp = await a.Http.PostAsJsonAsync("/api/inventory/enters", new
        {
            date = DateTime.UtcNow,
            storeId = refsA.StoreId,
            currencyId = refsA.CurrencyId,
            notes = "iso",
            lines = new[] { new { productId = productA, quantity = 1m, unitCost = 10m } },
        });
        resp.EnsureSuccessStatusCode();
        var enterId = (await resp.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();

        // B список не показывает документ A.
        var bList = await b.ListAsync("/api/inventory/enters?pageSize=200");
        bList.Should().NotContain(x => x.GetProperty("id").GetString() == enterId);

        // B напрямую → 404.
        using var direct = await b.Http.GetAsync($"/api/inventory/enters/{enterId}");
        direct.StatusCode.Should().Be(System.Net.HttpStatusCode.NotFound);
    }

    [Fact]
    public async Task Unpost_blocked_when_stock_would_go_negative()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"enter-neg-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var productId = await api.CreateProductAsync(refs, $"Prod-{Guid.NewGuid():N}", 200m, RandomBarcode());

        // Оприходовали 5 шт.
        var create = await api.Http.PostAsJsonAsync("/api/inventory/enters", new
        {
            date = DateTime.UtcNow,
            storeId = refs.StoreId,
            currencyId = refs.CurrencyId,
            notes = "stock-guard",
            lines = new[] { new { productId, quantity = 5m, unitCost = 100m } },
        });
        create.EnsureSuccessStatusCode();
        var enterId = (await create.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/inventory/enters/{enterId}/post", new { }))
            .IsSuccessStatusCode.Should().BeTrue();

        // Списали 5 шт — оставшийся стартовый остаток = 0, при unpost ушёл бы в -5.
        // Делаем розничную продажу 5 единиц и проводим.
        // Сначала надо чтобы товар имел розничную цену — она задана при CreateProductAsync.
        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, // Cash
            paidCash = 1000m,
            paidCard = 0m,
            lines = new[] { new { productId, quantity = 5m, unitPrice = 200m, discount = 0m, vatPercent = 12m } },
            notes = "drain",
        });
        sale.EnsureSuccessStatusCode();
        var saleId = (await sale.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/sales/retail/{saleId}/post", new { }))
            .IsSuccessStatusCode.Should().BeTrue();

        (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(0m);

        // Unpost оприходования должен быть отбит (остаток уйдёт в минус).
        using var unpost = await api.Http.PostAsJsonAsync($"/api/inventory/enters/{enterId}/unpost", new { });
        ((int)unpost.StatusCode).Should().Be(409);
    }
}
