using Elasticsearch.Net;
using JiaZhiQuan.Common.ElasticSearch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Senparc.Weixin.MP.Containers;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.Cache;
using Wicture.DbRESTFul.Infrastructure.Repository;

namespace JiaZhiQuan.Common
{
    public static partial class RepositoryExtension
    {

        /// <summary>
        /// 保存用户信息到ES。创建新的或者更新部分字段
        /// <param name="userInfo">必须包含id字段</param>
        /// <param name="ignorePost">是否忽略更新文章索引中用户昵称（暂时仅用于用户注销时，指定为true）</param>
        /// </summary>
        public static async Task UserInfoSave2ES(this DbRESTFulRepository repository, object userInfo, bool ignorePost = false)
        {
            var id = userInfo.GetType().GetProperty("id").GetValue(userInfo);
            var idStr = id.ToString();
            bool exist = await ESHelper.CheckExist("user", idStr);
            if (exist)
            {
                var rsp = await ESHelper.Client.UpdateAsync<StringResponse>("user", idStr, PostData.Serializable(new { doc = userInfo }));
                if (!rsp.Success)
                {
                    LoggerManager.Logger.Error("更新用户信息到ES失败:" + rsp.Body);
                    return;
                }
                // 如果存在，还需要更新 Post 中的用户昵称
                if (IsPropertyExist(userInfo, "alias"))
                {
                    string alias = userInfo.GetType().GetProperty("alias").GetValue(userInfo) as string;
                    alias = alias.Replace("'", "");
                    var query = new
                    {
                        script = new { source = $"ctx._source['userAlias']='{alias}'" },
                        query = new
                        {
                            term = new { userId = id }
                        }
                    };
                    rsp = await ESHelper.Client.UpdateByQueryAsync<StringResponse>(ESConstants.ESIndexName.Post, PostData.Serializable(query));
                    if (!rsp.Success)
                    {
                        LoggerManager.Logger.Error("更新ES中POST用户信息失败:" + rsp.Body);
                    }
                }
            }
            else
            {
                var rsp = await ESHelper.Client.CreateAsync<StringResponse>("user", idStr, PostData.Serializable(userInfo));
                if (!rsp.Success)
                {
                    LoggerManager.Logger.Error("创建用户信息到ES失败:" + rsp.Body);
                    return;
                }
            }
        }



        /// <summary>
        /// 通过用户Id加载用户信息
        /// id(string), alias, authType, authName, vip, headImage, domainTalent, publicTags, official, curProvince,watermark
        /// </summary>
        /// <param name="userIds"></param>
        /// <param name="conn">可以为空</param>
        /// <returns></returns>
        public static async Task<Dictionary<long, T>> UsersInfoByIds<T>(this DbRESTFulRepository repository, IEnumerable<long> userIds, IDbConnection conn = null) where T : UserInfoBase
        {
            userIds = userIds.Distinct();
            var list = await repository.QueryAsync<T>("select id, alias, authType, authName, vip, headImage, domainTalent, description, publicTags, official, curProvince,watermark from n_user where id in @userIds", new { userIds }, conn);
            var dic = new Dictionary<long, T>(userIds.Count());
            foreach (var id in userIds)
            {
                dic[id] = list.FirstOrDefault(e => id.ToString() == e.id);
            }
            return dic;
        }

        /// <summary>
        /// 通过用户Id加载用户信息
        /// id(string), username, alias, authType, authName, vip, headImage, domainTalent, mpRole, publicTags, official, curProvince,watermark
        /// </summary>
        /// <param name="userIds"></param>
        /// <param name="conn">可以为空</param>
        /// <returns></returns>
        public static async Task<Dictionary<long, T>> UsersInfoFullByIds<T>(this DbRESTFulRepository repository, IEnumerable<long> userIds, IDbConnection conn = null) where T : UserInfoFull
        {
            userIds = userIds.Distinct();
            var list = await repository.QueryAsync<T>("select id, username, alias, authType, authName, vip, headImage, domainTalent, description, mpRole, superior, publicTags, official, curProvince,watermark from n_user where id in @userIds", new { userIds }, conn);
            var dic = new Dictionary<long, T>(userIds.Count());
            foreach (var id in userIds)
            {
                dic[id] = list.FirstOrDefault(e => id.ToString() == e.id);
            }
            return dic;
        }

        /// <summary>
        /// 通过用户Id加载用户信息，附带是否关注
        /// id(string), alias, authType, authName, vip, headImage, domainTalent, publicTags, official, curProvince, focused,watermark
        /// </summary>
        /// <param name="userId">当前用户</param>
        /// <param name="conn">可以为空</param>
        /// <param name="ignoreFocusState">忽略当前用户关注用户列表的状态</param>
        /// <param name="ignoreFanState">忽略用户列表是否关注当前用户的状态</param>
        /// <returns></returns>
        public static async Task<Dictionary<long, T>> UsersInfoWithFocusStateByIds<T>(this DbRESTFulRepository repository, long userId, IEnumerable<long> userIds, IDbConnection conn = null, bool ignoreFocusState = false, bool ignoreFanState = false) where T : UserInfoBaseWithFocusState
        {
            userIds = userIds.Distinct();
            var dic = new Dictionary<long, T>(userIds.Count());
            if (userIds.Count() == 0)
            {
                return dic;
            }
            if (userId < 1)
            {
                ignoreFanState = true;
                ignoreFocusState = true;
            }
            var list = await repository.QueryAsync<T>("select id, alias, authType, authName, vip, headImage, domainTalent, description, publicTags, official, curProvince,watermark,growLevel level from n_user where id in @userIds", new { userIds }, conn);
            HashSet<long> focusState = null;
            if (!ignoreFocusState)
            {
                focusState = await CheckFocusState(repository, userId, userIds, conn);
            }
            HashSet<long> fanState = null;
            if (!ignoreFanState)
            {
                fanState = await CheckFanState(repository, userId, userIds, conn);
            }
            foreach (var id in userIds)
            {
                var user = list.FirstOrDefault(e => id.ToString() == e.id);
                if (user != null)
                {
                    if (focusState != null) user.focused = focusState.Contains(id) ? 1 : 0;
                    if (fanState != null) user.isFan = fanState.Contains(id) ? 1 : 0;
                }
                dic[id] = user;
            }
            return dic;
        }


        /// <summary>
        /// 返回 userIds 参数中 userId 所关注的用户Id列表
        /// </summary>
        public static async Task<HashSet<long>> CheckFocusState(this DbRESTFulRepository repository, long userId, IEnumerable<long> userIds, IDbConnection conn = null)
        {
            var list = await repository.QueryAsync<long>("select userId from n_user_fan where fanId=@userId and userId in @userIds", new { userId, userIds }, conn);
            var count = list.Count();
            if (count > 0)
            {
                return list.ToHashSet();
            }
            return new HashSet<long>(0);
        }

        /// <summary>
        /// 返回 userIds 参数中 userId 所被关注的用户Id列表
        /// </summary>
        public static async Task<HashSet<long>> CheckFanState(this DbRESTFulRepository repository, long userId, IEnumerable<long> userIds, IDbConnection conn = null)
        {
            var list = await repository.QueryAsync<long>("select fanId from n_user_fan where userId=@userId and fanId in @userIds", new { userId, userIds }, conn);
            var count = list.Count();
            if (count > 0)
            {
                return list.ToHashSet();
            }
            return new HashSet<long>(0);
        }

        /// <summary>
        /// 通过用户编号查询用户的隐私设置
        /// </summary>
        public static async Task<UserPrivacySetting> GetPrivacySettingByUserId(this DbRESTFulRepository repository, long userId, RedisCacheProvider cacheProvider, IDbConnection conn = null)
        {
            var cacheKey = CacheKeys.UserPrivacySetting(userId);
            var cacheVal = await cacheProvider.Get<int?>(cacheKey);
            if (cacheVal != null)
            {
                return UserPrivacySetting.InstanceByIntValue(cacheVal.Value);
            }
            else
            {
                UserPrivacySetting setting = await repository.QuerySingleOrDefaultAsync<UserPrivacySetting>($"select likePostRecordsHidden, collectPostRecordsHidden, focusRecordsHidden, fansHidden, personalRecommend from n_user_settings where userId={userId}", null, conn);
                bool watermark = await repository.QuerySingleOrDefaultAsync<bool>($"SELECT watermark FROM n_user WHERE id={userId}", null, conn);
                if (setting == null) setting = new UserPrivacySetting();
                setting.watermark = watermark;
                await cacheProvider.Set(cacheKey, setting.GetIntValue(), CacheKeys.UserPrivacySettingCacheSecs);
                return setting;
            }
        }

        public static async Task<int> GetUserState(this DbRESTFulRepository repository, long userId, RedisCacheProvider cacheProvider, IDbConnection conn = null)
        {
            var cacheKey = CacheKeys.UserStateValue(userId);
            var cacheVal = await cacheProvider.Get<int?>(cacheKey);
            if (cacheVal != null)
            {
                return cacheVal.Value;
            }
            else
            {
                var info = await repository.QuerySingleOrDefaultAsync<dynamic>($"select state from n_user where id={userId}", null, conn);
                var state = info?.state ?? -1;
                await cacheProvider.Set(cacheKey, state, CacheKeys.UserStateValueCacheSecs);
                return state;
            }
        }

    }

}
