using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace DiunaBI.API.Attributes;

/// <summary>
/// Authorization attribute that validates API key from X-API-Key header.
/// Uses constant-time comparison to prevent timing attacks.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ApiKeyAuthAttribute : Attribute, IAuthorizationFilter
{
    private const string ApiKeyHeaderName = "X-API-Key";

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var configuration = context.HttpContext.RequestServices.GetRequiredService<IConfiguration>();
        var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ApiKeyAuthAttribute>>();

        // Get expected API key from configuration
        var expectedApiKey = configuration["apiKey"];
        if (string.IsNullOrEmpty(expectedApiKey))
        {
            logger.LogError("API key not configured in appsettings");
            context.Result = new StatusCodeResult(StatusCodes.Status500InternalServerError);
            return;
        }

        // Get API key from header
        if (!context.HttpContext.Request.Headers.TryGetValue(ApiKeyHeaderName, out var extractedApiKey))
        {
            logger.LogWarning("API key missing from request header");
            context.Result = new UnauthorizedObjectResult(new { error = "API key is required" });
            return;
        }

        // Constant-time comparison to prevent timing attacks
        if (!IsApiKeyValid(extractedApiKey!, expectedApiKey))
        {
            logger.LogWarning("Invalid API key provided from {RemoteIp}", context.HttpContext.Connection.RemoteIpAddress);
            context.Result = new UnauthorizedObjectResult(new { error = "Invalid API key" });
            return;
        }

        // API key is valid - allow the request to proceed
    }

    /// <summary>
    /// Constant-time string comparison to prevent timing attacks.
    /// </summary>
    private static bool IsApiKeyValid(string providedKey, string expectedKey)
    {
        if (providedKey == null || expectedKey == null)
            return false;

        var providedBytes = Encoding.UTF8.GetBytes(providedKey);
        var expectedBytes = Encoding.UTF8.GetBytes(expectedKey);

        return CryptographicOperations.FixedTimeEquals(providedBytes, expectedBytes);
    }
}
