using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using Nalu.Web.Data.Repositories;

namespace Nalu.Web.Infrastructure;

public static class ApiKeyAuthScheme
{
    public const string Name = "ApiKey";
}

public record ApiKeyConfig
{
    public required string Key { get; init; }
    public required string Plan { get; init; }
    public required string Owner { get; init; }
}

public class NaluAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly ApiKeyRepository _apiKeyRepo;
    private readonly List<ApiKeyConfig> _configKeys;

    public NaluAuthHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ApiKeyRepository apiKeyRepo,
        IConfiguration config)
        : base(options, logger, encoder)
    {
        _apiKeyRepo = apiKeyRepo;
        _configKeys = config.GetSection("ApiKeys").Get<List<ApiKeyConfig>>() ?? [];
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var authHeader = Request.Headers.Authorization.FirstOrDefault();

        if (authHeader is null || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
            return AuthenticateResult.NoResult();

        var key = authHeader["Bearer ".Length..].Trim();

        string? plan;
        string? owner;

        // 1. Try MongoDB first
        var dbKey = await _apiKeyRepo.FindAsync(key, Context.RequestAborted);
        if (dbKey is not null)
        {
            plan = dbKey.Plan;
            owner = dbKey.Owner;
            // Fire-and-forget touch — don't block request for this
            _ = _apiKeyRepo.TouchAsync(key);
        }
        else
        {
            // 2. Fallback to config (test keys, bootstrap)
            var cfgKey = _configKeys.FirstOrDefault(k => k.Key == key);
            if (cfgKey is null)
                return AuthenticateResult.Fail("Invalid API key");

            plan = cfgKey.Plan;
            owner = cfgKey.Owner;
        }

        var claims = new[]
        {
            new Claim("api_key", key),
            new Claim("plan", plan),
            new Claim("owner", owner)
        };

        var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
        var ticket = new AuthenticationTicket(principal, Scheme.Name);

        return AuthenticateResult.Success(ticket);
    }
}
