using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Configuration.Validation;

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

[Trait("Category", "Unit")]
public sealed class DraftValidatorTests
{
    [Theory]
    [InlineData("valid-name", true)]
    [InlineData("line-01", true)]
    [InlineData("_default", true)]
    [InlineData("UPPER", false)]
    [InlineData("with space", false)]
    [InlineData("", false)]
    public void UnsSegment_rule_accepts_lowercase_or_default_only(string name, bool shouldPass)
    {
        var uuid = Guid.NewGuid();
        var draft = new DraftSnapshot
        {
            GenerationId = 1, ClusterId = "c",
            Equipment =
            [
                new Equipment
                {
                    EquipmentUuid = uuid,
                    EquipmentId = DraftValidator.DeriveEquipmentId(uuid),
                    Name = name,
                    DriverInstanceId = "d",
                    UnsLineId = "line-a",
                    MachineCode = "m",
                },
            ],
        };

        var errors = DraftValidator.Validate(draft);
        var hasUnsError = errors.Any(e => e.Code == "UnsSegmentInvalid");
        hasUnsError.ShouldBe(!shouldPass);
    }

    [Fact]
    public void Cross_cluster_namespace_binding_is_rejected()
    {
        var draft = new DraftSnapshot
        {
            GenerationId = 1, ClusterId = "c-A",
            Namespaces = [new Namespace { NamespaceId = "ns-1", ClusterId = "c-B", NamespaceUri = "urn:x", Kind = NamespaceKind.Equipment }],
            DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c-A", NamespaceId = "ns-1", Name = "drv", DriverType = "ModbusTcp", DriverConfig = "{}" }],
        };

        var errors = DraftValidator.Validate(draft);
        errors.ShouldContain(e => e.Code == "BadCrossClusterNamespaceBinding");
    }

    [Fact]
    public void Same_cluster_namespace_binding_is_accepted()
    {
        var draft = new DraftSnapshot
        {
            GenerationId = 1, ClusterId = "c-A",
            Namespaces = [new Namespace { NamespaceId = "ns-1", ClusterId = "c-A", NamespaceUri = "urn:x", Kind = NamespaceKind.Equipment }],
            DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c-A", NamespaceId = "ns-1", Name = "drv", DriverType = "ModbusTcp", DriverConfig = "{}" }],
        };

        DraftValidator.Validate(draft).ShouldNotContain(e => e.Code == "BadCrossClusterNamespaceBinding");
    }

    [Fact]
    public void EquipmentUuid_change_across_generations_is_rejected()
    {
        var oldUuid = Guid.Parse("11111111-1111-1111-1111-111111111111");
        var newUuid = Guid.Parse("22222222-2222-2222-2222-222222222222");
        var eid = DraftValidator.DeriveEquipmentId(oldUuid);

        var draft = new DraftSnapshot
        {
            GenerationId = 2, ClusterId = "c",
            Equipment = [new Equipment { EquipmentUuid = newUuid, EquipmentId = eid, Name = "eq", DriverInstanceId = "d", UnsLineId = "line-a", MachineCode = "m" }],
            PriorEquipment = [new Equipment { EquipmentUuid = oldUuid, EquipmentId = eid, Name = "eq", DriverInstanceId = "d", UnsLineId = "line-a", MachineCode = "m" }],
        };

        DraftValidator.Validate(draft).ShouldContain(e => e.Code == "EquipmentUuidImmutable");
    }

    [Fact]
    public void ZTag_reserved_by_different_uuid_is_rejected()
    {
        var uuid = Guid.NewGuid();
        var otherUuid = Guid.NewGuid();

        var draft = new DraftSnapshot
        {
            GenerationId = 1, ClusterId = "c",
            Equipment = [new Equipment { EquipmentUuid = uuid, EquipmentId = DraftValidator.DeriveEquipmentId(uuid), Name = "eq", DriverInstanceId = "d", UnsLineId = "line-a", MachineCode = "m", ZTag = "ZT-001" }],
            ActiveReservations = [new ExternalIdReservation { Kind = ReservationKind.ZTag, Value = "ZT-001", EquipmentUuid = otherUuid, ClusterId = "c", FirstPublishedBy = "t" }],
        };

        DraftValidator.Validate(draft).ShouldContain(e => e.Code == "BadDuplicateExternalIdentifier");
    }

    [Fact]
    public void EquipmentId_that_does_not_match_derivation_is_rejected()
    {
        var uuid = Guid.NewGuid();
        var draft = new DraftSnapshot
        {
            GenerationId = 1, ClusterId = "c",
            Equipment = [new Equipment { EquipmentUuid = uuid, EquipmentId = "EQ-operator-typed", Name = "eq", DriverInstanceId = "d", UnsLineId = "line-a", MachineCode = "m" }],
        };

        DraftValidator.Validate(draft).ShouldContain(e => e.Code == "EquipmentIdNotDerived");
    }

    [Fact]
    public void Galaxy_driver_in_Equipment_namespace_is_rejected()
    {
        var draft = new DraftSnapshot
        {
            GenerationId = 1, ClusterId = "c",
            Namespaces = [new Namespace { NamespaceId = "ns-1", ClusterId = "c", NamespaceUri = "urn:x", Kind = NamespaceKind.Equipment }],
            DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c", NamespaceId = "ns-1", Name = "drv", DriverType = "Galaxy", DriverConfig = "{}" }],
        };

        DraftValidator.Validate(draft).ShouldContain(e => e.Code == "DriverNamespaceKindMismatch");
    }

    [Fact]
    public void Draft_with_three_violations_surfaces_all_three()
    {
        var uuid = Guid.NewGuid();
        var draft = new DraftSnapshot
        {
            GenerationId = 1, ClusterId = "c-A",
            Namespaces = [new Namespace { NamespaceId = "ns-1", ClusterId = "c-B", NamespaceUri = "urn:x", Kind = NamespaceKind.Equipment }],
            DriverInstances = [new DriverInstance { DriverInstanceId = "d-1", ClusterId = "c-A", NamespaceId = "ns-1", Name = "drv", DriverType = "Galaxy", DriverConfig = "{}" }],
            Equipment = [new Equipment { EquipmentUuid = uuid, EquipmentId = "EQ-wrong", Name = "BAD NAME", DriverInstanceId = "d-1", UnsLineId = "line-a", MachineCode = "m" }],
        };

        var errors = DraftValidator.Validate(draft);
        errors.ShouldContain(e => e.Code == "BadCrossClusterNamespaceBinding");
        errors.ShouldContain(e => e.Code == "DriverNamespaceKindMismatch");
        errors.ShouldContain(e => e.Code == "EquipmentIdNotDerived");
        errors.ShouldContain(e => e.Code == "UnsSegmentInvalid");
    }
}
