using System.Security.Claims;
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using SIGCM2.Application.Abstractions.Security;
using SIGCM2.Domain.Entities;

namespace SIGCM2.Infrastructure.Security;

public sealed class JwtService : IJwtService
{
    private readonly RSA _rsa;
    private readonly JwtOptions _options;
    private readonly TimeProvider _timeProvider;

    public JwtService(RSA rsa, JwtOptions options, TimeProvider timeProvider)
    {
        _rsa = rsa;
        _options = options;
        _timeProvider = timeProvider;
    }

    /// <inheritdoc/>
    public ClaimsPrincipal GetPrincipalFromExpiredToken(string accessToken)
    {
        var parameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = _options.Issuer,
            ValidateAudience = true,
            ValidAudience = _options.Audience,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new RsaSecurityKey(_rsa),
            ValidateLifetime = false,  // Key: accept expired tokens in refresh flow
            ClockSkew = TimeSpan.Zero,
        };

        var handler = new JwtSecurityTokenHandler();
        var principal = handler.ValidateToken(accessToken, parameters, out var securityToken);

        if (securityToken is not JwtSecurityToken jwt ||
            !jwt.Header.Alg.Equals(SecurityAlgorithms.RsaSha256, StringComparison.OrdinalIgnoreCase))
            throw new SecurityTokenException("Invalid token algorithm");

        return principal;
    }

    /// <summary>
    /// UDT-009: Generates an access token with minimal claims.
    /// Claim 'permisos' has been removed — authorization handler resolves permissions
    /// from DB per-request using IUsuarioRepository + PermisoResolver.
    /// Token claims: sub, jti, name, rol (+ standard iat/exp/nbf).
    /// </summary>
    public string GenerateAccessToken(Usuario usuario)
    {
        var signingKey = new RsaSecurityKey(_rsa);
        var credentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);

        var claims = new List<Claim>
        {
            new(JwtRegisteredClaimNames.Sub, usuario.Id.ToString()),
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new("name", usuario.Username),
            new("rol", usuario.Rol),
        };

        var now = _timeProvider.GetUtcNow().UtcDateTime;
        var descriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Issuer = _options.Issuer,
            Audience = _options.Audience,
            IssuedAt = now,
            Expires = now.AddMinutes(_options.AccessTokenMinutes),
            SigningCredentials = credentials
        };

        var handler = new JwtSecurityTokenHandler();
        var token = handler.CreateToken(descriptor);
        return handler.WriteToken(token);
    }
}
