using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using NSubstitute;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;

namespace ZB.MOM.WW.ScadaBridge.ExternalSystemGateway.Tests;

/// <summary>
/// ExternalSystemGateway-013: configuration options must actually influence the
/// registered HTTP client — an operator setting them must not be silently ignored.
/// </summary>
public class ServiceWiringTests
{
    [Fact]
    public void MaxConcurrentConnectionsPerSystem_IsAppliedToTheNamedHttpClientPrimaryHandler()
    {
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["ScadaBridge:ExternalSystemGateway:MaxConcurrentConnectionsPerSystem"] = "4",
            })
            .Build();

        var services = new ServiceCollection();
        services.AddLogging();
        services.AddSingleton<IConfiguration>(config);
        services.AddSingleton(Substitute.For<IExternalSystemRepository>());
        services.AddExternalSystemGateway();

        using var provider = services.BuildServiceProvider();

        // Resolve the per-system named client's message-handler chain and walk to the
        // primary handler — the option must be reflected in MaxConnectionsPerServer.
        var handlerFactory = provider.GetRequiredService<IHttpMessageHandlerFactory>();
        var handler = handlerFactory.CreateHandler("ExternalSystem_AnySystem");

        var primary = FindPrimaryHandler(handler);
        var sockets = Assert.IsType<SocketsHttpHandler>(primary);
        Assert.Equal(4, sockets.MaxConnectionsPerServer);
    }

    [Fact]
    public void MaxConcurrentConnectionsPerSystem_IsNotAppliedToNonGatewayHttpClients()
    {
        // ExternalSystemGateway-016: the gateway's connection cap must be scoped to
        // its own per-system clients ("ExternalSystem_{name}"). It must NOT leak onto
        // unrelated HttpClient consumers in the same host process (e.g. the
        // Notification Service's OAuth2 token client) — that would silently throttle
        // and override the primary-handler configuration of another component.
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["ScadaBridge:ExternalSystemGateway:MaxConcurrentConnectionsPerSystem"] = "4",
            })
            .Build();

        var services = new ServiceCollection();
        services.AddLogging();
        services.AddSingleton<IConfiguration>(config);
        services.AddSingleton(Substitute.For<IExternalSystemRepository>());
        services.AddExternalSystemGateway();

        // A client owned by a different component, registered the way the
        // Notification Service registers its OAuth2 token client — a plain
        // AddHttpClient with no custom primary handler. Its primary handler must
        // remain the framework default (uncapped), not the gateway's SocketsHttpHandler.
        services.AddHttpClient("NotificationService_OAuth2");

        using var provider = services.BuildServiceProvider();
        var handlerFactory = provider.GetRequiredService<IHttpMessageHandlerFactory>();

        // The gateway's own client must still get the gateway cap.
        var gatewayPrimary = FindPrimaryHandler(handlerFactory.CreateHandler("ExternalSystem_AnySystem"));
        Assert.Equal(4, Assert.IsType<SocketsHttpHandler>(gatewayPrimary).MaxConnectionsPerServer);

        // The unrelated component's client must NOT inherit the gateway's connection
        // cap. With ConfigureHttpClientDefaults the primary handler is a
        // SocketsHttpHandler capped at the gateway's value (the leak); with a scoped
        // registration it is the framework default whose MaxConnectionsPerServer is
        // int.MaxValue.
        var otherPrimary = FindPrimaryHandler(handlerFactory.CreateHandler("NotificationService_OAuth2"));
        if (otherPrimary is SocketsHttpHandler otherSockets)
        {
            Assert.NotEqual(4, otherSockets.MaxConnectionsPerServer);
        }
    }

    private static HttpMessageHandler FindPrimaryHandler(HttpMessageHandler handler)
    {
        var current = handler;
        while (current is DelegatingHandler delegating && delegating.InnerHandler != null)
        {
            current = delegating.InnerHandler;
        }
        return current;
    }
}
