using Microsoft.Extensions.Logging;

namespace ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests;

/// <summary>
///     Captures what the driver logs, so that "and it says so" can be asserted rather than assumed.
/// </summary>
/// <remarks>
///     Used where the log line <b>is</b> the behaviour: a browse scope that matches nothing, and a
///     subscription the driver silently refuses to serve. Both are cases whose only other symptom is
///     an operator staring at an empty panel, so a fix that emits nothing is not a fix.
/// </remarks>
internal sealed class RecordingDriverLogger : ILogger<MTConnectDriver>
{
    /// <summary>Formatted <see cref="LogLevel.Warning"/> lines, in order.</summary>
    public List<string> Warnings { get; } = [];

    /// <summary>Formatted <see cref="LogLevel.Error"/> lines, in order.</summary>
    public List<string> Errors { get; } = [];

    /// <inheritdoc/>
    public IDisposable? BeginScope<TState>(TState state)
        where TState : notnull => null;

    /// <inheritdoc/>
    public bool IsEnabled(LogLevel logLevel) => true;

    /// <inheritdoc/>
    public void Log<TState>(
        LogLevel logLevel,
        EventId eventId,
        TState state,
        Exception? exception,
        Func<TState, Exception?, string> formatter)
    {
        ArgumentNullException.ThrowIfNull(formatter);

        var sink = logLevel switch
        {
            LogLevel.Warning => Warnings,
            LogLevel.Error => Errors,
            _ => null,
        };

        // Locked: the /sample pump logs from its own task while the test thread reads.
        if (sink is null)
        {
            return;
        }

        lock (sink)
        {
            sink.Add(formatter(state, exception));
        }
    }

    /// <summary>A snapshot copy of <see cref="Warnings"/>, safe to enumerate while the pump runs.</summary>
    public IReadOnlyList<string> WarningsSnapshot()
    {
        lock (Warnings)
        {
            return [.. Warnings];
        }
    }
}
