using Microsoft.Extensions.Logging.Abstractions;
using ScadaLink.SiteRuntime.Scripts;

namespace ScadaLink.SiteRuntime.Tests.Scripts;

/// <summary>
/// WP-17: Shared Script Library tests — compile, register, execute inline.
/// </summary>
public class SharedScriptLibraryTests
{
    private readonly ScriptCompilationService _compilationService;
    private readonly SharedScriptLibrary _library;

    public SharedScriptLibraryTests()
    {
        _compilationService = new ScriptCompilationService(
            NullLogger<ScriptCompilationService>.Instance);
        _library = new SharedScriptLibrary(
            _compilationService, NullLogger<SharedScriptLibrary>.Instance);
    }

    [Fact]
    public void CompileAndRegister_ValidScript_Succeeds()
    {
        var result = _library.CompileAndRegister("add", "1 + 2");
        Assert.True(result);
        Assert.True(_library.Contains("add"));
    }

    [Fact]
    public void CompileAndRegister_InvalidScript_ReturnsFalse()
    {
        var result = _library.CompileAndRegister("bad", "this is not valid {{{");
        Assert.False(result);
        Assert.False(_library.Contains("bad"));
    }

    [Fact]
    public void CompileAndRegister_ForbiddenApi_ReturnsFalse()
    {
        var result = _library.CompileAndRegister("evil", "System.IO.File.Delete(\"/tmp\")");
        Assert.False(result);
    }

    [Fact]
    public void CompileAndRegister_Replaces_ExistingScript()
    {
        _library.CompileAndRegister("calc", "1 + 1");
        _library.CompileAndRegister("calc", "2 + 2");

        Assert.True(_library.Contains("calc"));
        // Should have only one entry
        Assert.Equal(1, _library.GetRegisteredScriptNames().Count(n => n == "calc"));
    }

    [Fact]
    public void Remove_RegisteredScript_ReturnsTrue()
    {
        _library.CompileAndRegister("temp", "42");
        Assert.True(_library.Remove("temp"));
        Assert.False(_library.Contains("temp"));
    }

    [Fact]
    public void Remove_NonexistentScript_ReturnsFalse()
    {
        Assert.False(_library.Remove("nonexistent"));
    }

    [Fact]
    public void GetRegisteredScriptNames_ReturnsAllNames()
    {
        _library.CompileAndRegister("a", "1");
        _library.CompileAndRegister("b", "2");
        _library.CompileAndRegister("c", "3");

        var names = _library.GetRegisteredScriptNames();
        Assert.Equal(3, names.Count);
        Assert.Contains("a", names);
        Assert.Contains("b", names);
        Assert.Contains("c", names);
    }

    [Fact]
    public async Task ExecuteAsync_NonexistentScript_Throws()
    {
        await Assert.ThrowsAsync<InvalidOperationException>(
            () => _library.ExecuteAsync("missing", null!));
    }
}
