using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using RobotNet.Script;
using RobotNet.ScriptManager.Models;
using System.Collections.Immutable;
using System.Text;
using System.Text.RegularExpressions;

namespace RobotNet.ScriptManager.Helpers;

public static class CSharpSyntaxHelper
{
    public static bool ResolveValueFromString(string valueStr, Type type, out object? value)
    {
        // Check if type is in MissionParameterTypes
        if (!MissionParameterTypes.Contains(type))
        {
            value = null;
            return false;
        }

        // If type is RobotNet.Script.IRobot, return null
        if (type == RobotType)
        {
            value = null;
            return true;
        }

        // Convert string to the corresponding type
        if (type == typeof(string))
        {
            value = valueStr;
            return true;
        }

        if (type.IsEnum)
        {
            value = Enum.Parse(type, valueStr, ignoreCase: true);
            return true;
        }

        // Handle nullable types
        var underlyingType = Nullable.GetUnderlyingType(type);
        if (underlyingType != null)
        {
            value = null;
            return false;
        }

        value = Convert.ChangeType(valueStr, type);
        if (value is null)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    public static bool VerifyScript(string code, out string error,
        out List<ScriptVariableSyntax> variables,
        out List<ScriptTaskData> tasks,
        out List<ScriptMissionData> missions)
    {
        try
        {
            var listVariables = new List<ScriptVariableSyntax>();
            var wrappedCode = string.Join(Environment.NewLine, [ScriptConfiguration.UsingNamespacesScript, "public class DummyClass", "{", ScriptConfiguration.DeveloptGlobalsScript, code, "}"]);

            var devCompilation = CSharpCompilation.Create("CodeAnalysis")
                                    .WithReferences(ScriptConfiguration.MetadataReferences)
                                    .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
                                    .AddSyntaxTrees(CSharpSyntaxTree.ParseText(wrappedCode));

            var diagnostics = devCompilation.GetDiagnostics();
            if (diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error))
            {
                var message = "Verify errors: \r\n";
                foreach (var diag in diagnostics)
                {
                    if (diag.Severity == DiagnosticSeverity.Error)
                    {
                        message += $"\t❌ Error: {diag.GetMessage()} at {diag.Location}\r\n";
                    }
                    else if (diag.Severity == DiagnosticSeverity.Warning)
                    {
                        message += $"\t⚠️ Warning: {diag.GetMessage()} at {diag.Location}\r\n";
                    }
                }
                throw new Exception(message);
            }

            error = "";
            wrappedCode = string.Join(Environment.NewLine, [ScriptConfiguration.UsingNamespacesScript, "public class DummyClass", "{", code, "}"]);
            var syntaxTree = CSharpSyntaxTree.ParseText(wrappedCode);
            var root = syntaxTree.GetCompilationUnitRoot();

            var runCompilation = CSharpCompilation.Create("CodeAnalysis")
                .AddSyntaxTrees(syntaxTree)
                .WithReferences(ScriptConfiguration.MetadataReferences)
                .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            var classNode = root.DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault(c => c.Identifier.Text.Equals("DummyClass")) ?? throw new Exception("No class named 'DummyClass' found in the script.");
            var semanticModel = runCompilation.GetSemanticModel(syntaxTree);
            GetScriptVariables(classNode, semanticModel, out variables);
            GetScriptTasksAndMissions(classNode, semanticModel, out tasks, out missions);

            return true;
        }
        catch (Exception ex)
        {
            error = $"An error occurred while verifying the script: {ex.Message}";
            variables = [];
            tasks = [];
            missions = [];
            return false;
        }
        finally
        {
            GC.Collect();
        }
    }

    private static void GetScriptVariables(ClassDeclarationSyntax classNode, SemanticModel semanticModel, out List<ScriptVariableSyntax> variables)
    {
        variables = [];
        var fields = classNode.Members.OfType<FieldDeclarationSyntax>();
        foreach (var field in fields)
        {
            Type resolvedType = semanticModel.ToSystemType(field.Declaration.Type) ?? throw new Exception($"Failed to resolve type for field: {field.Declaration.Type.ToFullString()}");
            // Check if the field has VariableAttribute

            VariableAttribute? varAttr = null;
            foreach (var attrList in field.AttributeLists)
            {
                foreach (var attr in attrList.Attributes)
                {
                    var attrType = semanticModel.GetTypeAttribute(attr);
                    if (attrType == VariableAttributeType)
                    {
                        varAttr = semanticModel.GetConstantAttribute(attr, attrType) as RobotNet.Script.VariableAttribute;
                        break;
                    }
                }
                if (varAttr != null) break;
            }

            foreach (var variable in field.Declaration.Variables)
            {
                var name = variable.Identifier.Text;
                if (string.IsNullOrEmpty(name)) continue;



                if (variable.Initializer is null)
                {
                    var value = resolvedType.IsValueType ? Activator.CreateInstance(resolvedType) : null;
                    variables.Add(new ScriptVariableSyntax(name, resolvedType, value, varAttr != null, varAttr?.PublicWrite ?? false));
                }
                else
                {
                    var constant = semanticModel.GetConstantValue(variable.Initializer.Value);
                    if (constant.HasValue)
                    {
                        try
                        {
                            var value = Convert.ChangeType(constant.Value, resolvedType);
                            variables.Add(new ScriptVariableSyntax(name, resolvedType, value, varAttr != null, varAttr?.PublicWrite ?? false));
                        }
                        catch (Exception ex)
                        {
                            throw new Exception($"Failed to convert value of {name} = \"{constant.Value}\" to {resolvedType}: {ex}");
                        }
                    }
                    else
                    {
                        var code = variable.Initializer.Value.ToFullString();
                        object? value;
                        if (string.IsNullOrEmpty(code))
                        {
                            value = resolvedType.IsValueType ? Activator.CreateInstance(resolvedType) : null;
                        }
                        else
                        {
                            value = CSharpScript.EvaluateAsync<object>(code, ScriptConfiguration.ScriptOptions).GetAwaiter().GetResult();
                        }
                        variables.Add(new ScriptVariableSyntax(name, resolvedType, value, varAttr != null, varAttr?.PublicWrite ?? false));
                    }
                }
            }
        }

        // TODO: kiểm tra các properties là auto-property có đủ và getter và setter thì add vào variables
        var properties = classNode.Members.OfType<PropertyDeclarationSyntax>();
        foreach (var prop in properties)
        {
            // Kiểm tra có cả getter và setter
            var accessors = prop.AccessorList?.Accessors.ToList() ?? [];
            bool hasGetter = accessors?.Any(a => a.Kind() == SyntaxKind.GetAccessorDeclaration) == true;
            bool hasSetter = accessors?.Any(a => a.Kind() == SyntaxKind.SetAccessorDeclaration) == true;

            // Kiểm tra auto-property: cả getter và setter đều không có body và không phải expression-bodied
            bool isAutoProperty = hasGetter && hasSetter &&
                accessors!.All(a => a.Body == null && a.ExpressionBody == null) &&
                prop.ExpressionBody == null;

            if (isAutoProperty)
            {
                var name = prop.Identifier.Text;
                var type = semanticModel.ToSystemType(prop.Type) ?? throw new Exception($"Failed to resolve type for property: {prop.Type.ToFullString()}");
                // Giá trị mặc định của auto-property là default(T)
                object? value = type.IsValueType ? Activator.CreateInstance(type) : null;
                variables.Add(new ScriptVariableSyntax(name, type, value, false, false));
            }
        }
    }

    private static void GetScriptTasksAndMissions(ClassDeclarationSyntax classNode, SemanticModel semanticModel, out List<ScriptTaskData> tasks, out List<ScriptMissionData> missions)
    {
        tasks = [];
        missions = [];
        var methods = classNode.Members.OfType<MethodDeclarationSyntax>();
        foreach (var method in methods)
        {
            bool attrDone = false;
            foreach (var attrList in method.AttributeLists)
            {
                foreach (var attr in attrList.Attributes)
                {
                    var attrType = semanticModel.GetTypeAttribute(attr);
                    if (attrType == TaskAttributeType)
                    {
                        attrDone = true;

                        if (semanticModel.GetConstantAttribute(attr, attrType) is not RobotNet.Script.TaskAttribute taskAttr)
                        {
                            throw new Exception($"Failed to get TaskAttribute from method {method.Identifier.Text}");
                        }

                        // Check if method returns Task or Task<T>
                        var returnType = semanticModel.ToSystemType(method.ReturnType);
                        bool isTask = returnType == typeof(System.Threading.Tasks.Task) ||
                                      (returnType != null && returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(System.Threading.Tasks.Task<>));
                        bool isVoid = returnType == typeof(void);

                        if (method.ParameterList.Parameters.Count > 0 || !(isTask || isVoid))
                        {
                            throw new Exception($"Task Method {method.Identifier.Text} with TaskAttribute must have no parameters and return type void or Task.");
                        }

                        tasks.Add(new ScriptTaskData(method.Identifier.Text,
                            taskAttr.Interval,
                            taskAttr.AutoStart,
                            method.ToFullString(),
                            ExtractRelatedCodeForScriptRunner(classNode, method, $"{(isTask ? "await " : "")}{method.Identifier.Text}();")));
                        break;
                    }
                    else if (attrType == MisionAttributeType)
                    {
                        attrDone = true;
                        if (semanticModel.GetConstantAttribute(attr, attrType) is not RobotNet.Script.MissionAttribute missionAttr)
                        {
                            throw new Exception($"Failed to get MissionAttribute from method {method.Identifier.Text}");
                        }
                        var returnType = semanticModel.ToSystemType(method.ReturnType);
                        if (returnType is null || returnType != MisionReturnType)
                        {
                            throw new Exception($"Mission Method {method.Identifier.Text} with MissionAttribute must return type IEnumerator<MissionState>.");
                        }

                        var inputParameters = new List<string>();
                        var parameters = new List<ScriptMissionParameter>();
                        bool hasCancellationTokenParameter = false;
                        foreach (var param in method.ParameterList.Parameters)
                        {
                            if (param.Type is null)
                            {
                                throw new Exception($"Parameter {param.Identifier.Text} in method {method.Identifier.Text} has no type specified.");
                            }

                            var paramType = semanticModel.ToSystemType(param.Type) ?? throw new Exception($"Failed to resolve type for parameter {param.Identifier.Text} in method {method.Identifier.Text}");
                            if (!MissionParameterTypes.Contains(paramType))
                            {
                                throw new Exception($"Parameter type {param.Type} {param.Identifier.Text} in method mission {method.Identifier.Text} is not supported");
                            }

                            if (paramType == typeof(CancellationToken))
                            {
                                if (hasCancellationTokenParameter)
                                {
                                    throw new Exception($"Method {method.Identifier.Text} has multiple CancellationToken parameters, which is not allowed.");
                                }
                                hasCancellationTokenParameter = true;
                            }

                            // lấy default value nếu có
                            object? defaultValue = null;
                            if (param.Default is EqualsValueClauseSyntax equalsValue)
                            {
                                var constValue = semanticModel.GetConstantValue(equalsValue.Value);
                                if (constValue.HasValue)
                                {
                                    defaultValue = constValue.Value;
                                }
                            }

                            //inputParameters.Add($@"({paramType.FullName})parameters["""+param.Identifier.Text+"""]");
                            inputParameters.Add($@"({paramType.FullName})parameters[""{param.Identifier.Text}""]");
                            parameters.Add(new ScriptMissionParameter(param.Identifier.Text, paramType, defaultValue));
                        }

                        var execScript = $"return {method.Identifier.Text}({string.Join(", ", inputParameters)});";
                        missions.Add(new ScriptMissionData(method.Identifier.Text,
                                                                parameters,
                                                                method.ToFullString(),
                                                                ExtractRelatedCodeForScriptRunner(classNode, method, execScript),
                                                                missionAttr.IsMultipleRun));
                        break;
                    }

                }
                if (attrDone) break;
            }
        }
    }

    private static string ExtractRelatedCodeForScriptRunner(ClassDeclarationSyntax classNode, MethodDeclarationSyntax rootMethod, string execScript)
    {
        var allMethods = classNode.Members.OfType<MethodDeclarationSyntax>().ToList();
        var allFields = classNode.Members.OfType<FieldDeclarationSyntax>().ToList();
        var allProperties = classNode.Members.OfType<PropertyDeclarationSyntax>().ToList();
        var allNestedTypes = classNode.Members
            .Where(m => m is ClassDeclarationSyntax || m is StructDeclarationSyntax || m is InterfaceDeclarationSyntax || m is EnumDeclarationSyntax)
            .ToList();

        // 1. BFS: method + non-auto-property
        var usedMethodNames = new HashSet<string>();
        var usedPropertyNames = new HashSet<string>();
        var methodQueue = new Queue<MemberDeclarationSyntax>();
        var collectedMethods = new List<MethodDeclarationSyntax>();
        var collectedNonAutoProperties = new List<PropertyDeclarationSyntax>();

        methodQueue.Enqueue(rootMethod);

        while (methodQueue.Count > 0)
        {
            var member = methodQueue.Dequeue();
            if (member is MethodDeclarationSyntax method)
            {
                if (!usedMethodNames.Add(method.Identifier.Text))
                    continue;
                collectedMethods.Add(method);

                // Tìm các method/property được gọi trong method này
                var invokedNames = method.DescendantNodes()
                    .OfType<IdentifierNameSyntax>()
                    .Select(id => id.Identifier.Text)
                    .Distinct();

                foreach (var name in invokedNames)
                {
                    // Method
                    var nextMethod = allMethods.FirstOrDefault(m => m.Identifier.Text == name);
                    if (nextMethod != null && !usedMethodNames.Contains(name))
                        methodQueue.Enqueue(nextMethod);

                    // Property
                    var nextProp = allProperties.FirstOrDefault(p => p.Identifier.Text == name);
                    if (nextProp != null && !usedPropertyNames.Contains(name))
                        methodQueue.Enqueue(nextProp);
                }
            }
            else if (member is PropertyDeclarationSyntax prop)
            {
                if (!usedPropertyNames.Add(prop.Identifier.Text))
                    continue;

                // Auto-property: bỏ qua, sẽ xử lý sau
                var accessors = prop.AccessorList?.Accessors.ToList() ?? [];
                bool hasGetter = accessors.Any(a => a.Kind() == SyntaxKind.GetAccessorDeclaration);
                bool hasSetter = accessors.Any(a => a.Kind() == SyntaxKind.SetAccessorDeclaration);
                bool isAutoProperty = hasGetter && hasSetter &&
                    accessors.All(a => a.Body == null && a.ExpressionBody == null) &&
                    prop.ExpressionBody == null;

                if (isAutoProperty)
                    continue;

                collectedNonAutoProperties.Add(prop);

                // Tìm các method/property/field được gọi trong property này
                var invokedNames = prop.DescendantNodes()
                    .OfType<IdentifierNameSyntax>()
                    .Select(id => id.Identifier.Text)
                    .Distinct();

                foreach (var name in invokedNames)
                {
                    // Method
                    var nextMethod = allMethods.FirstOrDefault(m => m.Identifier.Text == name);
                    if (nextMethod != null && !usedMethodNames.Contains(name))
                        methodQueue.Enqueue(nextMethod);

                    // Property
                    var nextProp = allProperties.FirstOrDefault(p => p.Identifier.Text == name);
                    if (nextProp != null && !usedPropertyNames.Contains(name))
                        methodQueue.Enqueue(nextProp);
                }
            }
        }

        // 2. Collect all used member names (from all collected methods & non-auto-properties)
        var usedMemberNames = new HashSet<string>();
        foreach (var method in collectedMethods)
        {
            foreach (var id in method.DescendantNodes().OfType<IdentifierNameSyntax>().Select(id => id.Identifier.Text))
                usedMemberNames.Add(id);
        }
        foreach (var prop in collectedNonAutoProperties)
        {
            foreach (var id in prop.DescendantNodes().OfType<IdentifierNameSyntax>().Select(id => id.Identifier.Text))
                usedMemberNames.Add(id);
        }

        // 3. Collect fields
        var relatedFields = new List<string>();
        foreach (var field in allFields)
        {
            foreach (var variable in field.Declaration.Variables)
            {
                if (usedMemberNames.Contains(variable.Identifier.Text))
                {
                    var varType = field.Declaration.Type.ToString();
                    var varName = variable.Identifier.Text;
                    var propertyCode = $@"public {varType} {varName}
{{
    get => ({varType})globals[""{varName}""];
    set => globals[""{varName}""] = value;
}}";
                    relatedFields.Add(propertyCode.Trim());
                }
            }
        }

        // 4. Collect auto-properties
        var relatedAutoProperties = new List<string>();
        foreach (var prop in allProperties)
        {
            var accessors = prop.AccessorList?.Accessors.ToList() ?? [];
            bool hasGetter = accessors.Any(a => a.Kind() == SyntaxKind.GetAccessorDeclaration);
            bool hasSetter = accessors.Any(a => a.Kind() == SyntaxKind.SetAccessorDeclaration);
            bool isAutoProperty = hasGetter && hasSetter &&
                accessors.All(a => a.Body == null && a.ExpressionBody == null) &&
                prop.ExpressionBody == null;

            if (isAutoProperty && (usedMemberNames.Contains(prop.Identifier.Text) || usedPropertyNames.Contains(prop.Identifier.Text)))
            {
                var propType = prop.Type.ToString();
                var propName = prop.Identifier.Text;
                var propertyCode = $@"public {propType} {propName}
{{
    get => ({propType})globals[""{propName}""];
    set => globals[""{propName}""] = value;
}}";
                relatedAutoProperties.Add(propertyCode.Trim());
            }
        }

        // 5. Collect nested types if referenced
        var usedNestedTypeNames = new HashSet<string>(usedMemberNames);
        var relatedNestedTypes = allNestedTypes
            .Where(nt =>
            {
                if (nt is BaseTypeDeclarationSyntax btd)
                    return usedNestedTypeNames.Contains(btd.Identifier.Text);
                return false;
            })
            .Select(nt => nt.NormalizeWhitespace().ToFullString())
            .ToList();

        // 6. Compose the script
        var sb = new StringBuilder();
        sb.AppendLine(ScriptConfiguration.RuntimeGlobalsScript);
        foreach (var nt in relatedNestedTypes)
            sb.AppendLine(nt);
        foreach (var f in relatedFields)
            sb.AppendLine(f);
        foreach (var p in relatedAutoProperties)
            sb.AppendLine(p);
        foreach (var p in collectedNonAutoProperties)
            sb.AppendLine(p.NormalizeWhitespace().ToFullString());
        foreach (var m in collectedMethods)
            sb.AppendLine(m.NormalizeWhitespace().ToFullString());

        sb.AppendLine(execScript);
        return sb.ToString();
    }

    private static Type? GetTypeAttribute(this SemanticModel semanticModel, AttributeSyntax attrSynctax)
    {
        var typeInfo = semanticModel.GetTypeInfo(attrSynctax);
        if (typeInfo.Type is null) return null;

        string metadataName = typeInfo.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "");
        return ResolveTypeFromString(metadataName);
    }

    private static object? GetConstantAttribute(this SemanticModel semanticModel, AttributeSyntax attrSynctax, Type type)
    {
        var args = new List<object?>();

        foreach (var arg in attrSynctax.ArgumentList?.Arguments ?? default)
        {
            var constValue = semanticModel.GetConstantValue(arg.Expression);
            if (constValue.HasValue)
                args.Add(constValue.Value);
            else
                args.Add(null); // fallback nếu không phân giải được
        }

        // Find the constructor with the same number or more parameters (with optional)
        var ctors = type.GetConstructors();
        foreach (var ctor in ctors)
        {
            var parameters = ctor.GetParameters();
            if (args.Count <= parameters.Length)
            {
                // Fill missing optional parameters with their default values
                var finalArgs = args.ToList();
                for (int i = args.Count; i < parameters.Length; i++)
                {
                    if (parameters[i].IsOptional)
                        finalArgs.Add(parameters[i].DefaultValue);
                    else
                        goto NextCtor; // Not enough arguments and not optional
                }
                return ctor.Invoke([.. finalArgs]);
            }
        NextCtor:;
        }

        return null;
    }

    private static Type? ToSystemType(this SemanticModel semanticModel, TypeSyntax typeSyntax)
    {
        var typeSymbol = semanticModel.GetTypeInfo(typeSyntax).Type;
        if (typeSymbol is null) return null;

        if (typeSyntax is PredefinedTypeSyntax predefinedType
            && predefinedMap.TryGetValue(predefinedType.Keyword.Text, out var systemType))
        {
            return systemType;
        }

        string metadataName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "");
        return metadataName.Equals("void") ? typeof(void) : ResolveTypeFromString(metadataName);
    }

    private static Type? ResolveTypeFromString(string typeString)
    {
        typeString = typeString.Trim();

        // Handle array types (e.g., System.Int32[], string[], List<int>[])
        if (typeString.EndsWith("[]"))
        {
            var elementTypeString = typeString[..^2].Trim();
            var elementType = ResolveTypeFromString(elementTypeString);
            return elementType?.MakeArrayType();
        }

        // Trường hợp không phải generic
        if (!typeString.Contains('<'))
        {
            return FindType(typeString);
        }

        // Tách phần generic
        var match = Regex.Match(typeString, @"^(?<raw>[^<]+)<(?<args>.+)>$");
        if (!match.Success)
            return null;

        var genericTypeName = match.Groups["raw"].Value;
        var genericArgsString = match.Groups["args"].Value;

        // Phân tách các generic argument (xử lý nested generics)
        var genericArgs = SplitGenericArguments(genericArgsString);

        var genericType = FindType(genericTypeName + "`" + genericArgs.Count);
        if (genericType == null)
            return null;

        var resolvedArgs = genericArgs.Select(ResolveTypeFromString).ToArray();
        if (resolvedArgs.Any(t => t == null))
            return null;

        return genericType.MakeGenericType(resolvedArgs!);
    }

    private static List<string> SplitGenericArguments(string input)
    {
        var args = new List<string>();
        var sb = new StringBuilder();
        int depth = 0;

        foreach (char c in input)
        {
            if (c == ',' && depth == 0)
            {
                args.Add(sb.ToString().Trim());
                sb.Clear();
            }
            else
            {
                if (c == '<') depth++;
                else if (c == '>') depth--;
                sb.Append(c);
            }
        }

        if (sb.Length > 0)
        {
            args.Add(sb.ToString().Trim());
        }

        return args;
    }

    private static Type? FindType(string typeName)
    {
        if (predefinedMap.TryGetValue(typeName, out var systemType)) return systemType;

        return AppDomain.CurrentDomain
                        .GetAssemblies()
                        .Select(a => a.GetType(typeName, false))
                        .FirstOrDefault(t => t != null);
    }


    public static string ToTypeString(Type type)
    {
        if (type == typeof(void))
            return "void";

        if (type.IsGenericType)
        {
            var genericTypeName = type.GetGenericTypeDefinition().FullName;
            if (genericTypeName == null)
                return type.Name;

            var backtickIndex = genericTypeName.IndexOf('`');
            if (backtickIndex > 0)
                genericTypeName = genericTypeName.Substring(0, backtickIndex);

            var genericArgs = type.GetGenericArguments();
            var argsString = string.Join(", ", genericArgs.Select(ToTypeString));
            return $"{genericTypeName}<{argsString}>";
        }

        return type.FullName ?? type.Name;
    }

    private static readonly Dictionary<string, Type> predefinedMap = new()
    {
        ["bool"] = typeof(bool),
        ["byte"] = typeof(byte),
        ["sbyte"] = typeof(sbyte),
        ["short"] = typeof(short),
        ["ushort"] = typeof(ushort),
        ["int"] = typeof(int),
        ["uint"] = typeof(uint),
        ["long"] = typeof(long),
        ["ulong"] = typeof(ulong),
        ["float"] = typeof(float),
        ["double"] = typeof(double),
        ["decimal"] = typeof(decimal),
        ["char"] = typeof(char),
        ["string"] = typeof(string),
        ["object"] = typeof(object)
    };

    private static readonly Type TaskAttributeType = typeof(RobotNet.Script.TaskAttribute);
    private static readonly Type MisionAttributeType = typeof(RobotNet.Script.MissionAttribute);
    private static readonly Type MisionReturnType = typeof(IAsyncEnumerable<MissionState>);
    private static readonly Type VariableAttributeType = typeof(VariableAttribute);

    private static readonly Type RobotType = typeof(RobotNet.Script.IRobot);
    private static readonly ImmutableArray<Type> MissionParameterTypes = [
        typeof(bool),
        typeof(byte),
        typeof(sbyte),
        typeof(short),
        typeof(ushort),
        typeof(int),
        typeof(uint),
        typeof(long),
        typeof(ulong),
        typeof(float),
        typeof(double),
        typeof(decimal),
        typeof(char),
        typeof(string),
        typeof(CancellationToken),
    ];
}
