using ScadaLink.Commons.Types.Flattening;
using ScadaLink.TemplateEngine.Validation;

namespace ScadaLink.TemplateEngine.Tests.Validation;

public class ValidationServiceTests
{
    private readonly ValidationService _sut = new();

    [Fact]
    public void Validate_ValidConfig_ReturnsSuccess()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "Instance1",
            Attributes = [new ResolvedAttribute { CanonicalName = "Temp", Value = "25", DataType = "Double" }],
            Scripts = [new ResolvedScript { CanonicalName = "Monitor", Code = "var x = 1;" }]
        };

        var result = _sut.Validate(config);
        Assert.True(result.IsValid);
    }

    [Fact]
    public void Validate_EmptyInstanceName_ReturnsError()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "",
            Attributes = [new ResolvedAttribute { CanonicalName = "Temp", DataType = "Double" }]
        };

        var result = _sut.Validate(config);
        Assert.False(result.IsValid);
        Assert.Contains(result.Errors, e => e.Category == ValidationCategory.FlatteningFailure);
    }

    [Fact]
    public void Validate_NamingCollision_ReturnsError()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "Instance1",
            Attributes =
            [
                new ResolvedAttribute { CanonicalName = "Temp", DataType = "Double" },
                new ResolvedAttribute { CanonicalName = "Temp", DataType = "Int32" } // Duplicate!
            ]
        };

        var result = _sut.Validate(config);
        Assert.False(result.IsValid);
        Assert.Contains(result.Errors, e => e.Category == ValidationCategory.NamingCollision);
    }

    [Fact]
    public void Validate_ForbiddenApi_ReturnsCompilationError()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "Instance1",
            Scripts =
            [
                new ResolvedScript
                {
                    CanonicalName = "BadScript",
                    Code = "System.IO.File.ReadAllText(\"secret.txt\");"
                }
            ]
        };

        var result = _sut.Validate(config);
        Assert.False(result.IsValid);
        Assert.Contains(result.Errors, e => e.Category == ValidationCategory.ScriptCompilation);
    }

    [Fact]
    public void Validate_MismatchedBraces_ReturnsCompilationError()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "Instance1",
            Scripts =
            [
                new ResolvedScript { CanonicalName = "Bad", Code = "if (true) {" }
            ]
        };

        var result = _sut.Validate(config);
        Assert.False(result.IsValid);
        Assert.Contains(result.Errors, e => e.Category == ValidationCategory.ScriptCompilation);
    }

    [Fact]
    public void Validate_AlarmReferencesMissingAttribute_ReturnsError()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "Instance1",
            Attributes = [new ResolvedAttribute { CanonicalName = "Temp", DataType = "Double" }],
            Alarms =
            [
                new ResolvedAlarm
                {
                    CanonicalName = "HighPressure",
                    TriggerType = "RangeViolation",
                    TriggerConfiguration = "{\"attributeName\":\"Pressure\"}" // Pressure doesn't exist
                }
            ]
        };

        var result = _sut.Validate(config);
        Assert.False(result.IsValid);
        Assert.Contains(result.Errors, e => e.Category == ValidationCategory.AlarmTriggerReference);
    }

    [Fact]
    public void Validate_AlarmReferencesExistingAttribute_NoError()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "Instance1",
            Attributes = [new ResolvedAttribute { CanonicalName = "Temp", DataType = "Double" }],
            Alarms =
            [
                new ResolvedAlarm
                {
                    CanonicalName = "HighTemp",
                    TriggerType = "RangeViolation",
                    TriggerConfiguration = "{\"attributeName\":\"Temp\"}"
                }
            ]
        };

        var result = _sut.Validate(config);
        // Should not have alarm trigger reference errors
        Assert.DoesNotContain(result.Errors, e => e.Category == ValidationCategory.AlarmTriggerReference);
    }

    [Fact]
    public void Validate_ScriptTriggerReferencesMissingAttribute_ReturnsError()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "Instance1",
            Attributes = [new ResolvedAttribute { CanonicalName = "Temp", DataType = "Double" }],
            Scripts =
            [
                new ResolvedScript
                {
                    CanonicalName = "OnChange",
                    Code = "var x = 1;",
                    TriggerConfiguration = "{\"attributeName\":\"Missing\"}"
                }
            ]
        };

        var result = _sut.Validate(config);
        Assert.False(result.IsValid);
        Assert.Contains(result.Errors, e => e.Category == ValidationCategory.ScriptTriggerReference);
    }

    [Fact]
    public void Validate_UnboundDataSourceAttribute_ReturnsWarning()
    {
        var config = new FlattenedConfiguration
        {
            InstanceUniqueName = "Instance1",
            Attributes =
            [
                new ResolvedAttribute
                {
                    CanonicalName = "Temp",
                    DataType = "Double",
                    DataSourceReference = "ns=2;s=Temp",
                    BoundDataConnectionId = null // No binding!
                }
            ]
        };

        var result = _sut.Validate(config);
        Assert.Contains(result.Warnings, w => w.Category == ValidationCategory.ConnectionBinding);
    }

    [Fact]
    public void Validate_EmptyConfig_ReturnsWarning()
    {
        var config = new FlattenedConfiguration { InstanceUniqueName = "Instance1" };

        var result = _sut.Validate(config);
        Assert.Contains(result.Warnings, w => w.Category == ValidationCategory.FlatteningFailure);
    }
}
