using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

public class ScriptLoader
{
    private AssemblyLoadContext _lastContext;

    public void LoadAndExecuteScripts(string folderPath)
    {
        // 如果有旧的上下文，卸载它
        if (_lastContext != null)
        {
            _lastContext.Unload();
            _lastContext = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        var csFiles = Directory.GetFiles(folderPath, "*.cs", SearchOption.AllDirectories);

        if (!csFiles.Any())
        {
            Console.WriteLine("没有找到 .cs 文件");
            return;
        }

        var syntaxTrees = csFiles.Select(file =>
            CSharpSyntaxTree.ParseText(File.ReadAllText(file))
        );

        var references = AppDomain.CurrentDomain.GetAssemblies()
            .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location))
            .Select(a => MetadataReference.CreateFromFile(a.Location))
            .Cast<MetadataReference>();

        // 每次给程序集一个唯一名字
        string asmName = $"DynamicScripts_{Guid.NewGuid():N}";

        var compilation = CSharpCompilation.Create(
            assemblyName: asmName,
            syntaxTrees: syntaxTrees,
            references: references,
            options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
        );

        using var ms = new MemoryStream();
        var result = compilation.Emit(ms);

        if (!result.Success)
        {
            foreach (var diag in result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error))
                Console.WriteLine(diag.ToString());
            return;
        }

        ms.Seek(0, SeekOrigin.Begin);

        // 用可卸载的 AssemblyLoadContext 加载
        var alc = new AssemblyLoadContext(asmName, isCollectible: true);
        var assembly = alc.LoadFromStream(ms);
        _lastContext = alc;

        var type = assembly.GetType("MyNamespace.MyClass");
        var method = type?.GetMethod("Run");
        method?.Invoke(null, null);
    }
}
