using Shouldly;
using Xunit;
using ZB.MOM.WW.Secrets.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests;

/// <summary>
///     Unit-only coverage of <see cref="GalaxyDriverBrowser"/>'s pre-connect validation.
///     These tests do not require a live mxaccessgw endpoint and are safe to run without
///     the gateway fixture — they exercise the JSON deserialization + validation paths
///     that run before <c>GalaxyRepositoryClient.Create</c> + <c>TestConnectionAsync</c>.
///     The factory's transport-construction path is covered by the integration suite
///     (Task 17) and manual smoke (Task 18) since both require a real gateway.
/// </summary>
[Trait("Category", "Unit")]
public sealed class GalaxyDriverBrowserTests
{
    // These unit tests exercise only the pre-connect validation paths (empty endpoint,
    // blank client name) that throw BEFORE the API-key secret: arm is resolved, so a
    // null-returning stub resolver suffices — it is never consulted.
    private readonly GalaxyDriverBrowser _sut = new(new StubSecretResolver());

    /// <summary>A no-op resolver: every name resolves to null. Never consulted by these tests.</summary>
    private sealed class StubSecretResolver : ISecretResolver
    {
        /// <inheritdoc />
        public Task<string?> GetAsync(SecretName name, CancellationToken ct) => Task.FromResult<string?>(null);
    }

    /// <summary>The DriverType key must match the AdminUI's persisted "GalaxyMxGateway" value
    /// so the factory wire-up picks the right browser implementation.</summary>
    [Fact]
    public void DriverType_is_GalaxyMxGateway() => _sut.DriverType.ShouldBe("GalaxyMxGateway");

    /// <summary>An empty Gateway.Endpoint must fail fast with a clear, endpoint-mentioning
    /// message rather than surfacing a downstream gRPC URI parse error.</summary>
    [Fact]
    public async Task OpenAsync_with_empty_endpoint_throws_InvalidOperationException()
    {
        var json = """{"Gateway":{"Endpoint":"","ApiKeySecretRef":"dev:k"},"MxAccess":{"ClientName":"X"},"Repository":{},"Reconnect":{}}""";
        var ex = await Should.ThrowAsync<InvalidOperationException>(
            () => _sut.OpenAsync(json, TestContext.Current.CancellationToken));
        ex.Message.ShouldContain("Endpoint");
    }

    /// <summary>An empty MxAccess.ClientName must fail fast — refused so the gateway
    /// side doesn't see anonymous browse sessions during triage.</summary>
    [Fact]
    public async Task OpenAsync_with_empty_clientName_throws_InvalidOperationException()
    {
        var json = """{"Gateway":{"Endpoint":"http://127.0.0.1:1","ApiKeySecretRef":"dev:k"},"MxAccess":{"ClientName":""},"Repository":{},"Reconnect":{}}""";
        var ex = await Should.ThrowAsync<InvalidOperationException>(
            () => _sut.OpenAsync(json, TestContext.Current.CancellationToken));
        ex.Message.ShouldContain("ClientName");
    }

    /// <summary>A JSON literal that deserializes to null must fail fast with a
    /// "deserialized to null" message — never a downstream NRE.</summary>
    [Fact]
    public async Task OpenAsync_with_null_json_throws_InvalidOperationException()
    {
        var ex = await Should.ThrowAsync<InvalidOperationException>(
            () => _sut.OpenAsync("null", TestContext.Current.CancellationToken));
        ex.Message.ShouldContain("null");
    }
}
