using Brizco.Common.Models.Claims;
using Brizco.Core.BaseServices.Abstracts;
using Brizco.Domain.Models.Settings;
using Mapster;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Brizco.Common.Extensions;

namespace Brizco.Core.BaseServices;

public class JwtService : IJwtService
{
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly RoleManager<ApplicationRole> _roleManager;
    private readonly SiteSettings _siteSettings;

    public JwtService(
        IOptionsSnapshot<SiteSettings> siteSettings,
        SignInManager<ApplicationUser> userSignInManager,
        RoleManager<ApplicationRole> roleManager)
    {
        _signInManager = userSignInManager;
        _roleManager = roleManager;
        _siteSettings = siteSettings.Value;
    }
    public async Task<AccessToken<TUser>> Generate<TUser>(TUser user, Guid complexId, Guid roleId) where TUser : ApplicationUser
    {
        var tokenId = StringExtensions.GetId(8);
        var claims = await GetClaims(user, tokenId, roleId.ToString());
        claims.Add(new Claim("ComplexId", complexId.ToString()));

        var token = BaseGenerate<TUser>(user, claims);
        token.Permissions = claims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList();
        return token;
    }
    public async Task<AccessToken<TUser>> Generate<TUser>(TUser user, Guid complexId) where TUser : ApplicationUser
    {
        var tokenId = StringExtensions.GetId(8);
        var claims = await GetClaims(user, tokenId);
        claims.Add(new Claim("ComplexId", complexId.ToString()));

        return BaseGenerate(user, claims);
    }
    public async Task<AccessToken<TUser>> Generate<TUser>(TUser user) where TUser : ApplicationUser
    {
        var tokenId = StringExtensions.GetId(8);
        var claims = await GetClaims(user, tokenId);
        return BaseGenerate(user, claims);

    }

    public async Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user, Guid complexId, Guid roleId) where TUser : ApplicationUser
    {
        var tokenId = StringExtensions.GetId(8);
        var claims = await GetClaims(user, tokenId, roleId.ToString());
        claims.Add(new Claim("ComplexId", complexId.ToString()));

        var token = BaseGenerate<TUserDto, TUser>(user, claims);
        token.Permissions = claims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList();
        return token;
    }

    public async Task<AccessToken<TUserDto,TRole>> Generate<TUserDto, TUser, TRole>(TUser user, Guid complexId, Guid roleId) where TUser : ApplicationUser
    {
        var tokenId = StringExtensions.GetId(8);
        var claims = await GetClaims(user, tokenId,roleId.ToString());
        claims.Add(new Claim("ComplexId", complexId.ToString()));

        return BaseGenerate<TUserDto, TUser, TRole>(user, claims);
    }

    public async Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user, Guid complexId) where TUser : ApplicationUser
    {
        var tokenId = StringExtensions.GetId(8);
        var claims = await GetClaims(user, tokenId);
        claims.Add(new Claim("ComplexId", complexId.ToString()));

        return BaseGenerate<TUserDto, TUser>(user, claims);
    }
    public async Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user) where TUser : ApplicationUser
    {
        var tokenId = StringExtensions.GetId(8);
        var claims = await GetClaims(user, tokenId);
        return BaseGenerate<TUserDto, TUser>(user, claims);
    }




    private AccessToken<TUser> BaseGenerate<TUser>(TUser user, List<Claim> claims) where TUser : ApplicationUser
    {
        var secretKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.SecretKey);
        var signingCredintial = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha512Signature);

        var desctiptor = new SecurityTokenDescriptor
        {
            Issuer = _siteSettings.JwtSettings.Issuer,
            Audience = _siteSettings.JwtSettings.Audience,
            IssuedAt = DateTime.Now,
            NotBefore = DateTime.Now,
            Expires = DateTime.Now.AddDays(_siteSettings.JwtSettings.ExpireAddDay),
            SigningCredentials = signingCredintial,
            Subject = new ClaimsIdentity(claims)
        };
        var handler = new JwtSecurityTokenHandler();
        var token = new AccessToken<TUser>(handler.CreateJwtSecurityToken(desctiptor));
        token.User = user;
        return token;
    }
    private AccessToken<TUserDto> BaseGenerate<TUserDto, TUser>(TUser user, List<Claim> claims) where TUser : ApplicationUser
    {
        var secretKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.SecretKey);
        var signingCredintial = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha512Signature);

        var desctiptor = new SecurityTokenDescriptor
        {
            Issuer = _siteSettings.JwtSettings.Issuer,
            Audience = _siteSettings.JwtSettings.Audience,
            IssuedAt = DateTime.Now,
            NotBefore = DateTime.Now,
            Expires = DateTime.Now.AddDays(_siteSettings.JwtSettings.ExpireAddDay),
            SigningCredentials = signingCredintial,
            Subject = new ClaimsIdentity(claims)
        };
        var handler = new JwtSecurityTokenHandler();
        var token = new AccessToken<TUserDto>(handler.CreateJwtSecurityToken(desctiptor));
        token.User = user.Adapt<TUserDto>();
        return token;
    }

    private AccessToken<TUserDto,TRole> BaseGenerate<TUserDto, TUser, TRole>(TUser user, List<Claim> claims) where TUser : ApplicationUser
    {
        var secretKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.SecretKey);
        var signingCredintial = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha512Signature);

        var desctiptor = new SecurityTokenDescriptor
        {
            Issuer = _siteSettings.JwtSettings.Issuer,
            Audience = _siteSettings.JwtSettings.Audience,
            IssuedAt = DateTime.Now,
            NotBefore = DateTime.Now,
            Expires = DateTime.Now.AddDays(_siteSettings.JwtSettings.ExpireAddDay),
            SigningCredentials = signingCredintial,
            Subject = new ClaimsIdentity(claims)
        };
        var handler = new JwtSecurityTokenHandler();
        var token = new AccessToken<TUserDto, TRole>(handler.CreateJwtSecurityToken(desctiptor));
        token.User = user.Adapt<TUserDto>();
        token.Permissions = claims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList();
        return token;
    }


    private async Task<List<Claim>> GetClaims<TUser>(TUser baseUser, string jwtId) where TUser : ApplicationUser
    {
        var clFac = (await _signInManager.ClaimsFactory.CreateAsync(baseUser));
        var claims = new List<Claim>();
        claims.Add(new Claim("JwtID", jwtId));
        claims.Add(new Claim(ClaimTypes.Name, baseUser.UserName));
        claims.Add(new Claim("SignUpStatus", ((int)baseUser.SignUpStatus).ToString()));
        claims.Add(new Claim(ClaimTypes.NameIdentifier, baseUser.Id.ToString()));
        if (baseUser.Email != null)
            claims.Add(new Claim(ClaimTypes.Email, baseUser.Email));
        claims.Add(new Claim(ClaimTypes.Gender, baseUser.Gender == 0 ? "Female" : "Mail"));
        return claims;

    }

    private async Task<List<Claim>> GetClaims<TUser>(TUser baseUser, string jwtId, string roleId) where TUser : ApplicationUser
    {
        var applicationRole = await _roleManager.FindByIdAsync(roleId);
        var roleClaims = await _roleManager.GetClaimsAsync(applicationRole);
        var claims = new List<Claim>();
        claims.Add(new Claim("SignUpStatus",((int)baseUser.SignUpStatus).ToString()));
        claims.Add(new Claim(ClaimTypes.Name, baseUser.UserName));
        claims.Add(new Claim(ClaimTypes.NameIdentifier, baseUser.Id.ToString()));
        claims.Add(new Claim(ClaimTypes.Role,applicationRole.EnglishName));
        claims.Add(new Claim("RoleId", applicationRole.Id.ToString()));
        if (baseUser.Email != null)
            claims.Add(new Claim(ClaimTypes.Email, baseUser.Email));
        claims.AddRange(roleClaims);
        claims.Add(new Claim("JwtID", jwtId));
        claims.Add(new Claim(ClaimTypes.Gender, baseUser.Gender == 0 ? "Female" : "Mail"));
        return claims;

    }

}