using System;
using System.Collections.Generic;
using GameMessage;
using MrWu.Debug;
using Server.Core;
using Server.DB.Redis;
using StackExchange.Redis;

namespace Server
{
    public class ActivityPropManager : SingleInstance<ActivityPropManager>
    {
        private readonly Type ActivityPropDataBaseType = typeof(ActivityPropDataInfo);
        
        /// <summary>
        /// 道具信息 字典
        /// </summary>
        private readonly Dictionary<ActivityPropType, ActivityPropDataInfo> _activityPropData = new Dictionary<ActivityPropType, ActivityPropDataInfo>();
        
        /// <summary>
        /// 所有道具信息
        /// </summary>
        private readonly List<ActivityPropDataInfo> _activityPropDataInfos = new List<ActivityPropDataInfo>();
        
        public ActivityPropManager()
        {
            Type[] types = GetType().Assembly.GetTypes();
            foreach (var type in types)
            {
                if (type.IsSubclassOf(ActivityPropDataBaseType))
                {
                   ActivityPropDataInfo dataInfo = (ActivityPropDataInfo)Activator.CreateInstance(type);
                   _activityPropData[dataInfo.PropType] = dataInfo;
                   _activityPropDataInfos.Add(dataInfo);
                }
            }
        }
        
        /// <summary>
        /// 获取道具的过期时间
        /// </summary>
        /// <param name="propType"></param>
        /// <param name="propName"></param>
        /// <returns></returns>
        public DateTime GetExpireTime(ActivityPropType propType,string propName)
        {
            if (!_activityPropData.ContainsKey(propType))
            {
                //没有这个数据，那么现在就过期
                return DateTime.Now;
            }
            
            return _activityPropData[propType].GetExpireTime(propName);
        }

        /// <summary>
        /// 获取道具数据的KEY
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="propType"></param>
        /// <param name="propName"></param>
        /// <returns></returns>
        private string GetRedisKey(int userid, ActivityPropType propType, string propName)
        {
            return $"ActivityPropData:{userid}:{(int)propType}:{propName}";
        }
        
        /// <summary>
        /// 获取某个玩家的所有道具数据
        /// </summary>
        /// <param name="userid">玩家的userid</param>
        /// <returns></returns>
        public List<ActivityPropData> GetActivityPropDatas(int userid)
        {
            List<ActivityPropData> datas = new List<ActivityPropData>();
            List<RedisKey> keys = new List<RedisKey>();
            foreach (var activityPropDataInfo in _activityPropDataInfos)
            {
                string[] propNames = activityPropDataInfo.GetPropNames();
                for (int i = 0; i < propNames.Length; i++)
                {
                    keys.Add(GetRedisKey(userid, activityPropDataInfo.PropType, propNames[i]));
                    datas.Add(new ActivityPropData()
                    {
                        PropName = propNames[i],
                        Id = (int)activityPropDataInfo.PropType,
                    });
                }
            }

            RedisValue[] redisValues = redisManager.db.StringGet(keys.ToArray());
            for (int i = 0; i < redisValues.Length; i++)
            {
                if (!redisValues[i].IsNull && long.TryParse(redisValues[i], out long count))
                {
                    datas[i].Count = count;
                }
            }

            return datas;
        }
        
        /// <summary>
        /// 获取某个道具类型的 数据
        /// </summary>
        /// <param name="userid">谁的</param>
        /// <param name="propType">道具类型</param>
        /// <returns></returns>
        public ActivityPropData[] GetActivityPropDatas(int userid,ActivityPropType propType)
        {
            if (!_activityPropData.ContainsKey(propType))
            {
                Debug.Fatal($"没有这个道具类型:{propType}");
                return null;
            }

            string[] propNames = _activityPropData[propType].GetPropNames();
            RedisKey[] keys = new RedisKey[propNames.Length];
            for (int i = 0; i < propNames.Length; i++)
            {
                keys[i] = GetRedisKey(userid, propType, propNames[i]);
            }

            RedisValue[] redisValues = redisManager.db.StringGet(keys);
            ActivityPropData[] datas = new ActivityPropData[redisValues.Length];
            for (int i = 0; i < datas.Length; i++)
            {
                datas[i] = new ActivityPropData();
                datas[i].PropName = propNames[i];
                datas[i].Id = (int)propType;
                if (!redisValues[i].IsNull && long.TryParse(redisValues[i], out long count))
                {
                    datas[i].Count = count;
                }
            }

            return datas;
        }


        /// <summary>
        /// 获取某种道具的全部数量
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="propType"></param>
        /// <returns></returns>
        public long GetActivityPropCount(int userid, ActivityPropType propType)
        {
            if (!_activityPropData.ContainsKey(propType))
            {
                Debug.Fatal($"没有这个道具类型:{propType}");
                return 0;
            }

            long result = 0;
            string[] propNames = _activityPropData[propType].GetPropNames();
            RedisKey[] keys = new RedisKey[propNames.Length];
            for (int i = 0; i < propNames.Length; i++)
            {
                keys[i] = GetRedisKey(userid, propType, propNames[i]);
            }

            RedisValue[] redisValues = redisManager.db.StringGet(keys);
            for (int i = 0; i < redisValues.Length; i++)
            {
                if (!redisValues[i].IsNull && long.TryParse(redisValues[i], out long count))
                {
                    result += count;
                }
            }
            return result;
        }

        /// <summary>
        /// 添加道具数据
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="propType"></param>
        /// <param name="count"></param>
        public void AddActivityPropData(int userid,ActivityPropType propType,long count)
        {
            if (!_activityPropData.ContainsKey(propType))
            {
                Debug.Fatal($"没有这个道具类型:{userid} {propType} {count}");
                return;
            }

            if (count <= 0)
            {
                Debug.Error("不能添加小于等于0的道具数据");
                return;
            }

            string[] propNames = _activityPropData[propType].GetPropNames();
            string nowPropName = propNames[propNames.Length - 1];

            string key = GetRedisKey(userid, propType, nowPropName);
            TimeSpan ts = _activityPropData[propType].GetExpireTime(nowPropName) - DateTime.Now;
            IBatch batch = redisManager.db.CreateBatch();
            batch.StringIncrementAsync(key, count);
            batch.KeyExpireAsync(key, ts);
            batch.Execute();
        }

        /// <summary>
        /// 添加道具数据
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="propType"></param>
        /// <param name="propName"></param>
        /// <param name="count"></param>
        public void AddActivityPropData(int userid,ActivityPropType propType,string propName,long count)
        {
            if (!_activityPropData.ContainsKey(propType))
            {
                Debug.Fatal($"没有这个道具类型:{userid} {propType} {count}");
                return;
            }

            if (count <= 0)
            {
                Debug.Error("不能添加小于等于0的道具数据!");
                return;
            }
            
            string key = GetRedisKey(userid, propType, propName);
            TimeSpan ts = _activityPropData[propType].GetExpireTime(propName) - DateTime.Now.AddDays(-1); //延时一天再过期
            IBatch batch = redisManager.db.CreateBatch();
            batch.StringIncrementAsync(key, count);
            batch.KeyExpireAsync(key, ts);
            batch.Execute();
        }

        /// <summary>
        /// 移除道具数据
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="propType"></param>
        /// <param name="count"></param>
        /// <returns>消耗的道具信息</returns>
        public List<ActivityPropData> DecActivityPropData(int userid, ActivityPropType propType, long count)
        {
            if (!_activityPropData.ContainsKey(propType))
            {
                Debug.Fatal($"没有这个道具类型:{userid} {propType} {count}");
                return null;
            }

            if (count <= 0)
            {
                return null;
            }
            
            string[] propNames = _activityPropData[propType].GetPropNames();
            RedisKey[] keys = new RedisKey[propNames.Length];
            long[] currentCount = new long[propNames.Length];
            long totalCount = 0;
            for (int i = 0; i < keys.Length; i++)
            {
                keys[i] = GetRedisKey(userid, propType, propNames[i]);
            }
            
            RedisValue[] redisValues = redisManager.db.StringGet(keys);
            for (int i = 0; i < redisValues.Length; i++)
            {
                if (!redisValues[i].IsNull && long.TryParse(redisValues[i], out long countValue))
                {
                    currentCount[i] = countValue;
                    totalCount += countValue;
                }
            }

            //道具数量不足
            if (totalCount < count)
            {
                return null;
            }

            List<ActivityPropData> result = new List<ActivityPropData>();
            //剩余要消耗的数量
            long remainingCount = count;
            IBatch batch = redisManager.db.CreateBatch();
            for (int i = 0; i < propNames.Length; i++)
            {
                long consumeCount = (int)Math.Min(currentCount[i], count);
                if (consumeCount > 0)
                {
                    string key = GetRedisKey(userid, propType, propNames[i]);
                    batch.StringDecrementAsync(key, consumeCount);
                    remainingCount -= consumeCount;
                    result.Add(new ActivityPropData()
                    {
                        PropName = propNames[i],
                        Id = (int)propType,
                        Count = consumeCount
                    });
                }
            }
            
            batch.Execute();
            return result;
        }
    }
}