using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

namespace foodmarket.IntegrationTests.Support;

/// <summary>Тонкая обёртка над HttpClient для интеграционных тестов: логин,
/// JSON-хелперы, выборка справочников орг-бутстрапа. Один экземпляр = один
/// «актор» (свой токен/орг).</summary>
public sealed class ApiActor
{
    public HttpClient Http { get; }

    public ApiActor(HttpClient http) => Http = http;

    public void UseToken(string token)
        => Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    // ── Auth ────────────────────────────────────────────────────────────────
    public Task<HttpResponseMessage> SignupAsync(string email, string password, string orgName, string phone = "+77001234567")
        => Http.PostAsJsonAsync("/api/auth/signup",
            new { email, password, organizationName = orgName, phone, plan = (string?)null });

    public async Task<string> TokenAsync(string email, string password)
    {
        using var resp = await Http.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
        {
            ["grant_type"] = "password",
            ["username"] = email,
            ["password"] = password,
            ["client_id"] = "food-market-web",
            ["scope"] = "openid profile email roles api offline_access",
        }));
        resp.EnsureSuccessStatusCode();
        return (await resp.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("access_token").GetString()!;
    }

    /// <summary>Регистрирует орг, логинит её администратора и проставляет токен.
    /// Возвращает email/пароль администратора.</summary>
    public async Task<(string Email, string Password)> SignupAndLoginAsync(string slug)
    {
        var email = $"{slug}@example.kz";
        const string password = "Passw0rd!";
        (await SignupAsync(email, password, $"Org {slug}")).EnsureSuccessStatusCode();
        UseToken(await TokenAsync(email, password));
        return (email, password);
    }

    // ── JSON helpers ──────────────────────────────────────────────────────────
    public async Task<JsonElement> GetJsonAsync(string url)
    {
        using var resp = await Http.GetAsync(url);
        resp.EnsureSuccessStatusCode();
        return await resp.Content.ReadFromJsonAsync<JsonElement>();
    }

    /// <summary>items[] из PagedResult (или сам массив, если endpoint отдаёт список).</summary>
    public async Task<List<JsonElement>> ListAsync(string url)
    {
        var json = await GetJsonAsync(url);
        var arr = json.ValueKind == JsonValueKind.Array ? json
                : json.TryGetProperty("items", out var items) ? items
                : default;
        return arr.ValueKind == JsonValueKind.Array ? arr.EnumerateArray().ToList() : new();
    }

    public async Task<string> FirstIdAsync(string url, Func<JsonElement, bool>? match = null)
    {
        var list = await ListAsync(url);
        var hit = match is null ? list.FirstOrDefault() : list.FirstOrDefault(match);
        return hit.ValueKind == JsonValueKind.Object ? hit.GetProperty("id").GetString()! : throw new InvalidOperationException($"No item in {url}");
    }

    // ── Org bootstrap refs ─────────────────────────────────────────────────────
    public async Task<Refs> LoadRefsAsync()
    {
        var unit = await FirstIdAsync("/api/catalog/units-of-measure?pageSize=50");
        var group = await FirstIdAsync("/api/catalog/product-groups?pageSize=200");
        var store = await FirstIdAsync("/api/catalog/stores?pageSize=200");
        var currency = await FirstIdAsync("/api/catalog/currencies?pageSize=50",
            e => e.GetProperty("code").GetString() == "KZT");
        // Системный/розничный тип цены.
        var priceType = await FirstIdAsync("/api/catalog/price-types?pageSize=200",
            e => e.TryGetProperty("isSystem", out var s) && s.GetBoolean());
        return new Refs(unit, group, store, currency, priceType);
    }

    public async Task<string> CreateCounterpartyAsync(string name)
    {
        var resp = await Http.PostAsJsonAsync("/api/catalog/counterparties", new
        {
            name,
            legalName = name,
            type = 0,
            bin = "987654321098",
            iin = (string?)null,
            taxNumber = (string?)null,
            countryId = (string?)null,
            address = "Алматы",
            phone = "+77003332211",
            email = "cp@example.kz",
            bankName = (string?)null,
            bankAccount = (string?)null,
            bik = (string?)null,
            contactPerson = "Иванов",
            notes = (string?)null,
        });
        resp.EnsureSuccessStatusCode();
        return (await resp.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString()!;
    }

    public async Task<string> CreateProductAsync(Refs refs, string name, decimal retailPrice, string barcode)
    {
        var resp = await Http.PostAsJsonAsync("/api/catalog/products", new
        {
            name,
            article = (string?)null,
            description = (string?)null,
            unitOfMeasureId = refs.UnitId,
            vat = 12,
            vatEnabled = true,
            productGroupId = refs.GroupId,
            defaultSupplierId = (string?)null,
            countryOfOriginId = (string?)null,
            isService = false,
            packaging = 0,
            isMarked = false,
            minStock = (decimal?)null,
            maxStock = (decimal?)null,
            referencePrice = retailPrice * 0.7m,
            purchaseCurrencyId = refs.CurrencyId,
            imageUrl = (string?)null,
            prices = new[] { new { priceTypeId = refs.PriceTypeId, amount = retailPrice, currencyId = refs.CurrencyId } },
            barcodes = new[] { new { code = barcode, type = 0, isPrimary = true } },
        });
        var body = await resp.Content.ReadAsStringAsync();
        if (!resp.IsSuccessStatusCode)
            throw new InvalidOperationException($"CreateProduct failed {(int)resp.StatusCode}: {body}");
        return JsonDocument.Parse(body).RootElement.GetProperty("id").GetString()!;
    }

    public async Task<decimal> StockOfAsync(string storeId, string productId)
    {
        var list = await ListAsync($"/api/inventory/stock?productId={productId}&pageSize=200");
        var row = list.FirstOrDefault(i =>
            i.GetProperty("productId").GetString() == productId &&
            i.GetProperty("storeId").GetString() == storeId);
        return row.ValueKind == JsonValueKind.Object ? row.GetProperty("quantity").GetDecimal() : 0m;
    }

    public sealed record Refs(string UnitId, string GroupId, string StoreId, string CurrencyId, string PriceTypeId);
}
