using Shouldly;
using Xunit;

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

public sealed class DriverTypeRegistryTests
{
    private static DriverTypeMetadata SampleMetadata(
        string typeName = "Modbus",
        NamespaceKindCompatibility allowed = NamespaceKindCompatibility.Equipment,
        DriverTier tier = DriverTier.B) =>
        new(typeName, allowed,
            DriverConfigJsonSchema: "{\"type\": \"object\"}",
            DeviceConfigJsonSchema: "{\"type\": \"object\"}",
            TagConfigJsonSchema: "{\"type\": \"object\"}",
            Tier: tier);

    [Fact]
    public void Register_ThenGet_RoundTrips()
    {
        var registry = new DriverTypeRegistry();
        var metadata = SampleMetadata();

        registry.Register(metadata);

        registry.Get("Modbus").ShouldBe(metadata);
    }

    [Theory]
    [InlineData(DriverTier.A)]
    [InlineData(DriverTier.B)]
    [InlineData(DriverTier.C)]
    public void Register_Requires_NonNullTier(DriverTier tier)
    {
        var registry = new DriverTypeRegistry();
        var metadata = SampleMetadata(typeName: $"Driver-{tier}", tier: tier);

        registry.Register(metadata);

        registry.Get(metadata.TypeName).Tier.ShouldBe(tier);
    }

    [Fact]
    public void Get_IsCaseInsensitive()
    {
        var registry = new DriverTypeRegistry();
        registry.Register(SampleMetadata("Galaxy"));

        registry.Get("galaxy").ShouldNotBeNull();
        registry.Get("GALAXY").ShouldNotBeNull();
    }

    [Fact]
    public void Get_UnknownType_Throws()
    {
        var registry = new DriverTypeRegistry();
        registry.Register(SampleMetadata("Modbus"));

        Should.Throw<KeyNotFoundException>(() => registry.Get("UnregisteredType"));
    }

    [Fact]
    public void TryGet_UnknownType_ReturnsNull()
    {
        var registry = new DriverTypeRegistry();
        registry.Register(SampleMetadata("Modbus"));

        registry.TryGet("UnregisteredType").ShouldBeNull();
    }

    [Fact]
    public void Register_DuplicateType_Throws()
    {
        var registry = new DriverTypeRegistry();
        registry.Register(SampleMetadata("Modbus"));

        Should.Throw<InvalidOperationException>(() => registry.Register(SampleMetadata("Modbus")));
    }

    [Fact]
    public void Register_DuplicateTypeIsCaseInsensitive()
    {
        var registry = new DriverTypeRegistry();
        registry.Register(SampleMetadata("Modbus"));

        Should.Throw<InvalidOperationException>(() => registry.Register(SampleMetadata("modbus")));
    }

    [Fact]
    public void All_ReturnsRegisteredTypes()
    {
        var registry = new DriverTypeRegistry();
        registry.Register(SampleMetadata("Modbus"));
        registry.Register(SampleMetadata("S7"));
        registry.Register(SampleMetadata("Galaxy", NamespaceKindCompatibility.SystemPlatform));

        var all = registry.All();

        all.Count.ShouldBe(3);
        all.Select(m => m.TypeName).ShouldBe(new[] { "Modbus", "S7", "Galaxy" }, ignoreOrder: true);
    }

    [Fact]
    public void NamespaceKindCompatibility_FlagsAreBitmask()
    {
        // Per decision #111 — driver types like OpcUaClient may be valid for multiple namespace kinds.
        var both = NamespaceKindCompatibility.Equipment | NamespaceKindCompatibility.SystemPlatform;

        both.HasFlag(NamespaceKindCompatibility.Equipment).ShouldBeTrue();
        both.HasFlag(NamespaceKindCompatibility.SystemPlatform).ShouldBeTrue();
        both.HasFlag(NamespaceKindCompatibility.Simulated).ShouldBeFalse();
    }

    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("   ")]
    public void Get_RejectsEmptyTypeName(string? typeName)
    {
        var registry = new DriverTypeRegistry();
        Should.Throw<ArgumentException>(() => registry.Get(typeName!));
    }
}
