using System.Net.Http;
using System.Text.Json;
using Microsoft.Extensions.Logging.Abstractions;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
using ZB.MOM.WW.OtOpcUa.Server.Observability;

namespace ZB.MOM.WW.OtOpcUa.Server.Tests;

[Trait("Category", "Integration")]
public sealed class HealthEndpointsHostTests : IAsyncLifetime
{
    private static int _portCounter = 48500 + Random.Shared.Next(0, 99);
    private readonly int _port = Interlocked.Increment(ref _portCounter);
    private string Prefix => $"http://localhost:{_port}/";
    private readonly DriverHost _driverHost = new();
    private HealthEndpointsHost _host = null!;
    private HttpClient _client = null!;

    public ValueTask InitializeAsync()
    {
        _client = new HttpClient { BaseAddress = new Uri(Prefix) };
        return ValueTask.CompletedTask;
    }

    public async ValueTask DisposeAsync()
    {
        _client.Dispose();
        if (_host is not null) await _host.DisposeAsync();
    }

    private HealthEndpointsHost Start(Func<bool>? configDbHealthy = null, Func<bool>? usingStaleConfig = null)
    {
        _host = new HealthEndpointsHost(
            _driverHost,
            NullLogger<HealthEndpointsHost>.Instance,
            configDbHealthy,
            usingStaleConfig,
            prefix: Prefix);
        _host.Start();
        return _host;
    }

    [Fact]
    public async Task Healthz_ReturnsHealthy_EmptyFleet()
    {
        Start();

        var response = await _client.GetAsync("/healthz");

        response.IsSuccessStatusCode.ShouldBeTrue();
        var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
        body.GetProperty("status").GetString().ShouldBe("healthy");
        body.GetProperty("configDbReachable").GetBoolean().ShouldBeTrue();
        body.GetProperty("usingStaleConfig").GetBoolean().ShouldBeFalse();
    }

    [Fact]
    public async Task Healthz_StaleConfig_Returns200_WithFlag()
    {
        Start(configDbHealthy: () => false, usingStaleConfig: () => true);

        var response = await _client.GetAsync("/healthz");

        response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
        var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
        body.GetProperty("configDbReachable").GetBoolean().ShouldBeFalse();
        body.GetProperty("usingStaleConfig").GetBoolean().ShouldBeTrue();
    }

    [Fact]
    public async Task Healthz_UnreachableConfig_And_NoCache_Returns503()
    {
        Start(configDbHealthy: () => false, usingStaleConfig: () => false);

        var response = await _client.GetAsync("/healthz");

        response.StatusCode.ShouldBe(System.Net.HttpStatusCode.ServiceUnavailable);
    }

    [Fact]
    public async Task Readyz_EmptyFleet_Is200_Healthy()
    {
        Start();

        var response = await _client.GetAsync("/readyz");

        response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
        var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
        body.GetProperty("verdict").GetString().ShouldBe("Healthy");
    }

    [Fact]
    public async Task Readyz_WithHealthyDriver_Is200()
    {
        await _driverHost.RegisterAsync(new StubDriver("drv-1", DriverState.Healthy), "{}", CancellationToken.None);
        Start();

        var response = await _client.GetAsync("/readyz");

        response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
        var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
        body.GetProperty("verdict").GetString().ShouldBe("Healthy");
        body.GetProperty("drivers").GetArrayLength().ShouldBe(1);
    }

    [Fact]
    public async Task Readyz_WithFaultedDriver_Is503()
    {
        await _driverHost.RegisterAsync(new StubDriver("dead", DriverState.Faulted), "{}", CancellationToken.None);
        await _driverHost.RegisterAsync(new StubDriver("alive", DriverState.Healthy), "{}", CancellationToken.None);
        Start();

        var response = await _client.GetAsync("/readyz");

        response.StatusCode.ShouldBe(System.Net.HttpStatusCode.ServiceUnavailable);
        var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
        body.GetProperty("verdict").GetString().ShouldBe("Faulted");
    }

    [Fact]
    public async Task Readyz_WithDegradedDriver_Is200_WithDegradedList()
    {
        await _driverHost.RegisterAsync(new StubDriver("drv-ok", DriverState.Healthy), "{}", CancellationToken.None);
        await _driverHost.RegisterAsync(new StubDriver("drv-deg", DriverState.Degraded), "{}", CancellationToken.None);
        Start();

        var response = await _client.GetAsync("/readyz");

        response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
        var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
        body.GetProperty("verdict").GetString().ShouldBe("Degraded");
        body.GetProperty("degradedDrivers").GetArrayLength().ShouldBe(1);
        body.GetProperty("degradedDrivers")[0].GetString().ShouldBe("drv-deg");
    }

    [Fact]
    public async Task Readyz_WithInitializingDriver_Is503()
    {
        await _driverHost.RegisterAsync(new StubDriver("init", DriverState.Initializing), "{}", CancellationToken.None);
        Start();

        var response = await _client.GetAsync("/readyz");

        response.StatusCode.ShouldBe(System.Net.HttpStatusCode.ServiceUnavailable);
    }

    [Fact]
    public async Task Unknown_Path_Returns404()
    {
        Start();

        var response = await _client.GetAsync("/foo");

        response.StatusCode.ShouldBe(System.Net.HttpStatusCode.NotFound);
    }

    private sealed class StubDriver : IDriver
    {
        private readonly DriverState _state;
        public StubDriver(string id, DriverState state)
        {
            DriverInstanceId = id;
            _state = state;
        }
        public string DriverInstanceId { get; }
        public string DriverType => "Stub";
        public Task InitializeAsync(string _, CancellationToken ct) => Task.CompletedTask;
        public Task ReinitializeAsync(string _, CancellationToken ct) => Task.CompletedTask;
        public Task ShutdownAsync(CancellationToken ct) => Task.CompletedTask;
        public DriverHealth GetHealth() => new(_state, null, null);
        public long GetMemoryFootprint() => 0;
        public Task FlushOptionalCachesAsync(CancellationToken ct) => Task.CompletedTask;
    }
}
