using System;
using System.Security.Cryptography;

namespace iiie.Authentication.Business.JWT
{
    /// <summary>
    /// Hash generator for passwords
    /// </summary>
    public static class PasswordHash
    {
        private const int ITERATION_INDEX = 0;
        private const int SALT_INDEX = 1;
        private const int PBKDF2_INDEX = 2;

        /// <summary>
        /// Crée un hash à partir du password
        /// </summary>
        /// <param name="password">le password à hasher</param>
        /// <returns>le hash du password</returns>
        public static string CreateHash(string password)
        {
            // génaration du SALT aléatoire
            /*RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
            byte[] salt = new byte[AuthProvider.Instance.GetPasswordSaltByteSize()];
            csprng.GetBytes(salt);

            // hash le password et création de la chaine avec les paramêtres
            byte[] hash = PBKDF2(password, salt, AuthProvider.Instance.GetPasswordIterations(),
                AuthProvider.Instance.GetPasswordSaltByteSize());
            return AuthProvider.Instance.GetPasswordIterations() + ":" +
                Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash);*/
            return "";
        }

        /// <summary>
        /// Valide le password en adequation avec le hash
        /// </summary>
        /// <param name="password">le password à vérifier</param>
        /// <param name="correctHash">le hash du password stocké en base</param>
        /// <returns>True si c'est bon sinon false</returns>
        public static bool ValidatePassword(string password, string correctHash)
        {
            // Extraction des paramêtres du hash
            char[] delimiter = { ':' };
            string[] split = correctHash.Split(delimiter);
            int iterations = Int32.Parse(split[ITERATION_INDEX]);
            byte[] salt = Convert.FromBase64String(split[SALT_INDEX]);
            byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]);

            byte[] testHash = PBKDF2(password, salt, iterations, hash.Length);
            return SlowEquals(hash, testHash);
        }


        private static bool SlowEquals(byte[] a, byte[] b)
        {
            uint diff = (uint)a.Length ^ (uint)b.Length;
            for (int i = 0; i < a.Length && i < b.Length; i++)
                diff |= (uint)(a[i] ^ b[i]);
            return diff == 0;
        }

        /// <summary>
        /// Calcul le PBKDF2-SHA1 hash du password.
        /// </summary>
        /// <param name="password">The password à hasher</param>
        /// <param name="salt">le salt</param>
        /// <param name="iterations">le nombre d'itération</param>
        /// <param name="outputBytes">la longueur du hash à générer</param>
        /// <returns>le hash du password.</returns>
        private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
        {
            Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
            pbkdf2.IterationCount = iterations;
            return pbkdf2.GetBytes(outputBytes);
        }
    }
}
