using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using FamilyTreeAPI.Entities;
using FamilyTreeAPI.Interface;

namespace FamilyTreeAPI.Authorization;


public class JwtUtils : IJwtUtils
{
    private readonly AppSettings _appSettings;

    public JwtUtils(IOptions<AppSettings> appSettings)
    {
        _appSettings = appSettings.Value;
    }

    public string GenerateJwtToken(UserDto user)
    {
        // generate token that is valid for 2 days
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[] { 
              new Claim("id", user.Id.ToString()),
              new Claim("subject", user.Username),
              new Claim("firstName", user.FirstName + " " + user.LastName)
              {

              }

              //add more if we need it
            }
            ),
            Expires = DateTime.UtcNow.AddDays(2),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);
    }
    // get back id and username. in subject
    public UserDto? ValidateJwtToken(string token)
    {
        if (token == null)
            return null;

        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
                ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);

            var jwtToken = (JwtSecurityToken)validatedToken;
            // var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value); //old
            int userId = 0;
            string username = "";
            string firstName = "";
            Claim claim;
            for (int i = 0; i< jwtToken.Claims.Count(); i++)
            {
                claim = jwtToken.Claims.ElementAt(i);
                if (claim.Type == "id")
                {
                    userId = int.Parse(claim.Value);
                }
                else if (claim.Type == "subject")
                {
                    username = claim.Value;
                }
                else if (claim.Type == "firstName")
                {
                    firstName = claim.Value;
                }

                if (!string.IsNullOrEmpty(username) 
                    && !string.IsNullOrEmpty(firstName)
                    && userId > 0)
                {
                    break;
                }
            }

            UserDto user = new UserDto();
            user.Id = userId;
            user.Username = username;
            user.FirstName = firstName;
            
            // return user id from JWT token if validation successful
            return user;
        }
        catch
        {
            // return null if validation fails
            return null;
        }
    }
}