using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using CSRedis;
using Siger.Middlelayer.Common.Configuration;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Common.Log;
using Siger.Middlelayer.Redis.Repositories;

namespace Siger.Middlelayer.Redis
{
    public partial class RedisCache : IDisposable
    {
        private bool _disposed;


        /// <summary>
        /// redis超时时间
        /// </summary>
        private static int timeOut = 150;
        private static CSRedisClient _client = null;
        private static DateTime? initDate = null;
        private static void JudgeClientState()
        {
            if (initDate == null)
            {
                _client = null;
                Connect();
            }
            else if (DateTime.Now.AddSeconds(-timeOut) > ((DateTime)initDate))
            {
                _client = null;
                Connect();
            }
            else
            {
                initDate = DateTime.Now;
            }
        }
        public static CSRedisClient Client
        {
            get
            {
                JudgeClientState();
                return _client;
            }
            set
            {
                _client = value;
                initDate = DateTime.Now;
            }
        }
        private static string _connectionStr;
        private readonly int _defaultDataBase;
        private readonly int _poolSize;
        private readonly int _expiryMinutes;
        private ConcurrentDictionary<string, TokenValue> tokens;
        private ApiCountRepository apiCountRepository;

        private static RedisCache _cacheString;
        public static RedisCache Instance = _cacheString ?? (_cacheString = new RedisCache());

        ~RedisCache()
        {
            DoDispose();
        }

        private void DoDispose()
        {
            if (!_disposed)
            {
                Client.Dispose();
                tokens = null;
                apiCountRepository = null;
                _disposed = true;
            }
        }

        public RedisCache()
        {
            var useOneDb = ConfigManager.GetValue("DbSetting", "UseOneDb", true);
            if (useOneDb)
            {
                _connectionStr = ConfigManager.GetValue("RedisSetting", "ConnectionString", string.Empty);
                if (string.IsNullOrEmpty(_connectionStr))
                {
                    throw new Exception("Redis connection string not found.");
                }
                _poolSize = ConfigManager.GetValue("RedisSetting", "PoolSize", 1);
                _defaultDataBase = ConfigManager.GetValue("RedisSetting", "DefaultDatabase", 0);

                _connectionStr += $",poolsize={_poolSize},defaultDatabase={_defaultDataBase}";
            }

            if (_client == null)
            {
                Connect();
            }
            _expiryMinutes = ConfigManager.GetValue("RedisSetting", "TokenExpiryMinutes", 750);
            tokens = new ConcurrentDictionary<string, TokenValue>();
        }

        public static CSRedisClient GetClusterRedis()
        {
            var redisHosts = ConfigManager.GetValue("DbClusterSetting", "RedisHost", "");
            if (!string.IsNullOrWhiteSpace(redisHosts))
            {
                var hosts = redisHosts.Split(';').ToArray();
                var connections = new string[hosts.Length];
                for (var i = 0; i < hosts.Length; i++)
                {
                    connections[i] = hosts[i];
                }
                Client = new CSRedisClient(null, connections);
            }
            return Client;
        }

        public static void Connect()
        {
            try
            {
                if (_client == null)
                {
                    var useOneDb = ConfigManager.GetValue("DbSetting", "UseOneDb", true);
                    if (useOneDb)
                    {
                        Client = new CSRedisClient(_connectionStr);
                        var node = Client.Nodes.FirstOrDefault();
                        if (node.Value?.Policy != null)
                        {
                            node.Value.Policy.IsThrowGetTimeoutException = false;
                        }
                    }
                    else
                    {
                        Client = GetClusterRedis();
                    }
                }
            }
            catch (Exception e)
            {
                throw new Exception("connect redis failed:" + e.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value">"pid_uid"</param>
        public void AddToken(string key, string value)
        {
            Connect();

            Client.Set(key, value, (int)TimeSpan.FromMinutes(_expiryMinutes).TotalSeconds);

            GetTokenValue(key);
        }

        /// <summary>
        /// 不为空且已经登录
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public bool IsValidToken(string token)
        {
            try
            {
                //if (_client == null)
                //{
                //    Connect();
                //}
                var value = Client?.Get(token);
                if (value != null)
                {
                    if (!value.StartsWith(TokenHelper.Prefix))
                    {
                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"IsValidToken by {token} failed, error:" + ex);
                return false;
            }
            return false;
        }

        public void ResetToken(string token, string url)
        {
            Task.Run(() =>
            {
                var value = Client.Get(token);
                if (value != null)
                {
                    Client.Set(token, value, (int)TimeSpan.FromMinutes(_expiryMinutes).TotalSeconds);
                }

                //接口计数
                //var tokenValue = GetTokenValue(token);
                //if (tokenValue != null)
                //{
                //    apiCountRepository?.SetApiCount(url);
                //}
            });
        }

        /// <summary>
        /// 添加失败记录
        /// </summary>
        /// <param name="token"></param>
        /// <param name="url"></param>
        /// <param name="msg"></param>
        /// <param name="error"></param>
        /// <param name="userid"></param>
        /// <param name="parameter"></param>
        public void AddFailedAPI(string token, string url, string msg, string error, string parameter)
        {
            Task.Run(() =>
            {
                var tokenValue = GetTokenValue(token);
                if (tokenValue != null)
                {
                    apiCountRepository?.AddFiledApi(url, msg, error, tokenValue.UserId, parameter);
                }
            });
        }

        public TokenValue GetTokenValue(string token)
        {
            try
            {
                //if (_client == null)
                //{
                //    Connect();
                //}

                //if (tokens.ContainsKey(token))
                //{
                //    return tokens[token];
                //}

                var value = Client?.Get(token);
                var values = value?.Split('_');
                if (values?.Length == 4)
                {
                    int.TryParse(values[0], out var companyid);
                    int.TryParse(values[1], out var projectid);
                    int.TryParse(values[2], out var userid);
                    int.TryParse(values[3], out var roleid);

                    //if(companyid > 0)
                    //{
                    //    apiCountRepository = new ApiCountRepository(companyid, projectid, false);
                    //}

                    var tokenValue = new TokenValue(companyid, projectid, userid, roleid);
                    //tokens.AddOrUpdate(token, tokenValue, (oldkey, oldvalue) => tokenValue);
                    return tokenValue;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"GetTokenValue by {token} failed, error:" + ex);
                return null;
            }
            return null;
        }

        public void Dispose()
        {
            DoDispose();
            GC.SuppressFinalize(this);
        }
    }
}
