using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
using ActorCore;
using GameData;
using NetWorkMessage;
using ObjectModel.User;
using Server.Config;
using Server.Core;
using Server.Data;
using Server.DB.Redis;
using Server.DB.Sql;
using StackExchange.Redis;
using Debug = MrWu.Debug.Debug;

namespace Server
{
    public partial class PropManager
    {
        /// <summary>
        /// 道具数据缓存在Redis中的用户
        /// </summary>
        public static readonly RedisKey UserPropsCacheUserIdKey = "UserPropsCacheUserId";

        /// <summary>
        /// 变化值
        /// </summary>
        private const string ChangeFiledName = "Change";

        private MainActor _MainActor;

        public MainActor MainActor
        {
            get
            {
                if (_MainActor == null)
                {
                    _MainActor = ActorManager.Instance.Get(ActorTypeId.Main) as MainActor;
                }

                return _MainActor;
            }
        }

        public ActorId GameCenterMainActorId { get; private set; }

        public Task<UserPropData> GetUserPropDataAsync(int userId)
        {
            return Task.Run(() => GetUserPropData(userId));
        }

        /// <summary>
        /// 获取玩家的道具数据
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public UserPropData GetUserPropData(int userId)
        {
            //先读取内存
            if (UserPropCache.TryGetValue(userId, out UserPropData data))
            {
                return data;
            }

            //内存有则获取内存中的数据，没有则读取Redis，再没有就读取SqlServer
            data = ReadUserPropDataRedis(userId);
            if (data != null)
            {
                SaveUser2Memory(data);
                return data;
            }

            //还没有则读取SqlServer
            Dictionary<int, int> dataDic = SelectUserProp(userId);
            data = new UserPropData(userId);
            data.Init(dataDic);
            SaveUser2Memory(data);
            SaveOriginUserPropDataRedis(userId, dataDic);
            return data;
        }

        /// <summary>
        /// 保存玩家数据到内存
        /// </summary>
        /// <param name="data"></param>
        private void SaveUser2Memory(UserPropData data)
        {
            UserPropCache.TryAdd(data.UserId, data);
        }

        /// <summary>
        /// 通知其他服务器玩家道具数据变化
        /// </summary>
        /// <param name="userId"></param>
        public void NotifyOtherPropChange(int userId)
        {
            if (!ServerInfoHelper.IsGameServer)
            {
                //通知游戏
                _ = NotifyPropChange2Game(userId);
            }

            if (!ServerInfoHelper.IsGameCenterServer)
            {
                //通知中心服务器
                NotifyPropChange2GameCenter(userId);
            }
        }

        private async Task NotifyPropChange2Game(int userid)
        {
            MainActor mainActor = MainActor;
            if (mainActor != null)
            {
                PlayerLockInfo playerLockInfo = await PlayerFun.GetPlayerLockInfoAsync(userid);
                if (playerLockInfo != null && playerLockInfo.util != null)
                {
                    ActorId actorId = new ActorId((byte)playerLockInfo.util.id, (byte)playerLockInfo.util.nodeNum,
                        ActorTypeId.Main);
                    mainActor.Send(actorId, UserPropChangeMessage.Create(userid));
                }
            }
        }

        private void NotifyPropChange2GameCenter(int userid)
        {
            MainActor mainActor = MainActor;
            if (mainActor != null)
            {
                mainActor.Send(GameCenterMainActorId, UserPropChangeMessage.Create(userid));
            }
        }

        /// <summary>
        /// 保存变化值到Redis
        /// </summary>
        /// <param name="userId">玩家的UserId</param>
        /// <param name="clearCache">清理缓存</param>
        public bool SaveUserPropDataRedis(int userId, bool clearCache)
        {
            bool isChange = false;
            //保存变化值到Redis
            if (UserPropCache.TryGetValue(userId, out UserPropData data))
            {
                if (data.IsChange())
                {
                    Dictionary<int, int> valueChange = new Dictionary<int, int>();
                    try
                    {
                        //保存变化值到Redis，并更新内存
                        string key = GetRedisKey(userId);
                        IBatch batch = redisManager.db.CreateBatch();
                        foreach (var kv in data.PropChangeDic)
                        {
                            if (kv.Value != 0)
                            {
                                batch.HashIncrementAsync(key, $"{ChangeFiledName}{kv.Key}", kv.Value);
                                valueChange.Add(kv.Key, kv.Value);
                            }
                        }

                        batch.SortedSetAddAsync(UserPropsCacheUserIdKey, userId, TimeInfo.Instance.FrameTime);
                        batch.Execute();
                        data.UpdateChangeData(valueChange);
                        NotifyOtherPropChange(userId);
                        isChange = true;
                    }
                    catch (Exception e)
                    {
                        Debug.Fatal($"保存玩家道具失败! {e.Message} {JsonPack.GetJson(valueChange)}");
                    }
                }
            }

            if (clearCache)
            {
                DeleteMemoryCache(userId);
            }

            return isChange;
        }

        /// <summary>
        /// 删除本地内存缓存
        /// </summary>
        /// <param name="userid"></param>
        private void DeleteMemoryCache(int userid)
        {
            UserPropCache.TryRemove(userid, out _);
        }

        /// <summary>
        /// 保存原始数据到Redis
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="dataDic"></param>
        public void SaveOriginUserPropDataRedis(int userid, Dictionary<int, int> dataDic)
        {
            if (dataDic.Count <= 0)
            {
                return;
            }

            string key = GetRedisKey(userid);
            var tran = redisManager.GetDataBase().CreateTransaction();
            tran.AddCondition(Condition.KeyNotExists(key));
            List<HashEntry> hashEntries = new List<HashEntry>(dataDic.Count);
            foreach (var kv in dataDic)
            {
                hashEntries.Add(new HashEntry(kv.Key.ToString(), kv.Value.ToString()));
            }

            tran.HashSetAsync(key, hashEntries.ToArray());
            tran.SortedSetAddAsync(UserPropsCacheUserIdKey, userid, TimeInfo.Instance.FrameTime);
            tran.Execute();
        }

        //Redis 缓存
        public string GetRedisKey(int userId)
        {
            return $"Game:PropData:{userId}";
        }

        public string GetTempRedisKey(int userId)
        {
            return $"tmep:{GetRedisKey(userId)}:{System.Guid.NewGuid():N}";
        }

        public async Task<UserPropData> ReadUserPropDataChangeValue(int userId, string tempKey)
        {
            string key = GetRedisKey(userId);
            Debug.Info($"临时Key：{tempKey}");

            bool renamed;
            try
            {
                // key 不存在时会返回 false（不同 SE.Redis 版本行为略有差异，异常也兜底）
                renamed = await redisManager.db.KeyRenameAsync(key, tempKey, When.NotExists);
                if (renamed)
                {
                    redisManager.db.KeyExpireAsync(tempKey, TimeSpan.FromDays(30));
                }
            }
            catch (Exception e)
            {
                renamed = false;
                Debug.Error($"重命名报错:{e.Message}");
            }

            if (!renamed)
            {
                Debug.Error("重命名不成功!");
                return null;
            }

            HashEntry[] hashEntries = await redisManager.db.HashGetAllAsync(tempKey);
            UserPropData data = new UserPropData(userId);
            foreach (var hashEntry in hashEntries)
            {
                string filedName = hashEntry.Name;

                if (filedName.StartsWith(ChangeFiledName))
                {
                    filedName = filedName.Substring(ChangeFiledName.Length);
                    if (int.TryParse(filedName, out int propId) && int.TryParse(hashEntry.Value, out int count))
                    {
                        data.PropChangeDic.AddOrUpdate(propId, count, (key, oldValue) => oldValue + count);
                    }
                    else
                    {
                        Debug.Error($"玩家的道具数据格式有问题:{userId}");
                        return null;
                    }
                }
            }

            return data;
        }

        public UserPropData ReadUserPropDataRedis(int userId, HashEntry[] hashEntries)
        {
            if (hashEntries == null || hashEntries.Length == 0)
            {
                return null;
            }

            UserPropData data = new UserPropData(userId);
            foreach (var hashEntry in hashEntries)
            {
                string filedName = hashEntry.Name;

                if (filedName.StartsWith(ChangeFiledName))
                {
                    filedName = filedName.Substring(ChangeFiledName.Length);
                    if (int.TryParse(filedName, out int propId) && int.TryParse(hashEntry.Value, out int count))
                    {
                        data.PropDic.AddOrUpdate(propId, count, (key, oldValue) => oldValue + count);
                    }
                    else
                    {
                        Debug.Error($"玩家的道具数据格式有问题:{userId}");
                        return null;
                    }
                }
                else
                {
                    if (int.TryParse(hashEntry.Name, out int propId) && int.TryParse(hashEntry.Value, out int count))
                    {
                        data.PropDic.AddOrUpdate(propId, count, (key, oldValue) => oldValue + count);
                    }
                    else
                    {
                        Debug.Error($"玩家的道具数据格式有问题:{userId}");
                        return null;
                    }
                }
            }

            return data;
        }

        public UserPropData ReadUserPropDataRedis(int userId)
        {
            string key = GetRedisKey(userId);
            HashEntry[] hashEntries = redisManager.db.HashGetAll(key);
            return ReadUserPropDataRedis(userId, hashEntries);
        }

        private void OnUserPropChange(object param)
        {
            if (param is UserPropChangeMessage userPropChangeMessage)
            {
                Debug.Info($"玩家道具变化:{userPropChangeMessage.UserId} {Thread.CurrentThread.ManagedThreadId}");
                SaveUserPropDataRedis(userPropChangeMessage.UserId, true);
            }
        }

        private void SaveUserPropCaches()
        {
            foreach (var userid in UserPropCache.Keys)
            {
                if (SaveUserPropDataRedis(userid, false))
                {
                    Debug.ImportantLog($"保存玩家道具数据:{userid}");
                }
            }
        }

        //内存缓存 只有游戏和中心服务器才有缓存 玩家的内存道具缓存
        public readonly ConcurrentDictionary<int, UserPropData> UserPropCache =
            new ConcurrentDictionary<int, UserPropData>();

        /// <summary>
        /// 玩家的道具数据
        /// </summary>
        public class UserPropData
        {
            /// <summary>
            /// 玩家ID
            /// </summary>
            public readonly int UserId;

            /// <summary>
            /// 玩家的道具数据
            /// </summary>
            public readonly ConcurrentDictionary<int, int> PropDic = new ConcurrentDictionary<int, int>();

            /// <summary>
            /// 玩家的道具变化数据
            /// </summary>
            public readonly ConcurrentDictionary<int, int> PropChangeDic = new ConcurrentDictionary<int, int>();

            public UserPropData(int userid)
            {
                UserId = userid;
            }

            public int GetPropCount(PropType propType)
            {
                return GetPropCount((int)propType);
            }

            private int GetPropCount(int propType)
            {
                if (!PropDic.TryGetValue(propType, out int count))
                {
                    count = 0;
                }

                if (PropChangeDic.TryGetValue(propType, out int changeCount))
                {
                    count += changeCount;
                }

                return count;
            }
            
            public void AddGoldLog(long changeCount, long finalValue, UserGoldLogType propType, string tag)
            {
                Instance.AddGoldLog(UserId, changeCount, finalValue, propType, tag);
            }
            
            public void AddLog(int propType, long changeCount, long finalValue, string tag)
            {
                Instance.AddLog(UserId, propType, changeCount, finalValue, tag);
            }

            public void AddLogs(List<ZiChanLogData> logs)
            {
                Instance.AddLog(UserId,logs);
            }

            public void AddProp(PropType propType, int count, string tag)
            {
                AddProp((int)propType, count, tag);
            }

            internal void AddProp(int propType, int count, string tag)
            {
                if (count == 0)
                {
                    return;
                }

                PropChangeDic.AddOrUpdate(propType, count, (key, oldValue) => oldValue + count);
                int newValue = GetPropCount((PropType)propType);
                Instance.AddLog(UserId, propType, count, newValue, tag);
            }

            public void UpdateChangeData(Dictionary<int, int> changeData)
            {
                //更新变化值到内存
                foreach (var kv in changeData)
                {
                    PropChangeDic.AddOrUpdate(kv.Key, kv.Value, (key, oldValue) => oldValue - kv.Value);
                    PropDic.AddOrUpdate(kv.Key, kv.Value, (key, oldValue) => oldValue + kv.Value);
                }
            }

            public void Init(Dictionary<int, int> dic)
            {
                foreach (var kv in dic)
                {
                    PropDic.AddOrUpdate(kv.Key, kv.Value, (key, oldValue) => kv.Value);
                }
            }

            /// <summary>
            /// 是否有变化
            /// </summary>
            /// <returns></returns>
            public bool IsChange()
            {
                if (PropChangeDic.Count == 0)
                {
                    return false;
                }

                foreach (var value in PropChangeDic.Values)
                {
                    if (value != 0)
                    {
                        return true;
                    }
                }

                return false;
            }

            public DaoJu GetOldDaoJu()
            {
                DaoJu daoJu = new DaoJu();
                daoJu.userid = UserId;
                daoJu.cansaij = this.GetPropCount(PropType.MatchCard);
                daoJu.fuhuoka = this.GetPropCount(PropType.ReviveCard);
                daoJu.clubtoken = this.GetPropCount(PropType.ClubToken);
                daoJu.clubsupertoken = this.GetPropCount(PropType.ClubSuperToken);
                return daoJu;
            }

            public Dictionary<int, int> GetAllProps()
            {
                Dictionary<int,int> dic = new Dictionary<int, int>(PropDic.Count);
                foreach (var kv in PropDic.Keys)
                {
                    dic[kv] = this.GetPropCount((PropType)kv);
                }

                foreach (var kv in PropChangeDic.Keys)
                {
                    if (!dic.ContainsKey(kv))
                    {
                        dic[kv] = this.GetPropCount((PropType)kv);
                    }
                    
                }

                return dic;
            }
        }

        #region 临时用
        
        //登录 + 主动转换，转换完成后，删除此处代码
        
        public Task ChangePropDataAsync(int userid,bool isClearCache)
        {
            return Task.Run(() => ChangePropData(userid,isClearCache));
        }

        private void ChangePropData(int userid,bool isClearCache)
        {
            string sql = $"select * from tbDaoJu2023 where userid = {userid}";
            DataSet ds = new DataSet();
            GameDB.Instance.ExceSql(dbbase.gamedb, sql, null, ds);

            if (ds.Tables.Count <= 0 || ds.Tables[0].Rows.Count <= 0)
            {
                return;
            }
            
            //转换道具数据
            int cansaij = (int)ds.Tables[0].Rows[0]["cansaij"];
            int fuhuoka = (int)ds.Tables[0].Rows[0]["fuhuoka"];
            int clubtoken = (int)ds.Tables[0].Rows[0]["clubtoken"];
            int clubsupertoken = (int)ds.Tables[0].Rows[0]["clubsupertoken"];

            if (cansaij > 0 || fuhuoka > 0 || clubtoken > 0 || clubsupertoken > 0)
            {
                string updateSql = $"update tbDaoJu2023 set cansaij = cansaij - {cansaij}, fuhuoka = fuhuoka - {fuhuoka}, clubtoken = clubtoken - {clubtoken}, clubsupertoken = clubsupertoken - {clubsupertoken} where userid = {userid}";
                GameDB.Instance.ExceSql(dbbase.gamedb, updateSql);

                UserPropData userPropData = PropManager.Instance.GetUserPropData(userid);
                userPropData.AddProp(PropType.MatchCard,cansaij,"道具转换");
                userPropData.AddProp(PropType.ReviveCard,fuhuoka,"道具转换");
                userPropData.AddProp(PropType.ClubToken,clubtoken,"道具转换");
                userPropData.AddProp(PropType.ClubSuperToken,clubsupertoken,"道具转换");
                PropManager.Instance.SaveUserPropDataRedis(userid, isClearCache);
            
                Debug.ImportantLog($"道具转换 userid:{userid} cansaij:{cansaij} fuhuoka:{fuhuoka} clubtoken:{clubtoken} clubsupertoken:{clubsupertoken}");
            }
        }

        #endregion
    }
}