using OBSBoardsApi.Model;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Swashbuckle.AspNetCore.Annotations;
using System.Data;
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

namespace OBSBoardsApi.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthenticateController : ControllerBase
    {
        private readonly IConfiguration _configuration;

        public AuthenticateController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        private string GetIpAddressOfClient()
        {
            string ipAddress = string.Empty;
            IPAddress ip = Request.HttpContext.Connection.RemoteIpAddress;
            if (ip!=null)
            {
                if (ip.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    try
                    {
                        ip = Dns.GetHostEntry(ip).AddressList.First(x => x.AddressFamily == AddressFamily.InterNetwork);
                    } catch (Exception e)
                    {

                    } 
                    finally
                    {
                    }
                }
                ipAddress = ip.ToString();
            }
            return ipAddress;
        }

        [HttpPost]
        [SwaggerResponse(200, "Token, RefreshToken, Expiration, Roles")]
        public IActionResult Login([FromBody]UserModel login)
        {
            //using (var context = new EpioneContext(_configuration))
            //{
            //    var sha1 = System.Security.Cryptography.SHA1.Create();
            //    var p = sha1.ComputeHash(Encoding.UTF8.GetBytes(login.Password));
            //    var pwd = BitConverter.ToString(p).Replace("-", "").ToLower();

            //    var loggedUser = context.Users.Where(u => u.Name == login.Username && u.Password == pwd).FirstOrDefault();
            //    if (loggedUser!=null)
            //    {
            //        var authClaims = new List<Claim>
            //        {
            //            new Claim(ClaimTypes.Name, login.Username),
            //            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            //            new Claim("Id", loggedUser.Id.ToString()),
            //            new Claim(ClaimTypes.Role, "User")
            //        };
            //        var token = CreateToken(authClaims);
            //        var refreshToken = GenerateRefreshToken();
            //        context.Logs.Add(new Log { CreateDateTime = DateTime.Now, Method = MethodBase.GetCurrentMethod().Name, Value = "UserId: "+loggedUser.Id+"; IP: " + GetIpAddressOfClient() });
            //        context.SaveChanges();
            //        return Ok(new
            //        {
            //            Token = new JwtSecurityTokenHandler().WriteToken(token),
            //            RefreshToken = refreshToken,
            //            Expiration = token.ValidTo
            //        });
            //    }

            //}

            var authClaims = new List<Claim>
                    {
                        new Claim(ClaimTypes.Name, login.Username),
                        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                        new Claim("Id", "test"),
                        new Claim(ClaimTypes.Role, "User")
                    };
            var token = CreateToken(authClaims);
            var refreshToken = GenerateRefreshToken();
            return Ok(new
            {
                Token = new JwtSecurityTokenHandler().WriteToken(token),
                RefreshToken = refreshToken
            });

            return Unauthorized();
        }

        [HttpPost]
        public IActionResult RefreshToken([FromBody]TokenModel tokenModel)
        {
            if (string.IsNullOrEmpty(tokenModel.Token) || string.IsNullOrEmpty(tokenModel.RefreshToken))
            {
                return BadRequest("Invalid client request");
            }
            
            var authClaims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, GetPrincipalFromExpiredToken(tokenModel.Token).Identity.Name),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim("Id", GetPrincipalFromExpiredToken(tokenModel.Token).Claims.Where(c=>c.Type=="Id").FirstOrDefault().Value),
                    new Claim(ClaimTypes.Role, "User")
                };

            var newAccessToken = CreateToken(authClaims);
            var newRefreshToken = GenerateRefreshToken();

            return new ObjectResult(new
            {
                Token = new JwtSecurityTokenHandler().WriteToken(newAccessToken),
                RefreshToken = newRefreshToken
            });
        }

        private JwtSecurityToken CreateToken(List<Claim> authClaims)
        {
            var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Key"]));
            _ = int.TryParse(_configuration["JWT:TokenValidityInMinutes"], out int tokenValidityInMinutes);

            var token = new JwtSecurityToken(
                issuer: _configuration["JWT:Issuer"],
                audience: _configuration["JWT:Audience"],
                expires: DateTime.Now.AddMinutes(tokenValidityInMinutes),
                claims: authClaims,
                signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
                );

            return token;
        }

        private static string GenerateRefreshToken()
        {
            var randomNumber = new byte[64];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }

        private ClaimsPrincipal? GetPrincipalFromExpiredToken(string? token)
        {
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Key"])),
                ValidateLifetime = false
            };

            var tokenHandler = new JwtSecurityTokenHandler();
            var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken securityToken);
            if (securityToken is not JwtSecurityToken jwtSecurityToken || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
                throw new SecurityTokenException("Invalid token");
            return principal;
        }
    }

}
