using Flyshot.Server.Host.Middleware;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace Flyshot.Server.IntegrationTests;

/// <summary>
/// HTTP 请求响应日志中间件测试。
/// </summary>
public sealed class RequestResponseLoggingMiddlewareTests
{
    /// <summary>
    /// 高频状态快照路径命中忽略前缀时，不应写入请求和响应日志。
    /// </summary>
    [Fact]
    public async Task InvokeAsync_WhenPathMatchesIgnoredPrefix_DoesNotWriteRequestResponseLogs()
    {
        var logger = new CapturingLogger<RequestResponseLoggingMiddleware>();
        var nextWasCalled = false;
        var middleware = new RequestResponseLoggingMiddleware(
            async context =>
            {
                nextWasCalled = true;
                context.Response.StatusCode = StatusCodes.Status200OK;
                await context.Response.WriteAsync("ok");
            },
            logger);
        var context = new DefaultHttpContext();
        context.Request.Method = HttpMethods.Get;
        context.Request.Path = "/api/status/snapshot/current";
        context.Response.Body = new MemoryStream();

        await middleware.InvokeAsync(context);

        Assert.True(nextWasCalled);
        Assert.Empty(logger.Entries);
    }

    /// <summary>
    /// 捕获中间件写出的日志条目，避免测试依赖真实 NLog 目标。
    /// </summary>
    private sealed class CapturingLogger<T> : ILogger<T>
    {
        /// <summary>
        /// 已捕获的日志条目。
        /// </summary>
        public List<LogEntry> Entries { get; } = new();

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

        /// <inheritdoc />
        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        /// <inheritdoc />
        public void Log<TState>(
            LogLevel logLevel,
            EventId eventId,
            TState state,
            Exception? exception,
            Func<TState, Exception?, string> formatter)
        {
            Entries.Add(new LogEntry(logLevel, formatter(state, exception)));
        }
    }

    /// <summary>
    /// 测试用日志条目。
    /// </summary>
    /// <param name="Level">日志级别。</param>
    /// <param name="Message">格式化后的日志消息。</param>
    private sealed record LogEntry(LogLevel Level, string Message);
}
