using System.IdentityModel.Tokens.Jwt;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;

namespace Health.Infrastructure.Services;

/// <summary>
/// Apple IdentityToken 验证：下载 Apple 公钥、验签、检查声明
/// </summary>
public sealed class AppleTokenValidator
{
    private const string AppleKeysUrl = "https://appleid.apple.com/auth/keys";
    private const string AppleIssuer = "https://appleid.apple.com";
    private static readonly HttpClient _http = new();
    private static List<JsonWebKey>? _cachedKeys;
    private static DateTime _keysExpiry = DateTime.MinValue;
    private static readonly SemaphoreSlim _lock = new(1, 1);

    private readonly string _clientId;

    public AppleTokenValidator(IConfiguration config)
    {
        _clientId = config["APPLE_CLIENT_ID"] ?? "com.datalumina.YYA";
    }

    /// <summary>
    /// 验证 Apple identityToken，返回 sub（用户唯一标识）
    /// </summary>
    public async Task<string> ValidateAsync(string identityToken, CancellationToken ct)
    {
        var handler = new JwtSecurityTokenHandler();
        // 先解码获取 kid
        var rawToken = handler.ReadJwtToken(identityToken);
        var kid = rawToken.Header.Kid;

        var keys = await GetKeysAsync(ct);
        var key = keys.FirstOrDefault(k => k.KeyId == kid)
                  ?? throw new SecurityTokenException($"No matching key for kid: {kid}");

        var validationParams = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = AppleIssuer,
            ValidateAudience = true,
            ValidAudiences = [_clientId, "com.datalumina.YYA.signin"],
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = key,
            ClockSkew = TimeSpan.FromMinutes(1),
        };

        handler.ValidateToken(identityToken, validationParams, out _);
        // 从原始 JWT 取 sub（避免 .NET 的 ClaimType 映射把 "sub" 转成 NameIdentifier）
        var sub = rawToken.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
        if (string.IsNullOrWhiteSpace(sub))
            throw new SecurityTokenException("Missing 'sub' claim in Apple identityToken");

        return sub;
    }

    private static async Task<List<JsonWebKey>> GetKeysAsync(CancellationToken ct)
    {
        if (_cachedKeys != null && DateTime.UtcNow < _keysExpiry) return _cachedKeys;

        await _lock.WaitAsync(ct);
        try
        {
            if (_cachedKeys != null && DateTime.UtcNow < _keysExpiry) return _cachedKeys;

            var response = await _http.GetStringAsync(AppleKeysUrl, ct);
            var jwks = new JsonWebKeySet(response);
            _cachedKeys = jwks.Keys.ToList();
            _keysExpiry = DateTime.UtcNow.AddHours(6); // 缓存 6 小时
            return _cachedKeys;
        }
        finally { _lock.Release(); }
    }
}
