using Common;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;

namespace KC.Cache.Redis
{
    public class RedisHelper
    {
        #region 属性

        /// <summary>
        /// 数据库编号
        /// </summary>
        private int DbNum { get; }

        /// <summary>
        /// 连接
        /// </summary>
        private readonly ConnectionMultiplexer _conn;

        #endregion

        #region 构造函数


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbNum">数据库编号</param>
        /// <param name="readWriteHosts">链接地址</param>
        public RedisHelper()
        {
            this.DbNum = ConfigHelper.GetValue("RedisDbNum").TryToInt32();
            string readWriteHosts = ConfigHelper.GetValue("RedisExchangeHosts");
            this._conn = ConnectionMultiplexer.Connect(readWriteHosts);
        }

        #endregion 构造函数

        #region 方法

        /// <summary>
        /// 单个string值存入
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool StringSet(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {
            return Do(db => db.StringSet(key, value, expiry));
        }

        /// <summary>
        /// 单个string值存入
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool StringSet<T>(string key, T value, TimeSpan? expiry = default(TimeSpan?))
        {
            return Do(db => db.StringSet(key, ConvertJson(value), expiry));
        }

        /// <summary>
        /// 对象存储
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public void ListSet<T>(string key, T value)
        {
            Do(db => db.ListRightPush(key, value.TryToJson()));
        }


        /// <summary>
        /// 删除单个key
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns>是否删除成功</returns>
        public bool KeyDelete(string key)
        {
            return Do(db => db.KeyDelete(key));
        }
        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>
        public string StringGet(string key)
        {
            return Do(db => db.StringGet(key));
        }



        /// <summary>
        /// 获取指定key的List
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<T> ListGet<T>(string key)
        {
            return Do(redis =>
            {
                try
                {
                    var values = redis.ListRange(key);
                    return ConvetList<T>(values);
                }
                catch (Exception)
                {
                    return new List<T>();
                }
            });
        }

        /// <summary>
        /// 获取所有key
        /// </summary>
        /// <returns></returns>
        public List<string> GetAllKeys()
        {
            var res = Do(db =>
            {
                EndPoint[] endpoints = _conn.GetEndPoints();
                List<string> keyList = new List<string>();
                foreach (EndPoint ep in endpoints)
                {
                    var server = _conn.GetServer(ep);
                    var keys = server.Keys(DbNum, "*");
                    foreach (var item in keys)
                    {
                        keyList.Add((string)item);
                    }
                };
                return keyList;
            });
            return res;
        }

        /// <summary>
        /// 获取指定key的object
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public T ListRangeObj<T>(string key)
        {;
            return Do(redis =>
            {
                try
                {
                    var values = redis.ListRange(key);
                    var content = values.Count() > 0 ? values[0] : new RedisValue();
                    return ConvertObj<T>(content);
                }
                catch (Exception)
                {

                    Thread.Sleep(1000);
                    var values = redis.ListRange(key);
                    var content = values.Count() > 0 ? values[0] : new RedisValue();
                    return ConvertObj<T>(content);
                }
            });
        }

        /// <summary>
        /// 判断key是否存储
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public bool KeyExists(string key)
        {
            return Do(db => db.KeyExists(key));
        }

        /// <summary>
        /// 执行操作
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <returns></returns>
        private T Do<T>(Func<IDatabase, T> func)
        {
            var database = _conn.GetDatabase(DbNum);
            return func(database);
        }

        /// <summary>
        /// 设置Key的时间
        /// </summary>
        /// <param name="key">redis key</param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool KeyExpire(string key, TimeSpan? expiry = default(TimeSpan?))
        {
            return Do(db => db.KeyExpire(key, expiry));
        }

        /// <summary>
        /// 删除多个key
        /// </summary>
        /// <param name="keys">rediskey</param>
        /// <returns>成功删除的个数</returns>
        public long KeyDelete(List<string> keys)
        {
            return Do(db => db.KeyDelete(ConvertRedisKeys(keys)));
        }

        #endregion

        #region 转换
        /// <summary>
        /// 转换成集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="values"></param>
        /// <returns></returns>
        private List<T> ConvetList<T>(RedisValue[] values)
        {
            List<T> result = new List<T>();
            //try
            //{
            //    //一个Key下面存了多个相同实体
            //    foreach (RedisValue value in values)
            //    {
            //        var model = ConvertObj<T>(value);
            //        result.Add(model);
            //    }
            //}
            //catch (Exception e)
            //{
            //一个Key下面存的是一个集合
            foreach (RedisValue value in values)
            {
                List<T> temp = JsonConvert.DeserializeObject<List<T>>(value);
                result = result.Concat(temp).ToList();
            }
            // }
            return result;
        }

        /// <summary>
        /// 转换成Json字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        private string ConvertJson<T>(T value)
        {
            string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value);
            return result;
        }


        /// <summary>
        /// 转换成对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        private T ConvertObj<T>(RedisValue value)
        {
            if (!value.IsNullOrEmpty)
            {
                var val = value.ToString();
                try
                {
                    if (!val.IsEmpty() && val.IsJson())
                        return JsonConvert.DeserializeObject<T>(val);
                    return default(T);
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
            return default(T);
        }

        /// <summary>
        /// 批量转换成RedisKey
        /// </summary>
        /// <param name="redisKeys"></param>
        /// <returns></returns>
        private RedisKey[] ConvertRedisKeys(List<string> redisKeys)
        {
            return redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();
        }

        #endregion



    }
}