using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Resilience;
using ZB.MOM.WW.OtOpcUa.Host.Drivers;
using ZB.MOM.WW.Secrets.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;

/// <summary>
///     Guards the production DI wiring for the Phase 6.1 resilience dispatch pipeline (arch-review #10 +
///     #13). <c>AddOtOpcUaDriverFactories</c> (the driver-node bootstrap) must bind the <b>real</b>
///     <see cref="DriverCapabilityInvokerFactory"/> — not the <see cref="NullDriverCapabilityInvokerFactory"/>
///     pass-through — and that factory must mint a real <see cref="CapabilityInvoker"/> per driver instance.
///     This is the DI half of the #10 live gate: without it a refactor could silently drop back to the
///     pass-through and the retry/breaker/bulkhead pipeline would go inert in production (the exact
///     "built-but-never-wired" failure the whole review targets), while every unit test — which defaults to
///     the pass-through — stays green.
/// </summary>
public sealed class ResilienceInvokerFactoryRegistrationTests
{
    [Fact]
    public void AddOtOpcUaDriverFactories_binds_the_real_invoker_factory_not_the_pass_through()
    {
        var services = new ServiceCollection();
        services.AddLogging();
        // The driver-factory registration resolves ISecretResolver (Galaxy/OpcUaClient secret: refs);
        // the real host always has it via AddZbSecrets (registered unconditionally). Mirror that here.
        services.AddSingleton<ISecretResolver>(new StubSecretResolver());
        services.AddOtOpcUaDriverFactories();

        using var sp = services.BuildServiceProvider();
        var factory = sp.GetRequiredService<IDriverCapabilityInvokerFactory>();

        factory.ShouldBeOfType<DriverCapabilityInvokerFactory>(
            "the driver-node bootstrap must bind the real resilience factory — a NullDriverCapabilityInvokerFactory here means the pipeline is inert in production");
    }

    [Fact]
    public void The_registered_factory_mints_a_real_CapabilityInvoker_per_instance()
    {
        var services = new ServiceCollection();
        services.AddLogging();
        // The driver-factory registration resolves ISecretResolver (Galaxy/OpcUaClient secret: refs);
        // the real host always has it via AddZbSecrets (registered unconditionally). Mirror that here.
        services.AddSingleton<ISecretResolver>(new StubSecretResolver());
        services.AddOtOpcUaDriverFactories();

        using var sp = services.BuildServiceProvider();
        var factory = sp.GetRequiredService<IDriverCapabilityInvokerFactory>();

        var invoker = factory.Create("drv-1", "Modbus");

        invoker.ShouldBeOfType<CapabilityInvoker>(
            "the real factory must produce a real CapabilityInvoker (not the NullDriverCapabilityInvoker pass-through)");
    }

    [Fact]
    public void The_resilience_singletons_the_factory_depends_on_are_registered()
    {
        var services = new ServiceCollection();
        services.AddLogging();
        // The driver-factory registration resolves ISecretResolver (Galaxy/OpcUaClient secret: refs);
        // the real host always has it via AddZbSecrets (registered unconditionally). Mirror that here.
        services.AddSingleton<ISecretResolver>(new StubSecretResolver());
        services.AddOtOpcUaDriverFactories();

        using var sp = services.BuildServiceProvider();

        // The pipeline builder + status tracker are process singletons the factory closes over — the same
        // instance must be shared so pipelines cache + telemetry aggregate across all driver instances.
        sp.GetService<DriverResiliencePipelineBuilder>().ShouldNotBeNull();
        sp.GetService<DriverResilienceStatusTracker>().ShouldNotBeNull();
        sp.GetRequiredService<DriverResiliencePipelineBuilder>()
            .ShouldBeSameAs(sp.GetRequiredService<DriverResiliencePipelineBuilder>(), "builder must be a singleton");
    }

    // Minimal ISecretResolver so AddOtOpcUaDriverFactories' registration (which resolves it for
    // Galaxy/OpcUaClient secret: refs) can be built. These tests exercise no secret: refs, so
    // returning null for every name is sufficient — the real resolver comes from AddZbSecrets.
    private sealed class StubSecretResolver : ISecretResolver
    {
        public Task<string?> GetAsync(SecretName name, CancellationToken ct) => Task.FromResult<string?>(null);
    }
}
