using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Authentication;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using XF.Domain.Dependency;
using XF.Domain.Exceptions;
using XF.Domain.Options;

namespace XF.Domain.Authentications
{
    public interface IJwtSecurity
    {
        string EncodeJwtToken(ICollection<Claim> claims, string? audience = null);
        void DecodeJwtToken(string jwt);
    }

    public class JwtSecurity : IJwtSecurity, IScopeDependency
    {
        private readonly IOptions<IdentityConfiguration> _jwtConfigOptionAccessor;
        private readonly IHttpContextAccessor _contextAccessor;

        public JwtSecurity(IOptions<IdentityConfiguration> jwtConfigOptionAccessor, IHttpContextAccessor contextAccessor)
        {
            _jwtConfigOptionAccessor = jwtConfigOptionAccessor;
            _contextAccessor = contextAccessor;
        }

        public string EncodeJwtToken(ICollection<Claim> claims, string? audience = null)
        {
            var jwtOptions = _jwtConfigOptionAccessor.Value.Jwt;
            if (jwtOptions == null)
                throw new ArgumentNullException(nameof(jwtOptions));

            var bytes = Encoding.UTF8.GetBytes(jwtOptions.SecretKey);
            var securityKey = new SymmetricSecurityKey(bytes);
            var signingCredentials =
                new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
            var expired = DateTime.Now.AddMinutes(jwtOptions.Expired);
            audience ??= jwtOptions.Audience;
            var jwtSecurityToken = new JwtSecurityToken(jwtOptions.Issuer, audience, claims, DateTime.Now, expired,
                signingCredentials);
            var tokenHandler = new JwtSecurityTokenHandler();
            var token = tokenHandler.WriteToken(jwtSecurityToken);
            return token;
        }

        public void DecodeJwtToken(string jwt)
        {
            var jwtOptions = _jwtConfigOptionAccessor.Value.Jwt;
            if (jwtOptions == null)
                throw new ArgumentNullException(nameof(jwtOptions));

            JwtSecurityTokenHandler tokenHandler = new();
            TokenValidationParameters valParam = new();
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SecretKey));
            valParam.IssuerSigningKey = securityKey;
            valParam.ValidateIssuer = false;
            valParam.ValidateAudience = false;
            ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt, valParam, out SecurityToken secToken);
            //foreach (var claim in claimsPrincipal.Claims)
            //{
            //    Console.WriteLine($"{claim.Type}={claim.Value}");

            //}
            if (_contextAccessor.HttpContext is null)
                throw new AuthenticationException($"{nameof(_contextAccessor.HttpContext)} is null");
            _contextAccessor.HttpContext.User = claimsPrincipal;
        }
    }
}
