using Bunit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.JSInterop;
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Shared;

/// <summary>
/// Regression tests for CentralUI-018. <c>MonacoEditor</c> wrapped every JS
/// interop call in a bare <c>try { ... } catch { }</c> with no logging — a
/// genuine Monaco init failure became invisible. The fix narrows the catch to
/// the expected prerender / disconnect cases and logs any real
/// <see cref="JSException"/> via <c>ILogger</c>.
/// </summary>
public class MonacoEditorLoggingTests : BunitContext
{
    /// <summary>Captures log entries so the test can assert on them.</summary>
    private sealed class CapturingLoggerProvider : ILoggerProvider
    {
        public List<(LogLevel Level, string Message, Exception? Exception)> Entries { get; } = new();

        public ILogger CreateLogger(string categoryName) => new CapturingLogger(Entries);
        public void Dispose() { }

        private sealed class CapturingLogger : ILogger
        {
            private readonly List<(LogLevel, string, Exception?)> _entries;
            public CapturingLogger(List<(LogLevel, string, Exception?)> entries) => _entries = entries;

            public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
            public bool IsEnabled(LogLevel logLevel) => true;

            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
                Exception? exception, Func<TState, Exception?, string> formatter)
                => _entries.Add((logLevel, formatter(state, exception), exception));
        }
    }

    [Fact]
    public void CreateEditor_GenuineJsException_IsLogged_NotSwallowed()
    {
        var provider = new CapturingLoggerProvider();
        Services.AddLogging(b => b.AddProvider(provider));

        // createEditor is an InvokeVoidAsync call — configure it to throw a
        // genuine JSException so we exercise the real-failure path.
        JSInterop.Mode = JSRuntimeMode.Strict;
        JSInterop.SetupVoid("MonacoBlazor.createEditor", _ => true)
            .SetException(new JSException("Monaco failed to load"));

        // Pre-fix: the bare catch {} swallowed this with no trace. Post-fix:
        // the component renders fine but the failure is logged.
        var cut = Render<MonacoEditor>(p => p.Add(c => c.ShowToolbar, false));

        var errors = provider.Entries.Where(e => e.Level == LogLevel.Error).ToList();
        Assert.NotEmpty(errors);
        Assert.Contains(errors, e => e.Exception is JSException);
    }

    [Fact]
    public void CreateEditor_Prerender_DoesNotLog()
    {
        // When JS interop is unavailable (prerender), createEditor throws
        // InvalidOperationException — that is expected and must NOT be logged.
        var provider = new CapturingLoggerProvider();
        Services.AddLogging(b => b.AddProvider(provider));

        JSInterop.Mode = JSRuntimeMode.Strict;
        JSInterop.SetupVoid("MonacoBlazor.createEditor", _ => true)
            .SetException(new InvalidOperationException("JS interop not available during prerender"));

        var cut = Render<MonacoEditor>(p => p.Add(c => c.ShowToolbar, false));

        Assert.DoesNotContain(provider.Entries, e => e.Level >= LogLevel.Warning);
    }
}
