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

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

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

        // Принять 10 шт.
        var supply = await api.Http.PostAsJsonAsync("/api/purchases/supplies", new
        {
            date = DateTime.UtcNow, supplierId, storeId = refs.StoreId, currencyId = refs.CurrencyId,
            notes = "init", lines = new[] { new { productId, quantity = 10m, unitPrice = 70m } },
        });
        supply.EnsureSuccessStatusCode();
        var supplyId = (await supply.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/purchases/supplies/{supplyId}/post", new { })).EnsureSuccessStatusCode();
        (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(10m);

        // Вернуть 3 шт поставщику (с reference на приёмку).
        var ret = await api.Http.PostAsJsonAsync("/api/purchases/supplier-returns", new
        {
            date = DateTime.UtcNow, supplierId, storeId = refs.StoreId, currencyId = refs.CurrencyId,
            referenceSupplyId = supplyId,
            notes = "брак партии",
            lines = new[] { new { productId, quantity = 3m, unitPrice = 70m } },
        });
        ret.EnsureSuccessStatusCode();
        var retId = (await ret.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();

        using var post = await api.Http.PostAsJsonAsync($"/api/purchases/supplier-returns/{retId}/post", new { });
        post.IsSuccessStatusCode.Should().BeTrue($"post вернул {(int)post.StatusCode}: {await post.Content.ReadAsStringAsync()}");
        (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(7m);

        using var unpost = await api.Http.PostAsJsonAsync($"/api/purchases/supplier-returns/{retId}/unpost", new { });
        unpost.IsSuccessStatusCode.Should().BeTrue();
        (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(10m);
    }

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

        // Принять 2 шт.
        var supply = await api.Http.PostAsJsonAsync("/api/purchases/supplies", new
        {
            date = DateTime.UtcNow, supplierId, storeId = refs.StoreId, currencyId = refs.CurrencyId,
            notes = "init", lines = new[] { new { productId, quantity = 2m, unitPrice = 50m } },
        });
        supply.EnsureSuccessStatusCode();
        var supplyId = (await supply.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/purchases/supplies/{supplyId}/post", new { })).EnsureSuccessStatusCode();

        // Попытаться вернуть 5.
        var ret = await api.Http.PostAsJsonAsync("/api/purchases/supplier-returns", new
        {
            date = DateTime.UtcNow, supplierId, storeId = refs.StoreId, currencyId = refs.CurrencyId,
            referenceSupplyId = (string?)null,
            notes = "over", lines = new[] { new { productId, quantity = 5m, unitPrice = 50m } },
        });
        ret.EnsureSuccessStatusCode();
        var retId = (await ret.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();

        using var post = await api.Http.PostAsJsonAsync($"/api/purchases/supplier-returns/{retId}/post", new { });
        ((int)post.StatusCode).Should().Be(409);
        (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(2m);
    }

    [Fact]
    public async Task Reference_supplier_mismatch_400()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"sret-mm-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var supplierA = await api.CreateCounterpartyAsync($"SupA-{Guid.NewGuid():N}");
        var supplierB = await api.CreateCounterpartyAsync($"SupB-{Guid.NewGuid():N}");
        var productId = await api.CreateProductAsync(refs, $"Prod-{Guid.NewGuid():N}", 100m, RandomBarcode());

        var supply = await api.Http.PostAsJsonAsync("/api/purchases/supplies", new
        {
            date = DateTime.UtcNow, supplierId = supplierA, storeId = refs.StoreId, currencyId = refs.CurrencyId,
            notes = "from A", lines = new[] { new { productId, quantity = 3m, unitPrice = 50m } },
        });
        supply.EnsureSuccessStatusCode();
        var supplyId = (await supply.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/purchases/supplies/{supplyId}/post", new { })).EnsureSuccessStatusCode();

        // Попытка возвратить supplier B, ссылаясь на приёмку от A — должен быть 400.
        using var resp = await api.Http.PostAsJsonAsync("/api/purchases/supplier-returns", new
        {
            date = DateTime.UtcNow, supplierId = supplierB, storeId = refs.StoreId, currencyId = refs.CurrencyId,
            referenceSupplyId = supplyId, notes = "mismatch",
            lines = new[] { new { productId, quantity = 1m, unitPrice = 50m } },
        });
        ((int)resp.StatusCode).Should().Be(400);
    }

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

        var resp = await a.Http.PostAsJsonAsync("/api/purchases/supplier-returns", new
        {
            date = DateTime.UtcNow, supplierId = supplierA, storeId = refsA.StoreId, currencyId = refsA.CurrencyId,
            referenceSupplyId = (string?)null, notes = "iso",
            lines = new[] { new { productId = productA, quantity = 1m, unitPrice = 10m } },
        });
        resp.EnsureSuccessStatusCode();
        var retId = (await resp.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();

        var bList = await b.ListAsync("/api/purchases/supplier-returns?pageSize=200");
        bList.Should().NotContain(x => x.GetProperty("id").GetString() == retId);

        using var direct = await b.Http.GetAsync($"/api/purchases/supplier-returns/{retId}");
        direct.StatusCode.Should().Be(System.Net.HttpStatusCode.NotFound);
    }
}
