using ScadaLink.Commons.Entities.Templates;

namespace ScadaLink.TemplateEngine.Tests;

public class CycleDetectorTests
{
    // ========================================================================
    // WP-13: Inheritance cycle detection
    // ========================================================================

    [Fact]
    public void DetectInheritanceCycle_SelfInheritance_ReturnsCycle()
    {
        var template = new Template("A") { Id = 1 };
        var all = new List<Template> { template };

        var result = CycleDetector.DetectInheritanceCycle(1, 1, all);

        Assert.NotNull(result);
        Assert.Contains("itself", result);
    }

    [Fact]
    public void DetectInheritanceCycle_DirectCycle_ReturnsCycle()
    {
        var templateA = new Template("A") { Id = 1, ParentTemplateId = null };
        var templateB = new Template("B") { Id = 2, ParentTemplateId = 1 };
        var all = new List<Template> { templateA, templateB };

        // A tries to inherit from B (B already inherits from A)
        var result = CycleDetector.DetectInheritanceCycle(1, 2, all);

        Assert.NotNull(result);
        Assert.Contains("cycle", result, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void DetectInheritanceCycle_ThreeNodeCycle_ReturnsCycle()
    {
        var templateA = new Template("A") { Id = 1, ParentTemplateId = null };
        var templateB = new Template("B") { Id = 2, ParentTemplateId = 1 };
        var templateC = new Template("C") { Id = 3, ParentTemplateId = 2 };
        var all = new List<Template> { templateA, templateB, templateC };

        // A tries to inherit from C (C -> B -> A creates a cycle)
        var result = CycleDetector.DetectInheritanceCycle(1, 3, all);

        Assert.NotNull(result);
        Assert.Contains("cycle", result, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void DetectInheritanceCycle_NoCycle_ReturnsNull()
    {
        var templateA = new Template("A") { Id = 1, ParentTemplateId = null };
        var templateB = new Template("B") { Id = 2, ParentTemplateId = null };
        var all = new List<Template> { templateA, templateB };

        var result = CycleDetector.DetectInheritanceCycle(2, 1, all);

        Assert.Null(result);
    }

    // ========================================================================
    // WP-13: Composition cycle detection
    // ========================================================================

    [Fact]
    public void DetectCompositionCycle_SelfComposition_ReturnsCycle()
    {
        var template = new Template("A") { Id = 1 };
        var all = new List<Template> { template };

        var result = CycleDetector.DetectCompositionCycle(1, 1, all);

        Assert.NotNull(result);
        Assert.Contains("compose itself", result);
    }

    [Fact]
    public void DetectCompositionCycle_DirectCycle_ReturnsCycle()
    {
        var templateA = new Template("A") { Id = 1 };
        templateA.Compositions.Add(new TemplateComposition("b1") { Id = 1, TemplateId = 1, ComposedTemplateId = 2 });
        var templateB = new Template("B") { Id = 2 };
        var all = new List<Template> { templateA, templateB };

        // B tries to compose A (A already composes B)
        var result = CycleDetector.DetectCompositionCycle(2, 1, all);

        Assert.NotNull(result);
        Assert.Contains("cycle", result, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void DetectCompositionCycle_TransitiveCycle_ReturnsCycle()
    {
        var templateA = new Template("A") { Id = 1 };
        templateA.Compositions.Add(new TemplateComposition("b1") { Id = 1, TemplateId = 1, ComposedTemplateId = 2 });
        var templateB = new Template("B") { Id = 2 };
        templateB.Compositions.Add(new TemplateComposition("c1") { Id = 2, TemplateId = 2, ComposedTemplateId = 3 });
        var templateC = new Template("C") { Id = 3 };
        var all = new List<Template> { templateA, templateB, templateC };

        // C tries to compose A => C -> A -> B -> C
        var result = CycleDetector.DetectCompositionCycle(3, 1, all);

        Assert.NotNull(result);
    }

    [Fact]
    public void DetectCompositionCycle_NoCycle_ReturnsNull()
    {
        var templateA = new Template("A") { Id = 1 };
        var templateB = new Template("B") { Id = 2 };
        var all = new List<Template> { templateA, templateB };

        var result = CycleDetector.DetectCompositionCycle(1, 2, all);

        Assert.Null(result);
    }

    // ========================================================================
    // WP-13: Cross-graph cycle detection (inheritance + composition)
    // ========================================================================

    [Fact]
    public void DetectCrossGraphCycle_InheritanceCompositionCross_ReturnsCycle()
    {
        // A inherits from B, B composes C. If C tries to set parent = A, that's a cross-graph cycle.
        var templateA = new Template("A") { Id = 1, ParentTemplateId = 2 };
        var templateB = new Template("B") { Id = 2 };
        templateB.Compositions.Add(new TemplateComposition("c1") { Id = 1, TemplateId = 2, ComposedTemplateId = 3 });
        var templateC = new Template("C") { Id = 3 };
        var all = new List<Template> { templateA, templateB, templateC };

        // C tries to add parent = A
        var result = CycleDetector.DetectCrossGraphCycle(3, 1, null, all);

        Assert.NotNull(result);
        Assert.Contains("Cross-graph cycle", result);
    }

    [Fact]
    public void DetectCrossGraphCycle_NoCycle_ReturnsNull()
    {
        var templateA = new Template("A") { Id = 1 };
        var templateB = new Template("B") { Id = 2 };
        var templateC = new Template("C") { Id = 3 };
        var all = new List<Template> { templateA, templateB, templateC };

        var result = CycleDetector.DetectCrossGraphCycle(3, 1, 2, all);

        Assert.Null(result);
    }
}
