using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Shouldly;
using Xunit;

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

/// <summary>
///     A Grpc-mode node with no listen port or no key does not error — it simply never serves
///     telemetry, and whoever is watching sees nothing with no stack trace to point at. These tests
///     make that misconfiguration a loud host-start failure instead.
/// </summary>
public class TelemetryOptionsValidatorTests
{
    private static ValidateOptionsResult Validate(TelemetryOptions o, params string[] roles)
    {
        var pairs = new Dictionary<string, string?>();
        for (var i = 0; i < roles.Length; i++)
        {
            pairs[$"Cluster:Roles:{i}"] = roles[i];
        }

        var configuration = new ConfigurationBuilder().AddInMemoryCollection(pairs).Build();

        return new TelemetryOptionsValidator(configuration).Validate(TelemetryOptions.SectionName, o);
    }

    [Fact]
    public void Default_options_are_valid()
    {
        Validate(new TelemetryOptions()).Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void Unknown_mode_fails()
    {
        var result = Validate(new TelemetryOptions { Mode = "stream" });

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain("stream");
    }

    [Fact]
    public void Mode_matching_is_case_insensitive()
    {
        Validate(new TelemetryOptions
        {
            Mode = "grpc",
            GrpcListenPort = 5100,
            ApiKey = "k",
        }, "driver").Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void Grpc_driver_node_with_no_listen_port_fails()
    {
        var result = Validate(
            new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, ApiKey = "k" },
            "driver");

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain(nameof(TelemetryOptions.GrpcListenPort));
    }

    [Fact]
    public void Grpc_with_empty_key_fails()
    {
        var result = Validate(
            new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, GrpcListenPort = 5100, ApiKey = "" },
            "driver");

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain(nameof(TelemetryOptions.ApiKey));
    }

    [Fact]
    public void Grpc_driver_with_port_and_key_is_valid()
    {
        Validate(
            new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, GrpcListenPort = 5100, ApiKey = "k" },
            "driver").Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void Grpc_admin_only_node_does_not_require_a_listen_port()
    {
        // The listen-port requirement is driver-specific — an admin-only node dialling out (via
        // TelemetryDialOptions) never serves, so it has nothing to bind.
        Validate(
            new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, ApiKey = "k" },
            "admin").Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void Grpc_with_no_roles_does_not_require_a_key()
    {
        // Fail-closed only applies to a node that actually carries a role — a roleless node hosts
        // nothing to protect.
        Validate(new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, ApiKey = "" })
            .Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void Dps_ignores_the_empty_grpc_surface()
    {
        Validate(
            new TelemetryOptions { Mode = TelemetryOptions.ModeDps, GrpcListenPort = 0, ApiKey = "" },
            "driver").Succeeded.ShouldBeTrue();
    }
}

/// <summary>
///     Mirrors <see cref="TelemetryOptionsValidatorTests"/> for the central dial-side options.
/// </summary>
public class TelemetryDialOptionsValidatorTests
{
    private static ValidateOptionsResult Validate(TelemetryDialOptions o) =>
        new TelemetryDialOptionsValidator().Validate(TelemetryDialOptions.SectionName, o);

    [Fact]
    public void Default_options_are_valid()
    {
        Validate(new TelemetryDialOptions()).Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void Unknown_mode_fails()
    {
        var result = Validate(new TelemetryDialOptions { Mode = "poll" });

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain("poll");
    }

    [Fact]
    public void Grpc_with_empty_key_fails()
    {
        var result = Validate(new TelemetryDialOptions { Mode = TelemetryDialOptions.ModeGrpc, ApiKey = "" });

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain(nameof(TelemetryDialOptions.ApiKey));
    }

    [Fact]
    public void Grpc_with_a_key_is_valid()
    {
        Validate(new TelemetryDialOptions { Mode = TelemetryDialOptions.ModeGrpc, ApiKey = "k" })
            .Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void Dps_ignores_the_empty_key()
    {
        Validate(new TelemetryDialOptions { Mode = TelemetryDialOptions.ModeDps, ApiKey = "" })
            .Succeeded.ShouldBeTrue();
    }
}
