using e_suite.API.Common;
using e_suite.Database.Core.Tables.UserManager;
using e_suite.Modules.UserManager.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace e_suite.Modules.UserManager.Services;

/// <summary>
/// Json Web Token Service
/// </summary>
public class JwtService : IJwtService
{
    private readonly IRoleManager _roleManager;

    private readonly string _secret;
    private readonly string _expDate;
    private readonly string _audience;
    private readonly string _issuer;

    /// <summary>
    /// default constructor using dependency injection.
    /// </summary>
    /// <param name="config"></param>
    public JwtService(IConfiguration config, IRoleManager roleManager)
    {
        _roleManager = roleManager;
        var jwtConfig = config.GetSection("JwtConfig");

        _secret = jwtConfig["secret"]!;
        _expDate = jwtConfig["expirationInMinutes"]!;
        _audience = jwtConfig["audience"]!;
        _issuer = jwtConfig["issuer"]!;
    }

    /// <summary>
    /// Creates a valid JWT Security token for the provided User.
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public string GenerateSecurityToken(User user)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(_secret);
        
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.PrimarySid, user.Id.ToString()),
                new Claim(ClaimTypes.Email, user.Email),
                new Claim(ClaimTypes.Name, user.DisplayName),
                new Claim(ESuiteClaimTypes.DomainId, user.Domain.Id.ToString()),
                //todo remove this from the token, and make it a separate call, so that I can keep the token small and keep the application running fast
                new Claim(ESuiteClaimTypes.SecurityPrivileges, GetSecurityPrivileges(user)),
                new Claim(ESuiteClaimTypes.PreferredLocale, user.PreferredLocale)
            }),

            Expires = DateTime.UtcNow.AddMinutes(double.Parse(_expDate)),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
            Audience = _audience,
            Issuer = _issuer
        };

        var token = tokenHandler.CreateToken(tokenDescriptor);

        return tokenHandler.WriteToken(token);

    }

    private string GetSecurityPrivileges(User user)
    {
        var myUserAccess = _roleManager.GetMyUserAccess(user);
        var myUserPrivileges = myUserAccess.Select(x => x.SecurityAccess.ToString());
        return myUserPrivileges.ToJson(false);
    }
}