using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;

namespace ZB.MOM.WW.ScadaBridge.ExternalSystemGateway;

public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Name prefix of the per-system <see cref="System.Net.Http.HttpClient"/> clients
    /// created by <see cref="ExternalSystemClient"/> (<c>ExternalSystem_{systemName}</c>).
    /// </summary>
    internal const string GatewayClientNamePrefix = "ExternalSystem_";

    /// <summary>
    /// Registers the External System Gateway services, HTTP client factory, and options.
    /// </summary>
    /// <param name="services">The service collection to configure.</param>
    /// <returns>The same <see cref="IServiceCollection"/> for chaining.</returns>
    public static IServiceCollection AddExternalSystemGateway(this IServiceCollection services)
    {
        services.AddOptions<ExternalSystemGatewayOptions>()
            .BindConfiguration("ScadaBridge:ExternalSystemGateway")
            .ValidateOnStart();
        services.TryAddEnumerable(
            ServiceDescriptor.Singleton<IValidateOptions<ExternalSystemGatewayOptions>,
                ExternalSystemGatewayOptionsValidator>());

        services.AddHttpClient();

        // Wire MaxConcurrentConnectionsPerSystem
        // into the primary handler of the gateway's per-system named clients
        // ("ExternalSystem_{name}") only. The names are created dynamically, so a
        // static AddHttpClient("name") registration is not possible; instead a
        // post-configure on HttpClientFactoryOptions is applied, filtered by the
        // client-name prefix. ConfigureHttpClientDefaults is deliberately NOT used —
        // it is process-global and would replace the primary handler of every
        // HttpClient in the host (e.g. the Notification Service's OAuth2 token
        // client), silently capping and overriding unrelated components.
        services.AddSingleton<IConfigureOptions<HttpClientFactoryOptions>>(sp =>
            new GatewayHttpClientConfigurator(
                sp.GetRequiredService<IOptionsMonitor<ExternalSystemGatewayOptions>>()));

        services.AddScoped<ExternalSystemClient>();
        services.AddScoped<IExternalSystemClient>(sp => sp.GetRequiredService<ExternalSystemClient>());
        services.AddScoped<DatabaseGateway>();
        services.AddScoped<IDatabaseGateway>(sp => sp.GetRequiredService<DatabaseGateway>());

        return services;
    }

    /// <summary>
    /// Placeholder for External System Gateway Akka.NET actor registrations (handled in AkkaHostedService).
    /// </summary>
    /// <param name="services">The service collection to configure.</param>
    /// <returns>The same <see cref="IServiceCollection"/> for chaining.</returns>
    public static IServiceCollection AddExternalSystemGatewayActors(this IServiceCollection services)
    {
        // Actor registration happens in AkkaHostedService.
        // Script Execution Actors run on dedicated blocking I/O dispatcher.
        return services;
    }

    /// <summary>
    /// Configures the primary HTTP message handler with the
    /// gateway's <see cref="ExternalSystemGatewayOptions.MaxConcurrentConnectionsPerSystem"/>
    /// cap, but only for the gateway's own named clients
    /// (<see cref="GatewayClientNamePrefix"/>). Clients owned by other host components
    /// are left untouched, so the cap does not leak process-wide.
    /// </summary>
    private sealed class GatewayHttpClientConfigurator
        : IConfigureNamedOptions<HttpClientFactoryOptions>
    {
        private readonly IOptionsMonitor<ExternalSystemGatewayOptions> _options;

        /// <summary>
        /// Initializes the configurator with the gateway options monitor.
        /// </summary>
        /// <param name="options">Live options providing the max-connections-per-system cap.</param>
        public GatewayHttpClientConfigurator(IOptionsMonitor<ExternalSystemGatewayOptions> options)
        {
            _options = options;
        }

        /// <summary>No-op: the default unnamed client is not a gateway client.</summary>
        /// <param name="options">Options for the default client (ignored).</param>
        public void Configure(HttpClientFactoryOptions options)
        {
            // The default (unnamed) client is not a gateway client — do nothing.
        }

        /// <summary>Applies the max-connections cap to gateway-owned named clients only.</summary>
        /// <param name="name">Client name; non-gateway names are skipped.</param>
        /// <param name="options">Factory options whose primary handler is configured.</param>
        public void Configure(string? name, HttpClientFactoryOptions options)
        {
            if (name == null || !name.StartsWith(GatewayClientNamePrefix, StringComparison.Ordinal))
            {
                return;
            }

            options.HttpMessageHandlerBuilderActions.Add(builder =>
                builder.PrimaryHandler = new SocketsHttpHandler
                {
                    MaxConnectionsPerServer = _options.CurrentValue.MaxConcurrentConnectionsPerSystem,
                });
        }
    }
}
