using ScadaLink.Commons.Entities.Templates;
using ScadaLink.Commons.Types.Enums;

namespace ScadaLink.TemplateEngine.Tests;

public class CollisionDetectorTests
{
    // ========================================================================
    // WP-12: Naming Collision Detection
    // ========================================================================

    [Fact]
    public void DetectCollisions_NoCollisions_ReturnsEmpty()
    {
        var template = new Template("Pump") { Id = 1 };
        template.Attributes.Add(new TemplateAttribute("Speed") { Id = 1, TemplateId = 1, DataType = DataType.Float });
        template.Alarms.Add(new TemplateAlarm("HighTemp") { Id = 1, TemplateId = 1, TriggerType = AlarmTriggerType.ValueMatch });

        var all = new List<Template> { template };
        var collisions = CollisionDetector.DetectCollisions(template, all);

        Assert.Empty(collisions);
    }

    [Fact]
    public void DetectCollisions_DifferentModulesNoPrefixCollision_ReturnsEmpty()
    {
        // Two composed modules with same member name but different instance names
        var moduleA = new Template("ModuleA") { Id = 2 };
        moduleA.Attributes.Add(new TemplateAttribute("Value") { Id = 10, TemplateId = 2, DataType = DataType.Float });

        var moduleB = new Template("ModuleB") { Id = 3 };
        moduleB.Attributes.Add(new TemplateAttribute("Value") { Id = 11, TemplateId = 3, DataType = DataType.Float });

        var template = new Template("Pump") { Id = 1 };
        template.Compositions.Add(new TemplateComposition("modA") { Id = 1, TemplateId = 1, ComposedTemplateId = 2 });
        template.Compositions.Add(new TemplateComposition("modB") { Id = 2, TemplateId = 1, ComposedTemplateId = 3 });

        var all = new List<Template> { template, moduleA, moduleB };
        var collisions = CollisionDetector.DetectCollisions(template, all);

        // modA.Value and modB.Value are different canonical names => no collision
        Assert.Empty(collisions);
    }

    [Fact]
    public void DetectCollisions_DirectAndComposedNameCollision_ReturnsCollision()
    {
        // Template has a direct attribute "Speed"
        // Composed module also has an attribute that would produce canonical name "Speed"
        // This happens when a module's member has no prefix collision — actually
        // composed members always have a prefix so this shouldn't collide.
        // But a direct member "modA.Value" would collide with modA.Value from composition.
        // Let's test: direct attr named "modA.Value" and composition modA with member "Value"

        var module = new Template("Module") { Id = 2 };
        module.Attributes.Add(new TemplateAttribute("Value") { Id = 10, TemplateId = 2, DataType = DataType.Float });

        var template = new Template("Pump") { Id = 1 };
        template.Attributes.Add(new TemplateAttribute("modA.Value") { Id = 1, TemplateId = 1, DataType = DataType.Float });
        template.Compositions.Add(new TemplateComposition("modA") { Id = 1, TemplateId = 1, ComposedTemplateId = 2 });

        var all = new List<Template> { template, module };
        var collisions = CollisionDetector.DetectCollisions(template, all);

        Assert.NotEmpty(collisions);
        Assert.Contains(collisions, c => c.Contains("modA.Value"));
    }

    [Fact]
    public void DetectCollisions_NestedComposition_ReturnsCorrectCanonicalNames()
    {
        // Inner module
        var inner = new Template("Inner") { Id = 3 };
        inner.Attributes.Add(new TemplateAttribute("Pressure") { Id = 30, TemplateId = 3, DataType = DataType.Float });

        // Outer module composes inner
        var outer = new Template("Outer") { Id = 2 };
        outer.Compositions.Add(new TemplateComposition("inner1") { Id = 1, TemplateId = 2, ComposedTemplateId = 3 });

        // Main template composes outer
        var main = new Template("Main") { Id = 1 };
        main.Compositions.Add(new TemplateComposition("outer1") { Id = 2, TemplateId = 1, ComposedTemplateId = 2 });

        var all = new List<Template> { main, outer, inner };
        var collisions = CollisionDetector.DetectCollisions(main, all);

        // No collision, just checking it doesn't crash on nested compositions
        Assert.Empty(collisions);
    }

    [Fact]
    public void DetectCollisions_InheritedMembersCollideWithComposed_ReturnsCollision()
    {
        // Parent has a direct attribute "modA.Temp"
        var parent = new Template("Base") { Id = 1 };
        parent.Attributes.Add(new TemplateAttribute("modA.Temp") { Id = 10, TemplateId = 1, DataType = DataType.Float });

        // Module has attribute "Temp"
        var module = new Template("Module") { Id = 3 };
        module.Attributes.Add(new TemplateAttribute("Temp") { Id = 30, TemplateId = 3, DataType = DataType.Float });

        // Child inherits from parent and composes module as "modA"
        var child = new Template("Child") { Id = 2, ParentTemplateId = 1 };
        child.Compositions.Add(new TemplateComposition("modA") { Id = 1, TemplateId = 2, ComposedTemplateId = 3 });

        var all = new List<Template> { parent, child, module };
        var collisions = CollisionDetector.DetectCollisions(child, all);

        // "modA.Temp" from parent and "modA.Temp" from composed module
        Assert.NotEmpty(collisions);
        Assert.Contains(collisions, c => c.Contains("modA.Temp"));
    }
}
