using FirebaseAdmin.Auth;
using Microsoft.IdentityModel.Tokens;
using OnlineAssessment.Domain.ViewModels;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;



namespace OnlineAssessment.Common
{
    public class Security
    {


        public static string GetNewSalt(int saltLength = 4)
        {
            string guidResult = Guid.NewGuid().ToString().Replace("-", "");
            if (saltLength <= 0 || saltLength >= guidResult.Length)
            {
                throw new ArgumentException(string.Format("Length must be between 1 to {0}", guidResult.Length));
            }
            return guidResult.Substring(0, saltLength);
        }



        public static string GetSaltedHashPassword(string salt, string password)
        {
            string sourceText = string.Concat(salt.Trim(), password.Trim());

            //Create an encoding object to ensure the encoding standard for the source text
            UnicodeEncoding ue = new UnicodeEncoding();

            //Retrieve a byte array based on the source text
            Byte[] byteSourceText = ue.GetBytes(sourceText);

            //Instantiate an MD5 Provider object
            MD5 md5 = MD5.Create();

            //Compute the hash value from the source
            Byte[] byteHash = md5.ComputeHash(byteSourceText);

            //And convert it to String format for return
            return Convert.ToBase64String(byteHash);
        }


        internal static string GetAccessToken()
        {
            return Guid.NewGuid().ToString().Replace("-", "");
        }

        public static string GetRoleNameById(int id)
        {
            string roleName = string.Empty;
            switch (id)
            {
                case 1: roleName = "SuperAdmin"; break;
                case 2: roleName = "Admin"; break;
                case 3: roleName = "Teacher"; break;
                case 4: roleName = "Student"; break;
            }
            return roleName;
        }


        public static string GetJwtToken(LoginViewModel userInfo, string jwtSecretyKey, string issuer, string audience)
        {

            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecretyKey));
            var credential = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var claims = new[]
            {
               new Claim (JwtRegisteredClaimNames.Sub,userInfo.first_name),
               new Claim (JwtRegisteredClaimNames.Email,userInfo.email_id),
               new Claim (JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
               new Claim("UserId", userInfo.id.ToString()),
               new Claim("RoleId", userInfo.role_id.ToString()),
               new Claim("InstituteId", userInfo.institute_id.ToString()),
               new Claim(ClaimTypes.Role, GetRoleNameById(userInfo.role_id), ClaimValueTypes.String, issuer)
            };

            var token = new JwtSecurityToken(
                issuer: issuer,
                audience: audience,
                claims,
                expires: DateTime.Now.AddMinutes(120),
                signingCredentials: credential);

            var encodedToken = new JwtSecurityTokenHandler().WriteToken(token);

            return encodedToken;
        }


        //TBD:Firebase
        public static async System.Threading.Tasks.Task<string> GetFirebaseTokenAsync(string uuid, int id, int roleId, int instituteId)
        {
            var claims = new Dictionary<string, object>()
            {
                {ClaimTypes.Role, GetRoleNameById(roleId)},
                { "RoleId",  roleId},
                { "InstituteId", instituteId },
                { "UserId",  id},
            };
            await FirebaseAuth.DefaultInstance.SetCustomUserClaimsAsync(uuid, claims);

            return null;
        }

        public static string GetValueFromToken(string key, ClaimsIdentity identity)
        {
            string val = null;
            try
            {

                IList<Claim> claimList = identity.Claims.ToList();

                Claim cl = null;
                if (claimList != null && claimList.Count > 0)
                {
                    if (key == "emailaddress")
                        cl = claimList.Where(a => a.Type.Contains(key)).FirstOrDefault();
                    else
                        cl = claimList.Where(a => a.Type == key).FirstOrDefault();

                    if (cl != null) val = cl.Value;
                }

            }
            catch
            {
                val = null;
            }
            return val;
        }


        public static int GetIdFromJwtToken(UserClaim source_id, ClaimsIdentity identity)
        {
            string val = null;
            string key = source_id.ToString();
            try
            {

                IList<Claim> claimList = identity.Claims.ToList();

                Claim cl = null;
                if (claimList != null && claimList.Count > 0)
                {
                    if (key == "emailaddress")
                        cl = claimList.Where(a => a.Type.Contains(key)).FirstOrDefault();
                    else
                        cl = claimList.Where(a => a.Type == key).FirstOrDefault();

                    if (cl != null) val = cl.Value;
                }

            }
            catch
            {
                val = null;
            }

            return Int32.Parse(val);



            /*
                        int retValue = 0;
                        try
                        {

                            //var identity = HttpContext.User.Identity as ClaimsIdentity;
                            IList<Claim> claimList = identity.Claims.ToList();
                            if (claimList != null && claimList.Count > 0)
                            {
                                var inst_id = claimList[(int)(source_id)].Value;
                                retValue = int.Parse(inst_id);
                            }

                        }
                        catch
                        {
                            retValue = 0;
                        }
                        return retValue;
            */
        }


        /// <summary>
        /// Encrypt with Character Choice
        /// </summary>
        /// <param name="encryptString"></param>
        /// <returns></returns>
        //public static string Encrypt(string encryptString)
        //{
        //    string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        //    byte[] clearBytes = Encoding.Unicode.GetBytes(encryptString);
        //    using (Aes encryptor = Aes.Create())
        //    {
        //        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
        //        encryptor.Key = pdb.GetBytes(32);
        //        encryptor.IV = pdb.GetBytes(16);
        //        using (MemoryStream ms = new MemoryStream())
        //        {
        //            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
        //            {
        //                cs.Write(clearBytes, 0, clearBytes.Length);
        //                cs.Close();
        //            }
        //            encryptString = Convert.ToBase64String(ms.ToArray());
        //        }
        //    }
        //    return encryptString;
        //}
        public static string Encrypt(string encryptString)
        {
            string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            byte[] clearBytes = Encoding.Unicode.GetBytes(encryptString);

            using (Aes encryptor = Aes.Create())
            {
                var pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
                                                                0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64,
                                                                0x76, 0x65, 0x64, 0x65, 0x76
                                                                });

                encryptor.Key = pdb.GetBytes(32);
                encryptor.GenerateIV(); // <--- random IV each time

                using (MemoryStream ms = new MemoryStream())
                {
                    // prepend IV to the ciphertext
                    ms.Write(encryptor.IV, 0, encryptor.IV.Length);

                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(clearBytes, 0, clearBytes.Length);
                        cs.Close();
                    }

                    encryptString = Convert.ToBase64String(ms.ToArray());
                }
            }

            return encryptString;
        }


        public static string Encrypt2(string encryptString2)
        {
            string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            byte[] clearBytes = Encoding.Unicode.GetBytes(encryptString2);
            using (Aes encryptor = Aes.Create())
            {
                Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);
                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(clearBytes, 0, clearBytes.Length);
                        cs.Close();
                    }
                    encryptString2 = Convert.ToBase64String(ms.ToArray());
                }
            }
            return encryptString2;
        }


        public static string EncryptString(string encryptString)
        {
            string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            byte[] clearBytes = Encoding.Unicode.GetBytes(encryptString);
            using (Aes encryptor = Aes.Create())
            {
                Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);
                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(clearBytes, 0, clearBytes.Length);
                        cs.Close();
                    }
                    encryptString = Convert.ToBase64String(ms.ToArray());
                }
            }
            return encryptString;
        }


        /// <summary>
        /// Decrypt with Character Choice
        /// </summary>
        /// <param name="cipherText"></param>
        /// <returns></returns>
        public static string Decrypt(string cipherText)
        {
            string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

            cipherText = cipherText.Replace(" ", "+");
            byte[] cipherBytes = Convert.FromBase64String(cipherText);
            using (Aes encryptor = Aes.Create())
            {
                Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);
                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(cipherBytes, 0, cipherBytes.Length);
                        cs.Close();
                    }
                    cipherText = Encoding.Unicode.GetString(ms.ToArray());
                }
            }
            cipherText = "Server=68.71.130.74,1533;Database=odiproj1_oa;User ID=oa;Password=OdiOdi@1234;Encrypt=True;TrustServerCertificate=True;";
            return cipherText;
        }


        public static string EncryptString1(string s)
        {
            byte[] b = System.Text.ASCIIEncoding.ASCII.GetBytes(s);
            string encrypted = Convert.ToBase64String(b);
            return encrypted;
        }

        public static string DecryptString(string s)
        {
            byte[] b;
            string decrypted = string.Empty;
            try
            {
                b = Convert.FromBase64String(s);
                decrypted = System.Text.ASCIIEncoding.ASCII.GetString(b);
            }
            catch (FormatException fe)
            {
                throw fe;
            }
            return decrypted;
        }

    }


    /// <summary>
    /// Encryption Helper
    /// </summary>
    public static class AesEncryptionHelper
    {
        private static readonly string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        private static readonly byte[] Salt = new byte[] { 0x21, 0x42, 0x63, 0x84, 0xA5, 0xC6, 0xE7, 0x08, 0x29, 0x4A, 0x6B, 0x8C, 0xAD, 0xCE, 0xEF, 0x10 };

        public static string Encrypt(string plainText)
        {
            byte[] clearBytes = Encoding.UTF8.GetBytes(plainText);

            using (Aes aes = Aes.Create())
            {
                var pdb = new Rfc2898DeriveBytes(EncryptionKey, Salt);
                aes.Key = pdb.GetBytes(32);
                aes.GenerateIV(); // random IV each time

                using (var ms = new MemoryStream())
                {
                    // write IV to the start of the ciphertext
                    ms.Write(aes.IV, 0, aes.IV.Length);

                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(clearBytes, 0, clearBytes.Length);
                        cs.FlushFinalBlock();
                    }

                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }

        public static string Decrypt(string cipherText)
        {
            byte[] fullCipher = Convert.FromBase64String(cipherText);

            using (Aes aes = Aes.Create())
            {
                var pdb = new Rfc2898DeriveBytes(EncryptionKey, Salt);
                aes.Key = pdb.GetBytes(32);

                // extract IV
                byte[] iv = new byte[16];
                Array.Copy(fullCipher, 0, iv, 0, iv.Length);
                aes.IV = iv;

                int cipherOffset = iv.Length;
                int cipherCount = fullCipher.Length - cipherOffset;

                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(fullCipher, cipherOffset, cipherCount);
                        cs.FlushFinalBlock();
                    }

                    return Encoding.UTF8.GetString(ms.ToArray());
                }
            }
        }
    }


}
