using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.IdentityModel.Tokens;

namespace MiGu.Server.Auth;

/// <summary>
/// 平台 JWT 颁发与验签。Secret 从 <c>appsettings.json</c> 的 <c>Jwt:Secret</c> 读取；
/// 若为占位值 <c>"REPLACE_ME"</c> 则启动期生成临时随机 secret 并在日志里强制告警，
/// 防止生产部署忘改 secret 导致历史 hardcoded "simple-mock-secret" 风险复现。
/// </summary>
public sealed class JwtIssuer
{
    /// <summary>占位 secret；启动期检测到时自动换成进程随机值，并在日志里 critical 告警。</summary>
    public const string PlaceholderSecret = "REPLACE_ME";

    public string Issuer { get; }
    public string Audience { get; }
    public TimeSpan Lifetime { get; }
    public string SecretInUse { get; }
    public bool SecretIsEphemeral { get; }

    private readonly SymmetricSecurityKey _key;
    private readonly SigningCredentials _signing;
    private readonly TokenValidationParameters _validation;

    public JwtIssuer(string secret, string issuer, string audience, TimeSpan lifetime, ILogger<JwtIssuer> logger)
    {
        Issuer = issuer;
        Audience = audience;
        Lifetime = lifetime;

        if (string.IsNullOrWhiteSpace(secret) || secret == PlaceholderSecret)
        {
            // 兜底：用进程随机 secret，保证签名不被预知，但 token 在 MiGu.Server 重启后失效。
            // 这条路径只应出现在「开发机首次启动」/「忘改配置的部署」，**日志里强制告警让运维注意**。
            secret = RandomBase64Secret(64);
            SecretIsEphemeral = true;
            logger.LogCritical(
                "Jwt:Secret 是占位值或未配置 —— 已生成进程随机 secret（重启后所有 token 失效）。" +
                "生产环境必须在 appsettings.Production.json 或环境变量 PLATFORM__JWT__SECRET 配置一个 ≥ 32 字节的稳定 secret。");
        }
        else if (Encoding.UTF8.GetByteCount(secret) < 32)
        {
            logger.LogWarning("Jwt:Secret 长度不足 32 字节，HS256 推荐 ≥ 32 字节 secret 以达到 256bit 强度。");
        }

        SecretInUse = secret;
        _key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
        _signing = new SigningCredentials(_key, SecurityAlgorithms.HmacSha256);
        _validation = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateIssuerSigningKey = true,
            ValidateLifetime = true,
            ValidIssuer = Issuer,
            ValidAudience = Audience,
            IssuerSigningKey = _key,
            ClockSkew = TimeSpan.FromSeconds(30),
        };
    }

    /// <summary>颁发 access token。<paramref name="ops"/> 写入私有 claim <c>ops</c>（空格分隔，便于后端 <c>[Authorize]</c> policy 解析）。</summary>
    public string Issue(string userId, string username, string scope, IReadOnlyList<string> roles, IReadOnlyList<string> ops)
    {
        var now = DateTime.UtcNow;
        var claims = new List<Claim>
        {
            new(JwtRegisteredClaimNames.Sub, userId),
            new(JwtRegisteredClaimNames.UniqueName, username),
            new("scope", scope),
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")),
            new(JwtRegisteredClaimNames.Iat, new DateTimeOffset(now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
            new("ops", string.Join(' ', ops ?? Array.Empty<string>())),
        };
        foreach (var r in roles ?? Array.Empty<string>())
            claims.Add(new Claim(ClaimTypes.Role, r));

        var token = new JwtSecurityToken(
            issuer: Issuer,
            audience: Audience,
            claims: claims,
            notBefore: now,
            expires: now.Add(Lifetime),
            signingCredentials: _signing);
        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    /// <summary>给框架的 JwtBearer middleware 用的验证参数（启动期注入到 AddJwtBearer）。</summary>
    public TokenValidationParameters BuildValidationParameters() => _validation;

    private static string RandomBase64Secret(int byteLen)
    {
        var buf = RandomNumberGenerator.GetBytes(byteLen);
        return Convert.ToBase64String(buf);
    }
}
