using System.Net;
using System.Net.Http.Headers;

namespace SatelliteProvider.IntegrationTests;

// AZ-505 AC-5: HTTP/2 multiplexed responses.
//
// Kestrel is configured with `HttpProtocols.Http1AndHttp2` over TLS
// (docker-compose.yml mounts the dev cert; ASPNETCORE_URLS=https://+:8080).
// ALPN negotiates HTTP/2 with HTTP/2-capable clients and falls back to
// HTTP/1.1 for browsers and legacy callers. The integration-tests
// container trusts the dev cert via /usr/local/share/ca-certificates,
// so HttpClient negotiates HTTP/2 transparently — no h2c / unencrypted
// support switch is needed.
public static class Http2MultiplexingTests
{
    private const int ConcurrentRequestCount = 20;

    public static async Task RunAll(string apiUrl, string secret)
    {
        RouteTestHelpers.PrintTestHeader("Test: HTTP/2 multiplexing on /tiles/{z}/{x}/{y} (AZ-505)");

        var apiUri = new Uri(apiUrl);
        using var handler = new SocketsHttpHandler
        {
            // AC-5 requires the responses to multiplex on a SINGLE TCP
            // connection. Limiting the connection pool to 1 forces this.
            PooledConnectionLifetime = TimeSpan.FromMinutes(2),
            EnableMultipleHttp2Connections = false
        };

        using var client = new HttpClient(handler)
        {
            BaseAddress = apiUri,
            Timeout = TimeSpan.FromMinutes(1),
            DefaultRequestVersion = HttpVersion.Version20,
            DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact
        };
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
            "Bearer",
            JwtTestHelpers.MintAuthenticated(secret));

        // Pick a single (z, x, y) — caching means all 20 calls hit the same
        // tile, which is exactly what we want: prove the responses come back
        // over HTTP/2 with their CDN-style headers preserved.
        //
        // Coords (158485, 91707) at z=18 are the slippy projection of
        // (47.461747, 37.647063), the same lat/lon JwtIntegrationTests hits
        // — confirmed to have Google Maps satellite coverage by every prior
        // cycle's run, so the warmup download is reliable.
        const int z = 18;
        const int x = 158485;
        const int y = 91707;
        var path = $"/tiles/{z}/{x}/{y}";

        // Prime the cache with a single warm-up call so the 20 concurrent
        // calls don't pay the GoogleMaps download cost.
        var warmup = await client.GetAsync(path);
        await EnsureSuccess(warmup, "AC-5 warmup");

        var concurrentTasks = Enumerable.Range(0, ConcurrentRequestCount)
            .Select(_ => client.GetAsync(path))
            .ToArray();
        var responses = await Task.WhenAll(concurrentTasks);

        try
        {
            for (var i = 0; i < responses.Length; i++)
            {
                var response = responses[i];
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    throw new Exception($"AC-5: response {i} expected HTTP 200, got HTTP {(int)response.StatusCode}");
                }
                if (response.Version != HttpVersion.Version20)
                {
                    throw new Exception($"AC-5: response {i} expected HTTP/2.0, got HTTP/{response.Version}");
                }
                if (response.Headers.ETag is null)
                {
                    throw new Exception($"AC-5: response {i} is missing the ETag header — header preservation regressed.");
                }
                if (response.Headers.CacheControl is null)
                {
                    throw new Exception($"AC-5: response {i} is missing the Cache-Control header — header preservation regressed.");
                }
            }
        }
        finally
        {
            foreach (var r in responses)
            {
                r.Dispose();
            }
        }

        Console.WriteLine($"  ✓ All {ConcurrentRequestCount} concurrent GETs returned HTTP/2.0 with preserved ETag + Cache-Control");
    }

    private static async Task EnsureSuccess(HttpResponseMessage response, string label)
    {
        if (!response.IsSuccessStatusCode)
        {
            var body = await response.Content.ReadAsStringAsync();
            throw new Exception($"{label}: expected success, got HTTP {(int)response.StatusCode}. Body: {body}");
        }
        if (response.Version != HttpVersion.Version20)
        {
            throw new Exception($"{label}: expected HTTP/2 even on warmup, got HTTP/{response.Version}");
        }
    }
}
