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

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

/// <summary>
///     Every failure this validator catches otherwise surfaces as an <i>absence</i> — a deployment
///     that never arrives, an alarm ack that does nothing, a node whose ack is dropped without a
///     trace. Absences are the hardest class of fault to attribute in the field, so the whole point
///     of these tests is that the host refuses to start instead.
/// </summary>
public class MeshTransportOptionsValidatorTests
{
    private static ValidateOptionsResult Validate(MeshTransportOptions o) =>
        new MeshTransportOptionsValidator().Validate(MeshTransportOptions.SectionName, o);

    [Fact]
    public void Default_options_are_valid()
    {
        // The default is Dps — the pre-Phase-2 transport. A node that never sets this section must
        // behave exactly as it did before, which is the whole premise of the dark switch.
        Validate(new MeshTransportOptions()).Succeeded.ShouldBeTrue();
    }

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

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

    [Fact]
    public void Mode_matching_is_case_insensitive()
    {
        Validate(new MeshTransportOptions
        {
            Mode = "clusterclient",
            CentralContactPoints = ["akka.tcp://otopcua@central-1:4053"],
        }).Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void Non_positive_contact_refresh_fails()
    {
        var result = Validate(new MeshTransportOptions { ContactRefreshSeconds = 0 });

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain(nameof(MeshTransportOptions.ContactRefreshSeconds));
    }

    [Fact]
    public void ClusterClient_mode_requires_central_contact_points()
    {
        var result = Validate(new MeshTransportOptions
        {
            Mode = MeshTransportOptions.ModeClusterClient,
            CentralContactPoints = [],
        });

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain(nameof(MeshTransportOptions.CentralContactPoints));
    }

    [Fact]
    public void Dps_mode_does_not_require_central_contact_points()
    {
        // An admin-only node has no reason to carry them, and requiring them under the default mode
        // would make the section mandatory everywhere for a feature that is switched off.
        Validate(new MeshTransportOptions
        {
            Mode = MeshTransportOptions.ModeDps,
            CentralContactPoints = [],
        }).Succeeded.ShouldBeTrue();
    }

    [Fact]
    public void A_contact_point_that_is_not_an_akka_address_fails()
    {
        var result = Validate(new MeshTransportOptions
        {
            Mode = MeshTransportOptions.ModeClusterClient,
            CentralContactPoints = ["central-1:4053"],
        });

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain("akka.tcp://");
    }

    [Fact]
    public void A_contact_point_carrying_an_actor_path_suffix_fails()
    {
        // The sister project's shipped site template listed a full receptionist path, and separately
        // listed the site's OWN remoting port as the second contact — "a permanent failure in the
        // initial-contact rotation". Both are invisible until a command goes missing.
        var result = Validate(new MeshTransportOptions
        {
            Mode = MeshTransportOptions.ModeClusterClient,
            CentralContactPoints = ["akka.tcp://otopcua@central-1:4053/system/receptionist"],
        });

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain("/system/receptionist");
    }

    [Fact]
    public void One_bad_contact_point_among_good_ones_still_fails()
    {
        var result = Validate(new MeshTransportOptions
        {
            Mode = MeshTransportOptions.ModeClusterClient,
            CentralContactPoints =
            [
                "akka.tcp://otopcua@central-1:4053",
                "central-2:4053",
            ],
        });

        result.Failed.ShouldBeTrue();
        result.FailureMessage.ShouldContain("central-2:4053");
    }

    [Fact]
    public void Well_formed_ClusterClient_options_succeed()
    {
        Validate(new MeshTransportOptions
        {
            Mode = MeshTransportOptions.ModeClusterClient,
            CentralContactPoints =
            [
                "akka.tcp://otopcua@central-1:4053",
                "akka.tcp://otopcua@central-2:4053",
            ],
        }).Succeeded.ShouldBeTrue();
    }
}
