using Elasticsearch.Net;
using JiaZhiQuan.Common.Config;
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
    {


        public class MockActionGenerationModel
        {
            public long targetId { get; set; }
            public string action { get; set; }
            public string content { get; set; }
            public long mockUserId { get; set; }
            public DateTime execTime { get; set; }
        }

        public static List<DateTime> GenerateMockTimes(this DbRESTFulRepository repository, DateTime startMockTime, int hours, int count)
        {
            var rand = new Random();
            var list = new List<DateTime>();
            if (hours < 25)
            {
                var startTicks = startMockTime.Ticks;
                var endMockTime = startMockTime.AddHours(hours);
                var allSecs = (int)((endMockTime.Ticks - startTicks) / 10000000);
                for (var i = 0; i < count; i++)
                {
                    // 给定天内随机，如果在1~6点，则添加16个小时再模拟
                    var randSecs = rand.Next(allSecs);
                    var curTime = new DateTime(startTicks + randSecs * 10000000L);
                    if (curTime.Hour > 1 && curTime.Hour < 6)
                    {
                        if (rand.Next(100) < 95)
                        {
                            curTime = curTime.AddHours(16);
                        }
                    }
                    list.Add(curTime);
                }
                return list;
            }
            else
            {
                // 按天来随机
                var days = Math.Ceiling(hours / 24f);
                if (days > 15) days = 15;
                var pro = ConfigFromDb.RandomProbabilities[(int)days - 2];
                var totalProp = pro.Sum();
                // 计算一天的人数数量
                var dayCountDic = new Dictionary<int, int>();
                for (var idx = 0; idx < count; idx++)
                {
                    var rnum = rand.Next(totalProp + 1);
                    var tmp = 0;
                    var curDay = 1;
                    for (var i = 0; i < pro.Count; i++)
                    {
                        if (i == pro.Count - 1 || (rnum >= tmp && rnum <= tmp + pro[i]))
                        {
                            curDay = i + 1;
                            break;
                        };
                        tmp += pro[i];
                    }
                    if (dayCountDic.ContainsKey(curDay)) dayCountDic[curDay] = dayCountDic[curDay] + 1;
                    else dayCountDic[curDay] = 1;
                }
                var index = 0;
                foreach (var key in dayCountDic.Keys)
                {
                    var start = startMockTime.AddDays(key - 1);
                    var end = startMockTime.AddDays(key);

                    var allSecs = (int)((end.Ticks - start.Ticks) / 10000000);
                    for (var idx = 0; idx < dayCountDic[key]; idx++)
                    {
                        // 给定天内随机，如果在1~6点，则添加16个小时再模拟
                        var randSecs = rand.Next(allSecs);
                        var curTime = new DateTime(start.Ticks + randSecs * 10000000L);
                        if (curTime.Hour > 1 && curTime.Hour < 6)
                        {
                            if (rand.Next(100) < 95)
                            {
                                curTime = curTime.AddHours(16);
                            }
                        }
                        list.Add(curTime);
                        index++;
                    }
                }
                return list;
            }
        }

        /// <summary>
        /// 获取随机用户模拟信息
        /// </summary>
        /// <param name="modelList">如果不传，则从数据库查手机号大于90000000000的用户</param>
        /// <returns></returns>
        public async static Task<List<MockActionGenerationModel>> GenerateMockActions(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long targetId, string action, int expectCount, List<MockActionGenerationModel> modelList = null)
        {
            if (modelList == null)
            {
                modelList = (await GetMockUserIdsRandom(repository, expectCount, 0))
                    .Select(e => new MockActionGenerationModel { mockUserId = e }).ToList();
            }
            var rand = new Random();
            if (hours < 25)
            {
                var startTicks = startMockTime.Ticks;
                var endMockTime = startMockTime.AddHours(hours);
                var allSecs = (int)((endMockTime.Ticks - startTicks) / 10000000);
                foreach (var item in modelList)
                {
                    item.targetId = targetId;
                    item.action = action;
                    // 给定天内随机，如果在1~6点，则添加16个小时再模拟
                    var randSecs = rand.Next(allSecs);
                    var curTime = new DateTime(startTicks + randSecs * 10000000L);
                    if (curTime.Hour > 1 && curTime.Hour < 6)
                    {
                        if (rand.Next(100) < 95)
                        {
                            curTime = curTime.AddHours(16);
                        }
                    }
                    item.execTime = curTime;
                }
                return modelList;
            }
            else
            {
                // 按天来随机
                var days = Math.Ceiling(hours / 24f);
                if (days > 15) days = 15;
                var pro = ConfigFromDb.RandomProbabilities[(int)days - 2];
                var totalProp = pro.Sum();
                // 计算一天的人数数量
                var dayCountDic = new Dictionary<int, int>();
                for (var idx = 0; idx < modelList.Count; idx++)
                {
                    var rnum = rand.Next(totalProp + 1);
                    var tmp = 0;
                    var curDay = 1;
                    for (var i = 0; i < pro.Count; i++)
                    {
                        if (i == pro.Count - 1 || (rnum >= tmp && rnum <= tmp + pro[i]))
                        {
                            curDay = i + 1;
                            break;
                        };
                        tmp += pro[i];
                    }
                    if (dayCountDic.ContainsKey(curDay)) dayCountDic[curDay] = dayCountDic[curDay] + 1;
                    else dayCountDic[curDay] = 1;
                }
                var index = 0;
                foreach(var key in dayCountDic.Keys)
                {
                    var start = startMockTime.AddDays(key - 1);
                    var end = startMockTime.AddDays(key);

                    var allSecs = (int)((end.Ticks - start.Ticks) / 10000000);
                    for (var idx = 0; idx < dayCountDic[key]; idx++)
                    {
                        var item = modelList[index];
                        item.targetId = targetId;
                        item.action = action;
                        // 给定天内随机，如果在1~6点，则添加16个小时再模拟
                        var randSecs = rand.Next(allSecs);
                        var curTime = new DateTime(start.Ticks + randSecs * 10000000L);
                        if (curTime.Hour > 1 && curTime.Hour < 6)
                        {
                            if (rand.Next(100) < 95)
                            {
                                curTime = curTime.AddHours(16);
                            }
                        }
                        item.execTime = curTime;
                        index++;
                    }
                }
                return modelList;
            }
        }

        /// <summary>
        /// 给文章添加点赞数量任务
        /// </summary>
        public static async Task AddMockPostThumbsup(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long postId, int expectCount, IDbConnection conn = null)
        {
            var postInfo = await repository.QuerySingleOrDefaultAsync<dynamic>($"select id, state, userId, checkCntLevel from n_post where id={postId}", null, conn);
            if (postInfo == null || postInfo.state != 1 || "D".Equals(postInfo.checkCntLevel)) return;
            
            var list = await repository.GenerateMockActions(startMockTime, hours, postId, "POST_THUMBSUP", expectCount);
            if (list.Count > 0)
            {
                var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                var size = 500;
                for (var i = 0; i < list.Count; i += size)
                {
                    var leftCount = list.Count - i;
                    var subList = list.Skip(i).Take(leftCount >= size ? size : leftCount).ToList();
                    if (subList.Count > 0)
                    {
                        await repository.QueryAsync<dynamic>($"insert ignore into n_mock_action_records (targetId, action, content, mockUserId, execTime, createTime) values (@targetId, @action, @content, @mockUserId, @execTime, '{time}')", subList, conn, null, false);
                    }
                }
                await repository.QueryAsync<dynamic>("update n_post set mockThumbsupCnt=mockThumbsupCnt+" + list.Count + " where id=" + postId, null, conn, null, false);
            }
        }

        /// <summary>
        /// 给文章添加收藏数量任务
        /// </summary>
        public static async Task AddMockPostCollection(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long postId, int expectCount, IDbConnection conn = null)
        {
            var postInfo = await repository.QuerySingleOrDefaultAsync<dynamic>($"select id, state, userId, checkCntLevel from n_post where id={postId}", null, conn);
            if (postInfo == null || postInfo.state != 1 || "D".Equals(postInfo.checkCntLevel)) return;
            
            var list = await repository.GenerateMockActions(startMockTime, hours, postId, "POST_COLLECTION", expectCount);
            if (list.Count > 0)
            {
                var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                var size = 500;
                for (var i = 0; i < list.Count; i += size)
                {
                    var leftCount = list.Count - i;
                    var subList = list.Skip(i).Take(leftCount >= size ? size : leftCount).ToList();
                    if (subList.Count > 0)
                    {
                        await repository.QueryAsync<dynamic>($"insert ignore into n_mock_action_records (targetId, action, content, mockUserId, execTime, createTime) values (@targetId, @action, @content, @mockUserId, @execTime, '{time}')", subList, conn, null, false);
                    }
                }
                await repository.QueryAsync<dynamic>("update n_post set mockCollectionCnt=mockCollectionCnt+" + list.Count + " where id=" + postId, null, conn, null, false);
            }
        }

        /// <summary>
        /// 给文章添加分享数量任务
        /// </summary>
        public static async Task AddMockPostShare(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long postId, int expectCount, IDbConnection conn = null)
        {
            var postInfo = await repository.QuerySingleOrDefaultAsync<dynamic>($"select id, state, userId, checkCntLevel from n_post where id={postId}", null, conn);
            if (postInfo == null || postInfo.state != 1 || "D".Equals(postInfo.checkCntLevel)) return;
            
            var list = await repository.GenerateMockActions(startMockTime, hours, postId, "POST_SHARE", expectCount);
            if (list.Count > 0)
            {
                var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                var size = 500;
                for (var i = 0; i < list.Count; i += size)
                {
                    var leftCount = list.Count - i;
                    var subList = list.Skip(i).Take(leftCount >= size ? size : leftCount).ToList();
                    if (subList.Count > 0)
                    {
                        await repository.QueryAsync<dynamic>($"insert ignore into n_mock_action_records (targetId, action, content, mockUserId, execTime, createTime) values (@targetId, @action, @content, @mockUserId, @execTime, '{time}')", subList, conn, null, false);
                    }
                }
                await repository.QueryAsync<dynamic>("update n_post set mockShareCnt=mockShareCnt+" + list.Count + " where id=" + postId, null, conn, null, false);
            }
        }
        
        /// <summary>
        /// 按文章添加作者主页访问数量任务
        /// </summary>
        public static async Task AddMockPostPPVisit(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long postId, int expectCount, IDbConnection conn = null)
        {
            var postInfo = await repository.QuerySingleOrDefaultAsync<dynamic>($"select id, state, userId, checkCntLevel from n_post where id={postId}", null, conn);
            if (postInfo == null || postInfo.state != 1 || "D".Equals(postInfo.checkCntLevel)) return;
            
            var list = await repository.GenerateMockActions(startMockTime, hours, postId, "POST_PPVISIT", expectCount);
            if (list.Count > 0)
            {
                var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                var size = 500;
                for (var i = 0; i < list.Count; i += size)
                {
                    var leftCount = list.Count - i;
                    var subList = list.Skip(i).Take(leftCount >= size ? size : leftCount).ToList();
                    if (subList.Count > 0)
                    {
                        await repository.QueryAsync<dynamic>($"insert ignore into n_mock_action_records (targetId, action, content, mockUserId, execTime, createTime) values (@targetId, @action, @content, @mockUserId, @execTime, '{time}')", subList, conn, null, false);
                    }
                }
            }
        }
        
        /// <summary>
        /// 按用户添加主页访问数量任务
        /// </summary>
        public static async Task AddMockUserPPVisit(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long userId, int expectCount, IDbConnection conn = null)
        {
            var userInfo = await repository.QuerySingleOrDefaultAsync<dynamic>($"select id, state from n_user where id={userId}", null, conn);
            if (userInfo == null || userInfo.state != 0) return;
            
            var list = await repository.GenerateMockActions(startMockTime, hours, userId, "USER_PPVISIT", expectCount);
            if (list.Count > 0)
            {
                var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                var size = 500;
                for (var i = 0; i < list.Count; i += size)
                {
                    var leftCount = list.Count - i;
                    var subList = list.Skip(i).Take(leftCount >= size ? size : leftCount).ToList();
                    if (subList.Count > 0)
                    {
                        await repository.QueryAsync<dynamic>($"insert ignore into n_mock_action_records (targetId, action, content, mockUserId, execTime, createTime) values (@targetId, @action, @content, @mockUserId, @execTime, '{time}')", subList, conn, null, false);
                    }
                }
            }
        }

        /// <summary>
        /// 【已废弃】给文章添加评论数量任务
        /// </summary>
        public static async Task AddMockPostComment(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long postId, int expectCount)
        {
            await Task.CompletedTask;
            // var records = await repository.GenerateMockActions(DateTime.Now, hours, postId, "COMMENT", expectCount);
            // if (records.Count == 0) return;
            // // 获取模拟评论
            // var primaryType = await repository.QuerySingleOrDefaultAsync<int>($"select primaryType from n_post where id={postId}");
            // if (primaryType == 0) return;
            // var contList = (await repository.QueryAsync<string>($"select `comment`, rand() as rid from n_mock_comment_cnt where primaryType={primaryType} order by rid asc limit " + expectCount)).ToList();
            // if (contList.Count == 0) return;
            // if (contList.Count < records.Count)
            // {
            //     records = records.Take(contList.Count).ToList();
            // }
            // for (var i = 0; i < contList.Count; i++)
            // {
            //     records[i].content = contList[i];
            // }
            // var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            // await repository.QueryAsync<dynamic>($"insert ignore into n_mock_action_records (targetId, action, content, mockUserId, execTime, createTime) values (@targetId, @action, @content, @mockUserId, @execTime, '{time}')", records, null, null, false);
        }

        /// <summary>
        /// 给某用户添加模拟关注任务
        /// </summary>
        public static async Task AddMockFocusUser(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long userId, int expectCount, IDbConnection conn = null)
        {
            var list = (await repository.QueryAsync<MockActionGenerationModel>("select t.id as mockUserId, rand() as rid from (select id from n_user where autoGenerationType=1 and username>='90000000000' and id not in (select uf.fanId from n_user_fan as uf where uf.userId=@userId)) as t order by rid asc limit " + expectCount, new { userId })).ToList();
            if (list.Count < expectCount)
            {
                var count = expectCount - list.Count;
                var moreList = (await repository.QueryAsync<MockActionGenerationModel>("select t.id as mockUserId, rand() as rid from (select id from n_user where autoGenerationType=2 and username>='90000000000' and id not in (select uf.fanId from n_user_fan as uf where uf.userId=@userId)) as t order by rid asc limit " + count, new { userId })).ToList();
                list.AddRange(moreList);
                list.Shuffle();
            }
            list = await GenerateMockActions(repository, startMockTime, hours, userId, "USER_FOCUS", expectCount, list);
            if (list.Count > 0)
            {
                var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                var size = 500;
                for (var i = 0; i < list.Count; i += size)
                {
                    var leftCount = list.Count - i;
                    var subList = list.Skip(i).Take(leftCount >= size ? size : leftCount).ToList();
                    if (subList.Count > 0)
                    {
                        await repository.QueryAsync<dynamic>($"insert ignore into n_mock_action_records (targetId, action, content, mockUserId, execTime, createTime) values (@targetId, @action, @content, @mockUserId, @execTime, '{time}')", subList, conn, null, false);
                    }
                }
                await repository.QueryAsync<dynamic>($"update n_user set mockFocusCnt=mockFocusCnt+@count where id=@userId", new { count = list.Count, userId }, conn, null, false);
            }
        }

        /// <summary>
        /// 给某文章添加模拟阅读任务
        /// </summary>
        public static async Task AddMockPostRead(this DbRESTFulRepository repository, DateTime startMockTime, int hours, long postId, int count, IDbConnection conn = null)
        {
            var postInfo = await repository.QuerySingleOrDefaultAsync<dynamic>($"select id, state, userId, checkCntLevel from n_post where id={postId}", null, conn);
            if (postInfo == null || postInfo.state != 1 || "D".Equals(postInfo.checkCntLevel)) return;

            var records = GenerateMockTimes(repository, startMockTime, hours, count).Select(e => new { readAt = e.ToString("yyyy-MM-dd HH:mm:ss") }).ToList();

            // 一次处理500条记录
            var size = 500;
            for (var i = 0; i < records.Count; i += size)
            {
                var leftCount = records.Count - i;
                var subList = records.Skip(i).Take(leftCount >= size ? size : leftCount).ToList();
                if (subList.Count > 0)
                {
                    await repository.QueryAsync<dynamic>($"insert into n_mock_read_action_records(postId, readAt) values ({postId}, @readAt)", subList, conn);
                }
            }
        }

        /// <summary>
        /// 获取模拟用户
        /// </summary>
        /// <param name="expectCount">期望获取最多多少个</param>
        /// <returns></returns>
        public static async Task<List<long>> GetMockUserIdsRandom(this DbRESTFulRepository repository, int expectCount, int minPostCount = 0)
        {
            if (minPostCount > 0)
            {
                var userIds = (await repository.QueryAsync<long>("select tt.id from (select t.id, rand() as rid from n_user as t left join n_user_statistic as us on t.id=us.userId where t.autoGenerationType=1 and t.username>='90000000000' and us.post>=@minPostCount order by rid asc limit @expectCount) as tt", new { expectCount, minPostCount })).ToList();
                if (userIds.Count < expectCount)
                {
                    var count = expectCount - userIds.Count;
                    var moreUserIds = (await repository.QueryAsync<long>("select tt.id from (select t.id, rand() as rid from n_user as t left join n_user_statistic as us on t.id=us.userId where t.autoGenerationType=2 and t.username>='90000000000' and us.post>=@minPostCount order by rid asc limit @expectCount) as tt", new { expectCount = count, minPostCount })).ToList();
                    userIds.AddRange(moreUserIds);
                    userIds.Shuffle();
                }
                return userIds;
            }
            else
            {
                var userIds = (await repository.QueryAsync<long>("select tt.id from (select t.id, rand() as rid from n_user as t where t.autoGenerationType=1 and t.username>='90000000000' order by rid asc limit @expectCount) as tt", new { expectCount })).ToList();
                if (userIds.Count < expectCount)
                {
                    var count = expectCount - userIds.Count;
                    var moreUserIds = (await repository.QueryAsync<long>("select tt.id from (select t.id, rand() as rid from n_user as t where t.autoGenerationType=2 and t.username>='90000000000' order by rid asc limit @expectCount) as tt", new { expectCount = count })).ToList();
                    userIds.AddRange(moreUserIds);
                    userIds.Shuffle();
                }
                return userIds;
            }
        }

        /// <summary>
        /// 获取数据库配置的模拟发文章的用户
        /// </summary>
        public static async Task<List<long>> GetMockUserIdsRandomByCategoryType(this DbRESTFulRepository repository, int expectCount, int categoryType)
        {
            return (await repository.QueryAsync<long>($"select tt.userId from (select t.userId, rand() as rid from s_mock_user_post as t where t.categoryType={categoryType} order by rid asc limit {expectCount}) as tt")).ToList();
        }
    }

}
