using System;
using System.Collections.Generic;
using Top.Api.Util;
using System.Threading;
using Top.Api.Report;


namespace Top.Api.Security
{

    /// <summary>
    /// 加、解密核心类
    /// </summary>
    public class SecurityCore : SecurityConstants
    {
        private static readonly ITopLogger Log = Top.Api.Log.Instance;
        // 缓存用户单独分配秘钥,需要加同步锁
        private static readonly IDictionary<string, SecretContext> AppUserSecretCache = new Dictionary<string, SecretContext>();
        private static readonly IDictionary<string, SecretContext> AppSecretCache = new Dictionary<string, SecretContext>();
        private static readonly object EmptyObject = new object();
        private static readonly object CacheLock = new object();
        private static readonly object AppLock = new object();
        private static readonly object AsynQueueKeyLock = new object();

        private string randomNum;// 伪随机码
        private DefaultTopClient topClient;
        private static IDictionary<string, object> asynQueueKey = new Dictionary<string, object>();
        private static readonly IDictionary<string, IDictionary<string, object>> AllAppConfig = new Dictionary<string, IDictionary<string, object>>();

        private bool streetest;

        public static IDictionary<string, SecretContext> GetAppUserSecretCache()
        {
            return AppUserSecretCache;
        }

        /// <summary>
        /// 判断密文是否支持检索
        /// </summary>
        /// <param name="key"></param>
        /// <param name="version"></param>
        /// <returns></returns>
        public bool IsIndexEncrypt(string key, Nullable<Int64> version)
        {
            if (version != null && version < 0)
            {
                key = PREVIOUS + key;
            }
            else
            {
                key = CURRENT + key;
            }

            IDictionary<string, Object> appConfig = getAppConfig();

            if (appConfig == null)
            {
                return false;
            }

            object encryptType = null;
            appConfig.TryGetValue(key, out encryptType);

            return INDEX_ENCRYPT_TYPE.Equals(encryptType);
        }

        private IDictionary<string, Object> getAppConfig()
        {
            IDictionary<string, Object> appConfig = null;
            AllAppConfig.TryGetValue(topClient.appKey, out appConfig);
            return appConfig;
        }

        /// <summary>
        /// 获取压缩长度
        /// </summary>
        /// <returns></returns>
        public int GetCompressLen()
        {
            IDictionary<string, Object> appConfig = getAppConfig();
            if (appConfig != null)
            {
                object compressLen = null;
                appConfig.TryGetValue(ENCRYPT_INDEX_COMPRESS_LEN, out compressLen);

                if (compressLen != null)
                {
                    return Convert.ToInt32(compressLen);
                }
            }
            return DEFAULT_INDEX_ENCRYPT_COMPRESS_LEN;
        }

        /// <summary>
        /// 获取滑动窗口大小
        /// </summary>
        /// <returns></returns>
        public int GetSlideSize()
        {
            IDictionary<string, Object> appConfig = getAppConfig();
            if (appConfig != null)
            {
                object encryptSlideSize = null;
                appConfig.TryGetValue(ENCRYPT_SLIDE_SIZE, out encryptSlideSize);
                if (encryptSlideSize != null)
                {
                    return Convert.ToInt32(encryptSlideSize);
                }
            }
            return DEFAULT_ENCRYPT_SLIDE_SIZE;
        }

        public SecurityCore(DefaultTopClient topClient, string randomNum, bool streetest)
        {
            this.streetest = streetest;
            this.topClient = topClient;
            this.randomNum = randomNum;
            // 初始化报表
            ApiReporter apiReporter = new ApiReporter();
            apiReporter.InitSecret(topClient);
        }

        public void SetRandomNum(string randomNum)
        {
            this.randomNum = randomNum;
        }
        
        /// <summary>
        /// 获取秘钥
        /// </summary>
        /// <param name="session"></param>
        /// <param name="secretVersion"></param>
        /// <returns></returns>
        public SecretContext GetSecret(string session, Nullable<Int64> secretVersion)
        {
            SecretContext secretContext = GetSecret(session, GenerateSecretKey(session, secretVersion));
            if (secretContext != null)
            {
                if (secretContext.IsValid())
                {
                    return secretContext;
                }

                if (secretContext.IsMaxValid())
                {
                    // 异步更新秘钥
                    AsynUpdateSecret(session, secretVersion);
                    return secretContext;
                }

                string cacheKey = GenerateSecretKey(session, secretVersion);
                lock (CacheLock)
                {
                    if (session != null)
                    {
                        AppUserSecretCache.Remove(cacheKey);
                    }
                    else
                    {
                        AppSecretCache.Remove(cacheKey);
                    }
                }
                // 同步调用获取秘钥
                return CallSecretApi(session, secretVersion);
            }
            else
            {
                // 同步调用获取秘钥
                return CallSecretApi(session, secretVersion);
            }
        }

        private string GenerateSecretKey(string session, Nullable<Int64> secretVersion)
        {
            if (session == null)
            {
                return this.topClient.appKey;
            }
            if (secretVersion == null)
            {
                return session;
            }

            return session + "_" + secretVersion;
        }

        /// <summary>
        /// 从本地获取秘钥信息
        /// </summary>
        /// <param name="session"></param>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        private SecretContext GetSecret(string session, string cacheKey)
        {
            SecretContext secretContext;
            if (session != null)
            {
                AppUserSecretCache.TryGetValue(cacheKey, out secretContext);
            }
            else
            {
                AppSecretCache.TryGetValue(cacheKey, out secretContext);
            }
            return secretContext;
        }

        /// <summary>
        /// 调用获取秘钥api
        /// </summary>
        /// <param name="session"></param>
        /// <param name="secretVersion"></param>
        /// <returns></returns>
        private SecretContext CallSecretApi(string session, Nullable<Int64> secretVersion)
        {
            // 获取伪随机码
            if (string.IsNullOrEmpty(randomNum))
            {
                throw new ArgumentException("randomNum can`t be empty");
            }

            TopSecretGetRequest request = new TopSecretGetRequest();
            request.RandomNum = randomNum;
            request.SecretVersion = secretVersion;
            if (streetest)
            {
                request.AddOtherParameter("tb_eagleeyex_t", "1");
            }

            TopSecretGetResponse response;
            if (session != null && session.StartsWith(UNDERLINE))
            {
                string customerUserId = session.Substring(1);
                if (!StringUtil.IsDigits(customerUserId))
                {
                    throw new ArgumentException("session invalid");
                }
                request.CustomerUserId = Convert.ToInt64(customerUserId);
                response = topClient.Execute(request, null);
            }
            else
            {
                response = topClient.Execute(request, session);
            }
            if (!response.IsError)
            {

                IDictionary<string, Object> appConfig = null;
                if (!string.IsNullOrEmpty(response.AppConfig))
                {
                    appConfig = (IDictionary<string, Object>)TopUtils.JsonToObject(response.AppConfig);
                    putAppConfig(appConfig);
                }
                SecretContext secretContext = new SecretContext();
                if (response.Secret != null)
                {
                    long currentTime = TopUtils.GetCurrentTimeMillis();
                    secretContext.InvalidTime = currentTime + (response.Interval * 1000);
                    secretContext.MaxInvalidTime = (currentTime + (response.MaxInterval * 1000));
                    secretContext.Secret = Convert.FromBase64String(response.Secret);
                    secretContext.SecretVersion = response.SecretVersion;
                }
                else
                {
                    if (appConfig != null)
                    {
                        object publishStatus = null;
                        appConfig.TryGetValue(PUBLISH_STATUS, out publishStatus);
                        if (BETA_STATUS.Equals(publishStatus))
                        {
                            // 设置空缓存
                            SetNullCache(secretContext);
                        }
                    }
                }
                
                PutToCache(session, secretVersion, secretContext);
                return secretContext;
            }
            else
            {
                // 查找不到历史秘钥
                if ("20005".Equals(response.SubErrCode))
                {
                    SecretContext secretContext = new SecretContext();
                    // 设置空缓存
                    SetNullCache(secretContext);

                    PutToCache(session, secretVersion, secretContext);
                    return secretContext;
                }
                throw new SecretException(response.ErrCode, response.ErrMsg, response.SubErrCode, response.SubErrMsg);
            }
        }

        private void putAppConfig(IDictionary<string, Object> appConfig)
        {
            lock (AppLock)
            {
                  if (!AllAppConfig.ContainsKey(topClient.appKey))
                  {
                      AllAppConfig.Add(topClient.appKey, appConfig);
                  }
            }
        }

        private void PutToCache(string session, Nullable<Int64> secretVersion, SecretContext secretContext)
        {
            string cacheKey = GenerateSecretKey(session, secretVersion);

            lock (CacheLock)
            {
                if (session != null)
                {
                    if (AppUserSecretCache.ContainsKey(cacheKey))
                    {
                        AppUserSecretCache[cacheKey] = secretContext;
                    }
                    else
                    {
                        AppUserSecretCache.Add(cacheKey, secretContext);
                    }
                }
                else
                {
                    if (AppSecretCache.ContainsKey(cacheKey))
                    {
                        AppSecretCache[cacheKey] = secretContext;
                    }
                    else
                    {
                        AppSecretCache.Add(cacheKey, secretContext);
                    }
                }
            }
        }

        /// <summary>
        /// 设置空缓存
        /// </summary>
        /// <param name="secretContext"></param>
        private void SetNullCache(SecretContext secretContext)
        {
            long currentTime = TopUtils.GetCurrentTimeMillis();
            secretContext.InvalidTime = currentTime + (DEFAULT_INTERVAL * 1000);
            secretContext.MaxInvalidTime = currentTime + (DEFAULT_MAX_INTERVAL * 1000);
        }

        /// <summary>
        /// 异步更新秘钥
        /// </summary>
        /// <param name="session"></param>
        /// <param name="secretVersion"></param>
        private void AsynUpdateSecret(string session, Nullable<Int64> secretVersion)
        {
            string cacheKey = GenerateSecretKey(session, secretVersion);

            lock (AsynQueueKeyLock)
            {
                // 不需要重复提交秘钥请求
                if (asynQueueKey.ContainsKey(cacheKey))
                {
                    return;
                }
                SecretContext secretContext = GetSecret(session, GenerateSecretKey(session, secretVersion));
                if (secretContext != null && secretContext.IsValid())
                {
                    return;
                }

                asynQueueKey.Add(cacheKey, EmptyObject);
            }


            WaitCallback secretApiCallback = (state) =>
            {
                try
                {
                    CallSecretApi(session, secretVersion);
                }
                catch (Exception e)
                {
                    Log.Error(string.Format("asyn update secret error: {0}", e.Message));

                }
                finally
                {
                    lock (AsynQueueKeyLock)
                    {
                        asynQueueKey.Remove(cacheKey);
                    }
                }
            };

            try
            {
                ThreadPool.QueueUserWorkItem(secretApiCallback);
            }
            catch (Exception e)
            {
                lock (AsynQueueKeyLock)
                {
                    asynQueueKey.Remove(cacheKey);
                }
                Log.Error(string.Format("add QueueUserWorkItem error: {0}", e.Message));
            }
           
        }


    }
}
