using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;

public sealed class GatewayApiKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public const string SchemeName = "GatewayApiKey";

    private readonly IConfiguration _config;

    public GatewayApiKeyAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        IConfiguration config)
        : base(options, logger, encoder) => _config = config;

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Request.Headers.TryGetValue("X-Api-Key", out var suppliedHeader))
            return Task.FromResult(AuthenticateResult.NoResult());

        var configured = _config["ApiKey:Gateway"];
        if (string.IsNullOrEmpty(configured))
            return Task.FromResult(AuthenticateResult.Fail("Gateway API key not configured."));

        if (!FixedTimeEquals(suppliedHeader.ToString(), configured))
            return Task.FromResult(AuthenticateResult.Fail("Invalid API key."));

        var claims = new List<Claim> { new("client_type", "gateway") };

        if (Request.Headers.TryGetValue("X-Gateway-Id", out var gatewayIdHeader)
            && Guid.TryParse(gatewayIdHeader.ToString(), out _))
            claims.Add(new Claim("gateway_id", gatewayIdHeader.ToString()!));

        var identity = new ClaimsIdentity(claims, SchemeName);
        var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), SchemeName);
        return Task.FromResult(AuthenticateResult.Success(ticket));
    }

    private static bool FixedTimeEquals(string supplied, string configured)
    {
        var suppliedBytes = Encoding.UTF8.GetBytes(supplied);
        var configuredBytes = Encoding.UTF8.GetBytes(configured);
        return CryptographicOperations.FixedTimeEquals(suppliedBytes, configuredBytes);
    }
}
