using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Resilience;

namespace ZB.MOM.WW.OtOpcUa.Core.Tests.Resilience;

[Trait("Category", "Unit")]
public sealed class DriverResilienceOptionsTests
{
    [Theory]
    [InlineData(DriverTier.A)]
    [InlineData(DriverTier.B)]
    [InlineData(DriverTier.C)]
    public void TierDefaults_Cover_EveryCapability(DriverTier tier)
    {
        var defaults = DriverResilienceOptions.GetTierDefaults(tier);

        foreach (var capability in Enum.GetValues<DriverCapability>())
            defaults.ShouldContainKey(capability);
    }

    [Theory]
    [InlineData(DriverTier.A)]
    [InlineData(DriverTier.B)]
    [InlineData(DriverTier.C)]
    public void Write_NeverRetries_ByDefault(DriverTier tier)
    {
        var defaults = DriverResilienceOptions.GetTierDefaults(tier);
        defaults[DriverCapability.Write].RetryCount.ShouldBe(0);
    }

    [Theory]
    [InlineData(DriverTier.A)]
    [InlineData(DriverTier.B)]
    [InlineData(DriverTier.C)]
    public void AlarmAcknowledge_NeverRetries_ByDefault(DriverTier tier)
    {
        var defaults = DriverResilienceOptions.GetTierDefaults(tier);
        defaults[DriverCapability.AlarmAcknowledge].RetryCount.ShouldBe(0);
    }

    [Theory]
    [InlineData(DriverTier.A, DriverCapability.Read)]
    [InlineData(DriverTier.A, DriverCapability.HistoryRead)]
    [InlineData(DriverTier.B, DriverCapability.Discover)]
    [InlineData(DriverTier.B, DriverCapability.Probe)]
    [InlineData(DriverTier.C, DriverCapability.AlarmSubscribe)]
    public void IdempotentCapabilities_Retry_ByDefault(DriverTier tier, DriverCapability capability)
    {
        var defaults = DriverResilienceOptions.GetTierDefaults(tier);
        defaults[capability].RetryCount.ShouldBeGreaterThan(0);
    }

    [Fact]
    public void TierC_DisablesCircuitBreaker_DeferringToSupervisor()
    {
        var defaults = DriverResilienceOptions.GetTierDefaults(DriverTier.C);

        foreach (var (_, policy) in defaults)
            policy.BreakerFailureThreshold.ShouldBe(0, "Tier C breaker is handled by the Proxy supervisor (decision #68)");
    }

    [Theory]
    [InlineData(DriverTier.A)]
    [InlineData(DriverTier.B)]
    public void TierAAndB_EnableCircuitBreaker(DriverTier tier)
    {
        var defaults = DriverResilienceOptions.GetTierDefaults(tier);

        foreach (var (_, policy) in defaults)
            policy.BreakerFailureThreshold.ShouldBeGreaterThan(0);
    }

    [Fact]
    public void Resolve_Uses_TierDefaults_When_NoOverride()
    {
        var options = new DriverResilienceOptions { Tier = DriverTier.A };

        var resolved = options.Resolve(DriverCapability.Read);

        resolved.ShouldBe(DriverResilienceOptions.GetTierDefaults(DriverTier.A)[DriverCapability.Read]);
    }

    [Fact]
    public void Resolve_Uses_Override_When_Configured()
    {
        var custom = new CapabilityPolicy(TimeoutSeconds: 42, RetryCount: 7, BreakerFailureThreshold: 9);
        var options = new DriverResilienceOptions
        {
            Tier = DriverTier.A,
            CapabilityPolicies = new Dictionary<DriverCapability, CapabilityPolicy>
            {
                [DriverCapability.Read] = custom,
            },
        };

        options.Resolve(DriverCapability.Read).ShouldBe(custom);
        options.Resolve(DriverCapability.Write).ShouldBe(
            DriverResilienceOptions.GetTierDefaults(DriverTier.A)[DriverCapability.Write]);
    }
}
