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

    /// <summary>/metrics доступен без авторизации (стандарт Prometheus) и
    /// выдаёт text/plain в exposition format.</summary>
    [Fact]
    public async Task Metrics_endpoint_unauthenticated_returns_prometheus_text()
    {
        using var client = _factory.CreateClient();
        using var resp = await client.GetAsync("/metrics");
        resp.IsSuccessStatusCode.Should().BeTrue($"/metrics вернул {(int)resp.StatusCode}");
        resp.Content.Headers.ContentType!.MediaType.Should().StartWith("text/plain");
        var text = await resp.Content.ReadAsStringAsync();
        // process_* + dotnet_* — встроенные коллекторы prometheus-net.
        text.Should().Contain("process_cpu_seconds_total");
        // HTTP-метрика появляется после первого UseHttpMetrics-обработанного запроса
        // (этот же GET /metrics на пути HTTP-pipeline уже учитан в счётчике).
        text.Should().Contain("http_requests_received_total");
    }

    /// <summary>После проведения чека счётчик sales_posted увеличивается.</summary>
    [Fact]
    public async Task Sales_posted_counter_increments_after_post()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"metr-sale-{Guid.NewGuid():N}");
        var refs = await api.LoadRefsAsync();
        var p1 = await api.CreateProductAsync(refs, $"P-{Guid.NewGuid():N}", 100m, RandomBarcode());

        // Подкормка склада, чек, проводим.
        var enter = 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 = 50m } },
        });
        enter.EnsureSuccessStatusCode();
        var eid = (await enter.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString();
        (await api.Http.PostAsJsonAsync($"/api/inventory/enters/{eid}/post", new { })).EnsureSuccessStatusCode();

        // Снимаем "до".
        var before = await ReadMetricSample("food_market_sales_posted_total", null);

        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 = 100m, paidCard = 0m,
            lines = new[] { new { productId = p1, quantity = 1m, 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 after = await ReadMetricSample("food_market_sales_posted_total", null);
        after.Should().BeGreaterThan(before);
    }

    /// <summary>DB-длительность измеряется через интерсептор EF.</summary>
    [Fact]
    public async Task Db_query_duration_metric_observed()
    {
        var api = new ApiActor(_factory.CreateClient());
        await api.SignupAndLoginAsync($"metr-db-{Guid.NewGuid():N}");
        // Любой запрос с EF в обработчике (signup уже сделал SELECT/INSERT'ы).
        await api.GetJsonAsync("/api/catalog/products?pageSize=10");

        using var client = _factory.CreateClient();
        var text = await client.GetStringAsync("/metrics");
        text.Should().Contain("food_market_db_query_duration_seconds_count");
    }

    /// <summary>Читает значение sample'а метрики (тип counter без labels).
    /// Возвращает 0 если строки нет.</summary>
    private async Task<double> ReadMetricSample(string metricName, string? label)
    {
        using var client = _factory.CreateClient();
        var text = await client.GetStringAsync("/metrics");
        var prefix = label is null ? metricName : metricName;
        foreach (var line in text.Split('\n'))
        {
            if (line.StartsWith("#")) continue;
            if (!line.StartsWith(prefix)) continue;
            // Формат: "metric_name{labels} value timestamp?" или "metric_name value"
            var parts = line.Trim().Split(' ');
            if (parts.Length >= 2 && double.TryParse(parts[^1], System.Globalization.NumberStyles.Any,
                System.Globalization.CultureInfo.InvariantCulture, out var v))
                return v;
        }
        return 0;
    }
}
