using Microsoft.AspNetCore.Http;
using Predictalytics.Api.Security;

namespace Predictalytics.Application.Tests.Hosting;

/// <summary>
/// Der Filter ist das Einzige, was die schreibenden Endpunkte schuetzt, sobald der
/// Dienst ueber das Netz erreichbar ist.
/// </summary>
public class ApiTokenFilterTests
{
    private const string Token = "s3cr3t-token";

    [Fact]
    public async Task Correct_token_passes_through()
    {
        var (context, next, called) = Build(Token);
        var result = await new ApiTokenFilter(Token).InvokeAsync(context, next);

        Assert.True(called.Value);
        Assert.Equal("weiter", result);
    }

    [Theory]
    [InlineData("")]                 // Header fehlt
    [InlineData("falsch")]
    [InlineData("s3cr3t-toke")]      // ein Zeichen zu kurz
    [InlineData("s3cr3t-tokenX")]    // ein Zeichen zu lang
    [InlineData("S3CR3T-TOKEN")]     // Schreibweise muss zaehlen
    [InlineData(" s3cr3t-token")]    // fuehrender Leerraum
    public async Task Wrong_or_missing_token_is_rejected(string provided)
    {
        var (context, next, called) = Build(provided);
        var result = await new ApiTokenFilter(Token).InvokeAsync(context, next);

        Assert.False(called.Value);
        Assert.NotNull(result);
        Assert.NotEqual("weiter", result);
    }

    private static (DefaultEndpointFilterInvocationContext Context, EndpointFilterDelegate Next, StrongBox<bool> Called)
        Build(string headerValue)
    {
        var httpContext = new DefaultHttpContext();
        if (headerValue.Length > 0)
        {
            httpContext.Request.Headers[ApiTokenFilter.HeaderName] = headerValue;
        }

        var called = new StrongBox<bool>(false);
        EndpointFilterDelegate next = _ =>
        {
            called.Value = true;
            return ValueTask.FromResult<object?>("weiter");
        };

        return (new DefaultEndpointFilterInvocationContext(httpContext), next, called);
    }

    private sealed class StrongBox<T>(T value)
    {
        public T Value { get; set; } = value;
    }
}
