using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using DomainResults.Common;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;

namespace JWTService {

  public interface IJWTService {
  
    string CreateJwtToken(DateTime expires, List<KeyValuePair<string, string>>? claims);
    (List<KeyValuePair<string, string>>?, IDomainResult) JwtTokenClaims(string token);
  }

  public class JWTService : IJWTService {

    private readonly ILogger<JWTService> _logger;
    private readonly IJwtConfig _configuration;

    public JWTService(
      ILogger<JWTService> logger,
      IJwtConfig configuration
    ) {
      _logger = logger;
      _configuration = configuration;
    }

    public string CreateJwtToken(DateTime expires, List<KeyValuePair<string, string>>? claims) =>
      CreateJwtToken(_configuration.Secret, expires, claims);

    public string CreateJwtToken(string secret, DateTime expires, List<KeyValuePair<string, string>>? claims) {

      // add roles to claims identity from database
      var tokenClaims = new List<Claim>();

      if (claims != null)
        foreach (var claim in claims)
          tokenClaims.Add(new Claim(claim.Key, claim.Value));

      var tokenHandler = new JwtSecurityTokenHandler();

      var securityToken = tokenHandler.CreateToken(new SecurityTokenDescriptor {
        IssuedAt = DateTime.UtcNow,
        Subject = new ClaimsIdentity(tokenClaims),
        Expires = expires,
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Convert.FromBase64String(secret)), SecurityAlgorithms.HmacSha512Signature),
      });
      _logger.LogInformation($"Creted new JWT {securityToken}");

      return tokenHandler.WriteToken(securityToken);
    }

    public (List<KeyValuePair<string, string>>?, IDomainResult) JwtTokenClaims(string token) {

      var securityToken = new JwtSecurityTokenHandler().ReadToken(token) as JwtSecurityToken;
      var claims = securityToken?.Claims?.Select(x => new KeyValuePair<string, string>(x.Type, x.Value));

      if (claims == null)
        return IDomainResult.Failed<List<KeyValuePair<string, string>>?>();

      return claims.Count() > 0
        ? IDomainResult.Success(claims.ToList())
        : IDomainResult.NotFound<List<KeyValuePair<string, string>>?>();
    }
  }
}
