using System;
using System.Collections.Generic;
using Top.Api.Util;
using System.Threading;
using Top.Api.Report;
namespace Top.Api.Security
{
    /// 
    /// 加、解密核心类
    /// 
    public class SecurityCore : SecurityConstants
    {
        private static readonly ITopLogger Log = Top.Api.Log.Instance;
        // 缓存用户单独分配秘钥,需要加同步锁
        private static readonly IDictionary AppUserSecretCache = new Dictionary();
        private static readonly IDictionary AppSecretCache = new Dictionary();
        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 asynQueueKey = new Dictionary();
        private static readonly IDictionary> AllAppConfig = new Dictionary>();
        private bool streetest;
        public static IDictionary GetAppUserSecretCache()
        {
            return AppUserSecretCache;
        }
        /// 
        /// 判断密文是否支持检索
        /// 
        /// 
        /// 
        /// 
        public bool IsIndexEncrypt(string key, Nullable version)
        {
            if (version != null && version < 0)
            {
                key = PREVIOUS + key;
            }
            else
            {
                key = CURRENT + key;
            }
            IDictionary appConfig = getAppConfig();
            if (appConfig == null)
            {
                return false;
            }
            object encryptType = null;
            appConfig.TryGetValue(key, out encryptType);
            return INDEX_ENCRYPT_TYPE.Equals(encryptType);
        }
        private IDictionary getAppConfig()
        {
            IDictionary appConfig = null;
            AllAppConfig.TryGetValue(topClient.appKey, out appConfig);
            return appConfig;
        }
        /// 
        /// 获取压缩长度
        /// 
        /// 
        public int GetCompressLen()
        {
            IDictionary 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;
        }
        /// 
        /// 获取滑动窗口大小
        /// 
        /// 
        public int GetSlideSize()
        {
            IDictionary 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;
        }
        
        /// 
        /// 获取秘钥
        /// 
        /// 
        /// 
        /// 
        public SecretContext GetSecret(string session, Nullable 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 secretVersion)
        {
            if (session == null)
            {
                return this.topClient.appKey;
            }
            if (secretVersion == null)
            {
                return session;
            }
            return session + "_" + secretVersion;
        }
        /// 
        /// 从本地获取秘钥信息
        /// 
        /// 
        /// 
        /// 
        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;
        }
        /// 
        /// 调用获取秘钥api
        /// 
        /// 
        /// 
        /// 
        private SecretContext CallSecretApi(string session, Nullable 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 appConfig = null;
                if (!string.IsNullOrEmpty(response.AppConfig))
                {
                    appConfig = (IDictionary)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 appConfig)
        {
            lock (AppLock)
            {
                  if (!AllAppConfig.ContainsKey(topClient.appKey))
                  {
                      AllAppConfig.Add(topClient.appKey, appConfig);
                  }
            }
        }
        private void PutToCache(string session, Nullable 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);
                    }
                }
            }
        }
        /// 
        /// 设置空缓存
        /// 
        /// 
        private void SetNullCache(SecretContext secretContext)
        {
            long currentTime = TopUtils.GetCurrentTimeMillis();
            secretContext.InvalidTime = currentTime + (DEFAULT_INTERVAL * 1000);
            secretContext.MaxInvalidTime = currentTime + (DEFAULT_MAX_INTERVAL * 1000);
        }
        /// 
        /// 异步更新秘钥
        /// 
        /// 
        /// 
        private void AsynUpdateSecret(string session, Nullable 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));
            }
           
        }
    }
}