using ScadaLink.Commons.Types;

namespace ScadaLink.TemplateEngine.Validation;

/// <summary>
/// Validates script code by attempting to compile it using Roslyn.
/// In production, this would compile C# scripts against a stub ScriptApi assembly
/// that provides the allowed API surface (attribute read/write, CallScript, CallShared, etc.)
/// and enforces the forbidden API list (System.IO, Process, Threading, Reflection, raw network).
///
/// For now, this implementation performs basic syntax validation.
/// </summary>
public class ScriptCompiler
{
    /// <summary>
    /// Forbidden namespace patterns — scripts (and trigger expressions, via
    /// <see cref="ValidationService"/>) must not use these. Trigger expressions run
    /// under the same trust model as scripts, so the list is shared from here rather
    /// than duplicated.
    /// </summary>
    internal static readonly string[] ForbiddenPatterns =
    [
        "System.IO.",
        "System.Diagnostics.Process",
        "System.Threading.",
        "System.Reflection.",
        "System.Net.Sockets.",
        "System.Net.Http.",
    ];

    /// <summary>
    /// Attempts to compile a script and returns success or a compilation error.
    /// </summary>
    /// <param name="code">The C# script code.</param>
    /// <param name="scriptName">The canonical name of the script (for error messages).</param>
    /// <returns>Success if the script compiles, or Failure with the error message.</returns>
    public Result<bool> TryCompile(string code, string scriptName)
    {
        if (string.IsNullOrWhiteSpace(code))
            return Result<bool>.Failure($"Script '{scriptName}' has empty code.");

        // Check for forbidden APIs
        foreach (var pattern in ForbiddenPatterns)
        {
            if (code.Contains(pattern, StringComparison.Ordinal))
            {
                return Result<bool>.Failure(
                    $"Script '{scriptName}' uses forbidden API: '{pattern.TrimEnd('.')}'. " +
                    "Scripts cannot use System.IO, Process, Threading, Reflection, or raw network APIs.");
            }
        }

        // Basic brace matching validation
        var braceDepth = 0;
        var inString = false;
        var inLineComment = false;
        var inBlockComment = false;

        for (int i = 0; i < code.Length; i++)
        {
            var c = code[i];
            var next = i + 1 < code.Length ? code[i + 1] : '\0';

            if (inLineComment)
            {
                if (c == '\n') inLineComment = false;
                continue;
            }

            if (inBlockComment)
            {
                if (c == '*' && next == '/')
                {
                    inBlockComment = false;
                    i++;
                }
                continue;
            }

            if (c == '/' && next == '/')
            {
                inLineComment = true;
                i++;
                continue;
            }

            if (c == '/' && next == '*')
            {
                inBlockComment = true;
                i++;
                continue;
            }

            if (c == '"' && !inString)
            {
                inString = true;
                continue;
            }

            if (c == '"' && inString)
            {
                // Check for escaped quote
                if (i > 0 && code[i - 1] != '\\')
                    inString = false;
                continue;
            }

            if (inString) continue;

            if (c == '{') braceDepth++;
            else if (c == '}') braceDepth--;

            if (braceDepth < 0)
                return Result<bool>.Failure($"Script '{scriptName}' has mismatched braces (unexpected closing brace).");
        }

        if (braceDepth != 0)
            return Result<bool>.Failure($"Script '{scriptName}' has mismatched braces ({braceDepth} unclosed).");

        if (inBlockComment)
            return Result<bool>.Failure($"Script '{scriptName}' has an unclosed block comment.");

        return Result<bool>.Success(true);
    }
}
