using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using TakeoutSaaS.Application.Identity.Abstractions;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Infrastructure.Identity.Options;

namespace TakeoutSaaS.Infrastructure.Identity.Services;

/// <summary>
/// JWT 令牌生成器。
/// </summary>
public sealed class JwtTokenService(IRefreshTokenStore refreshTokenStore, IOptions<JwtOptions> options) : IJwtTokenService
{
    private readonly JwtSecurityTokenHandler _tokenHandler = new();
    private readonly JwtOptions _options = options.Value;

    /// <summary>
    /// 创建访问令牌和刷新令牌对。
    /// </summary>
    /// <param name="profile">用户档案</param>
    /// <param name="isNewUser">是否为新用户（首次登录）</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>令牌响应</returns>
    public async Task<TokenResponse> CreateTokensAsync(CurrentUserProfile profile, bool isNewUser = false, CancellationToken cancellationToken = default)
    {
        var now = DateTime.UtcNow;
        var accessExpires = now.AddMinutes(_options.AccessTokenExpirationMinutes);
        var refreshExpires = now.AddMinutes(_options.RefreshTokenExpirationMinutes);

        // 1. 构建 JWT Claims（包含用户 ID、账号、租户 ID、商户 ID、角色、权限等）
        var claims = BuildClaims(profile);

        // 2. 创建签名凭据（使用 HMAC SHA256 算法）
        var signingCredentials = new SigningCredentials(
            new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Secret)),
            SecurityAlgorithms.HmacSha256);

        // 3. 创建 JWT 安全令牌
        var jwt = new JwtSecurityToken(
            issuer: _options.Issuer,
            audience: _options.Audience,
            claims: claims,
            notBefore: now,
            expires: accessExpires,
            signingCredentials: signingCredentials);

        // 4. 序列化 JWT 为字符串
        var accessToken = _tokenHandler.WriteToken(jwt);

        // 5. 生成刷新令牌并存储到 Redis
        var refreshDescriptor = await refreshTokenStore.IssueAsync(profile.UserId, refreshExpires, cancellationToken);

        return new TokenResponse
        {
            AccessToken = accessToken,
            AccessTokenExpiresAt = accessExpires,
            RefreshToken = refreshDescriptor.Token,
            RefreshTokenExpiresAt = refreshDescriptor.ExpiresAt,
            User = profile,
            IsNewUser = isNewUser
        };
    }

    /// <summary>
    /// 构建 JWT Claims：将用户档案转换为 Claims 集合。
    /// </summary>
    /// <param name="profile">用户档案</param>
    /// <returns>Claims 集合</returns>
    private static List<Claim> BuildClaims(CurrentUserProfile profile)
    {
        var userId = profile.UserId.ToString();
        var claims = new List<Claim>
        {
            new(JwtRegisteredClaimNames.Sub, userId),
            new(ClaimTypes.NameIdentifier, userId),
            new(JwtRegisteredClaimNames.UniqueName, profile.Account),
            new("tenant_id", profile.TenantId.ToString()),
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        if (profile.MerchantId.HasValue)
        {
            claims.Add(new Claim("merchant_id", profile.MerchantId.Value.ToString()));
        }

        claims.AddRange(profile.Roles.Select(role => new Claim(ClaimTypes.Role, role)));

        claims.AddRange(profile.Permissions.Select(permission => new Claim("permission", permission)));

        return claims;
    }
}
