﻿using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using ACE.Common;
using ACE.Entity.Enum;
using ACE.Common.Cryptography;
using MySql.Data.MySqlClient;

namespace ACE.Entity
{
    [DbTable("account", HasAutoGeneratedId = true, AutoGeneratedIdColumn = "accountId")]
    public class Account
    {
        /// <summary>
        /// creates a new account object and pre-creates a new, random salt
        /// </summary>
        public Account()
        {
            byte[] salt = new byte[64]; // 64 bytes = 512 bits, ideal for use with SHA512
            using (var salter = new RNGCryptoServiceProvider())
            {
                salter.GetNonZeroBytes(salt);
            }

            Salt = Convert.ToBase64String(salt);
            AccountGuid = Guid.NewGuid();
        }
        
        [DbField("accountId", (int)MySqlDbType.UInt32, Insert = false, IsCriteria = true, Update = false)]
        public uint AccountId { get; set; }

        public Guid AccountGuid { get; set; }

        [DbField("accountGuid", (int)MySqlDbType.Binary, Update = false)]
        public byte[] AccountGuid_Binder
        {
            get { return AccountGuid.ToByteArray(); }
            set { AccountGuid = new Guid(value); }
        }

        /// <summary>
        /// login name of the account.  for now, this is immutable.
        /// </summary>
        [DbField("accountName", (int)MySqlDbType.Text, Update = false)]
        public string Name { get; set; }

        [DbField("displayName", (int)MySqlDbType.Text)]
        public string DisplayName { get; set; }
        
        /// <summary>
        /// base64 encoded version of the salt.  salts may not be changed for an account.
        /// </summary>
        [DbField("passwordSalt", (int)MySqlDbType.Text, Update = false)]
        public string Salt { get; set; }

        /// <summary>
        /// the base64 encoded, hashed, nonrecoverable password
        /// </summary>
        [DbField("passwordHash", (int)MySqlDbType.Text)]
        public string PasswordHash { get; set; }

        public void SetPassword(string value)
        {
            PasswordHash = GetPasswordHash(value);
        }

        public bool PasswordMatches(string password)
        {
            var input = GetPasswordHash(password);
            return input == PasswordHash;
        }

        private string GetPasswordHash(string password)
        {
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            byte[] saltBytes = Convert.FromBase64String(Salt);
            byte[] buffer = passwordBytes.Concat(saltBytes).ToArray();
            byte[] hash;

            // rehash it
            using (SHA512Managed hasher = new SHA512Managed())
            {
                hash = hasher.ComputeHash(buffer);
            }

            return Convert.ToBase64String(hash);
        }

        [DbField("email", (int)MySqlDbType.Text)]
        public string Email { get; set; }

        [DbField("githubSshKey", (int)MySqlDbType.Text)]
        public string GithubSshKey { get; set; }

        [DbField("githubEmail", (int)MySqlDbType.Text)]
        public string GithubEmail { get; set; }

        [DbField("githubProviderKey", (int)MySqlDbType.Text)]
        public string GithubProviderKey { get; set; }
    }
}
