using JiaZhiQuan.Common.Messaging;
using JiaZhiQuan.Common.Messaging.Models;
using JiaZhiQuan.Common.Models.CacheModel;
using JiaZhiQuan.Common.Models.PO;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.Cache;
using Wicture.DbRESTFul.Infrastructure.Repository;
using static JiaZhiQuan.Common.UserGrow;

namespace JiaZhiQuan.Common
{
    public static partial class RepositoryExtension
    {
        /// <summary>
        /// 完成任务
        /// </summary>
        /// <param name="repository"></param>
        /// <param name="userId"></param>
        /// <param name="taskEvent"></param>
        /// <param name="data"></param>
        /// <param name="redis"></param>
        /// <param name="producer"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public static async Task<TaskCompleteResult> TaskCompleteAsync(this DbRESTFulRepository repository, long userId, TaskEvent taskEvent, Dictionary<string, object> data, RedisCacheProvider redis, Producer producer)
        {
            string key = CacheKeys.DailyUserTaskCompletion();
            // 获取用户当天的行为
            UserGrowBehavior userBehavior = await GetDailyUserBehavior(userId, redis);
            // 查询任务详情
            var task = await repository.GetTaskInfo(taskEvent);
            if (task is null) return new TaskCompleteResult { isComplete = false };

            // 根据任务类型判断任务是否完成
            bool isOk = taskEvent switch
            {
                TaskEvent.Collect => TaskCollectAsync(task, userBehavior),
                TaskEvent.Share => TaskShareAsync(task, userBehavior),
                TaskEvent.PublishCommentOrReply => TaskCommentOrReplyAsync(task, userBehavior),
                TaskEvent.FocusUser => TaskFocusUserAsync(task, userBehavior),
                TaskEvent.PublishPost => TaskPublishPostAsync(task, userBehavior),
                TaskEvent.CheckPost => TaskCheckPostAsync(task, userBehavior),
                TaskEvent.DayRead => TaskReadAsync(task, userBehavior),
                TaskEvent.DayThumbsup => TaskThumbsupAsync(task, userBehavior),
                _ => throw new NotImplementedException()
            };
            bool experienceAddSuccess = false;
            // 说明用户完成任务，添加经验值
            if (isOk)
            {
                experienceAddSuccess = await UserAddExperience(repository, userId, task, producer);
                if (experienceAddSuccess)
                {
                    switch (taskEvent)
                    {
                        case TaskEvent.Collect:
                        case TaskEvent.Share:
                        case TaskEvent.PublishCommentOrReply:
                        case TaskEvent.FocusUser:
                        case TaskEvent.PublishPost:
                        case TaskEvent.CheckPost:
                        case TaskEvent.DayRead:
                        case TaskEvent.DayThumbsup:
                            await redis.CSRedisClient.HSetAsync(key, userId.ToString(), userBehavior);
                            break;
                    }
                }
            }
            else
            {
                switch (taskEvent)
                {
                    case TaskEvent.Collect:
                    case TaskEvent.Share:
                    case TaskEvent.PublishCommentOrReply:
                    case TaskEvent.FocusUser:
                    case TaskEvent.PublishPost:
                    case TaskEvent.CheckPost:
                    case TaskEvent.DayRead:
                    case TaskEvent.DayThumbsup:
                        await redis.CSRedisClient.HSetAsync(key, userId.ToString(), userBehavior);
                        break;
                }
            }
            return new TaskCompleteResult
            {
                isComplete = experienceAddSuccess,
                taskMessage = $"{task.taskName}完成，+{task.experience}成长值",
                taskImage = task.taskImage
            };
        }

        /// <summary>
        /// 阅读数
        /// </summary>
        /// <param name="task"></param>
        /// <param name="userBehavior"></param>
        /// <returns></returns>
        private static bool TaskReadAsync(TaskInfo task, UserGrowBehavior userBehavior)
        {
            ++userBehavior.read;
            if (userBehavior.read >= task.taskCompleteNeedCount && userBehavior.hasRead == false)
            {
                userBehavior.hasRead = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 点赞数
        /// </summary>
        /// <param name="task"></param>
        /// <param name="userBehavior"></param>
        /// <returns></returns>
        private static bool TaskThumbsupAsync(TaskInfo task, UserGrowBehavior userBehavior)
        {
            ++userBehavior.thumbsup;
            if (userBehavior.thumbsup >= task.taskCompleteNeedCount && userBehavior.hasThumb == false)
            {
                userBehavior.hasThumb = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 收藏
        /// </summary>
        /// <param name="task"></param>
        /// <param name="userBehavior"></param>
        /// <returns></returns>
        private static bool TaskCollectAsync(TaskInfo task, UserGrowBehavior userBehavior)
        {
            // 收藏次数+1
            userBehavior.collection = userBehavior.collection + 1;
            // 如果收藏次数大于等于任务需要的分享次数，且当前还没有完成过收藏任务
            if (userBehavior.collection >= task.taskCompleteNeedCount && userBehavior.hasCollection == false)
            {
                // 标记已完成任务
                userBehavior.hasCollection = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 分享
        /// </summary>
        /// <param name="task"></param>
        /// <param name="userBehavior"></param>
        /// <returns></returns>
        private static bool TaskShareAsync(TaskInfo task, UserGrowBehavior userBehavior)
        {
            // 分享次数+1
            userBehavior.share = userBehavior.share + 1;
            // 如果分享次数大于等于任务需要的分享次数，且当前还没有完成过分享任务
            if (userBehavior.share >= task.taskCompleteNeedCount && userBehavior.hasShare == false)
            {
                // 标记已完成任务
                userBehavior.hasShare = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 评论/回复
        /// </summary>
        /// <param name="task"></param>
        /// <param name="userBehavior"></param>
        /// <returns></returns>
        private static bool TaskCommentOrReplyAsync(TaskInfo task, UserGrowBehavior userBehavior)
        {
            // 评论/回复次数+1
            userBehavior.comment = userBehavior.comment + 1;
            // 如果评论/回复次数大于等于任务需要的次数，且当前还没有完成过任务
            if (userBehavior.comment >= task.taskCompleteNeedCount && userBehavior.hasComment == false)
            {
                // 标记已完成任务
                userBehavior.hasComment = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 关注用户
        /// </summary>
        /// <param name="task"></param>
        /// <param name="userBehavior"></param>
        /// <returns></returns>
        private static bool TaskFocusUserAsync(TaskInfo task, UserGrowBehavior userBehavior)
        {
            // 关注次数+1
            userBehavior.focus = userBehavior.focus + 1;
            // 如果关注次数大于等于任务需要的次数，且当前还没有完成过任务
            if (userBehavior.focus >= task.taskCompleteNeedCount && userBehavior.hasFocus == false)
            {
                // 标记已完成任务
                userBehavior.hasFocus = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 发布笔记
        /// </summary>
        /// <param name="task"></param>
        /// <param name="userBehavior"></param>
        /// <returns></returns>
        private static bool TaskPublishPostAsync(TaskInfo task, UserGrowBehavior userBehavior)
        {
            // 发布笔记次数+1
            userBehavior.publish = userBehavior.publish + 1;
            // 如果发布笔记次数大于等于任务需要的次数，且当前还没有完成过任务
            if (userBehavior.publish >= task.taskCompleteNeedCount && userBehavior.hasPublish == false)
            {
                // 标记已完成任务
                userBehavior.hasPublish = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 审核笔记通过
        /// </summary>
        /// <param name="task"></param>
        /// <param name="userBehavior"></param>
        /// <returns></returns>
        private static bool TaskCheckPostAsync(TaskInfo task, UserGrowBehavior userBehavior)
        {
            // 审核笔记通过次数+1
            userBehavior.approved = userBehavior.approved + 1;
            // 如果审核笔记次数大于等于任务需要的次数，且当前还没有完成过任务
            if (userBehavior.approved >= task.taskCompleteNeedCount && userBehavior.hasApproved == false)
            {
                // 标记已完成任务
                userBehavior.hasApproved = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        #region Utils
        /// <summary>
        /// 获取当前用户当天的任务完成情况
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="redis"></param>
        /// <returns></returns>
        public static async Task<UserGrowBehavior> GetDailyUserBehavior(long userId, RedisCacheProvider redis)
        {
            string key = CacheKeys.DailyUserTaskCompletion();
            UserGrowBehavior userGrow = await redis.CSRedisClient.HGetAsync<UserGrowBehavior>(key, userId.ToString());
            if (userGrow is null)
            {
                userGrow = new UserGrowBehavior();
                await redis.CSRedisClient.HSetAsync(key, userId.ToString(), userGrow);
                await redis.CSRedisClient.ExpireAtAsync(key, DateTime.Now.AddDays(1).Date);
            }
            return userGrow;
        }

        /// <summary>
        /// 用户增加成长经验
        /// </summary>
        /// <param name="repository"></param>
        /// <param name="userId"></param>
        /// <param name="data"></param>
        /// <param name="userBehavior"></param>
        /// <param name="redis"></param>
        /// <param name="producer"></param>
        /// <returns></returns>
        public static async Task<bool> UserAddExperience(DbRESTFulRepository repository, long userId, TaskInfo task, Producer producer)
        {
            // 获取用户相关经验信息
            var user = await repository.QuerySingleOrDefaultAsync<UserExperience>($@"select id as userId, growLevel, growNowExperience, growSumExperience from n_user where id = @userId", new { userId });
            if (user is null)
            {
                LoggerManager.Logger.Error($"未找到用户，用户id：{userId}，任务类型：{TaskEvent.CheckPost}");
                return false;
            }
            user.versionId = task.versionId;

            using var conn = repository.ConnectionManager.GetConnection(false);
            conn.Open();
            var trans = conn.BeginTransaction();
            try
            {
                // 用户增加经验
                user.growNowExperience = user.growNowExperience + task.experience;
                user.growSumExperience = user.growSumExperience + task.experience;

                // 保存用户成长经验记录
                await repository.QuerySingleOrDefaultAsync<int>($@"INSERT INTO n_user_grow_experience_log
                                                                   (versionId, userId, source, taskId, experience, nowSumExperience, createAt) VALUES 
                                                                   (@versionId, @userId, @source, @taskId, @experience, @nowSumExperience, now());",
                                                                   new
                                                                   {
                                                                       task.versionId,
                                                                       userId,
                                                                       source = (int)UserGrow.ExperienceSource.任务,
                                                                       taskId = task.id,
                                                                       task.experience,
                                                                       nowSumExperience = user.growSumExperience
                                                                   }, conn, trans);

                await repository.QuerySingleOrDefaultAsync<int>($@"update n_user set growNowExperience = @growNowExperience, growSumExperience = @growSumExperience where id = @userId", user, conn, trans);
                // 用户升级
                await UserLevelUp(user, repository, conn, trans, producer);

                trans.Commit();
                return true;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.Error(ex, $"用户获取经验报错，错误信息：" + ex.Message);
                trans.Rollback();
                return false;
            }
        }

        /// <summary>
        /// 用户升级
        /// </summary>
        /// <param name="user"></param>
        /// <param name="repository"></param>
        /// <param name="conn"></param>
        /// <param name="trans"></param>
        /// <returns></returns>
        public static async Task UserLevelUp(UserExperience user, DbRESTFulRepository repository, IDbConnection conn, IDbTransaction trans, Producer producer)
        {
            var level = await repository.QuerySingleOrDefaultAsync<dynamic>($@"select id, `code`, experience from grow_level where versionId = @versionId and level = @level and @experience >= experience ",
                                                                               new { user.versionId, level = user.growLevel + 1, experience = user.growNowExperience },
                                                                               conn);
            // 说明用户暂时不满足升级条件
            if (level is null)
                return;

            user.growLevel = user.growLevel + 1;
            user.growNowExperience = user.growNowExperience - level.experience;

            string code = Guid.NewGuid().ToString("N");
            // 保存用户升级记录
            await repository.QuerySingleOrDefaultAsync<int>($@"insert into n_user_grow_level_log 
                                                            (code, versionId, userId, levelId, level, levelUpNeedExperience, nowLevelSumExperience, nowExperience, createAt) values 
                                                            (@code, @versionId, @userId, @levelId, @level, @levelUpNeedExperience, @nowLevelSumExperience, @nowExperience, now())",
                                                            new
                                                            {
                                                                code,
                                                                user.versionId,
                                                                user.userId,
                                                                levelId = level.id,
                                                                level = user.growLevel,
                                                                levelUpNeedExperience = level.experience,
                                                                nowLevelSumExperience = user.growSumExperience - user.growNowExperience,
                                                                nowExperience = user.growNowExperience
                                                            }, conn, trans);

            // 等级对应的奖励
            var prizes = await repository.QueryAsync<dynamic>($@"select * from grow_level_prize where versionId = @versionId and levelCode = @code",
                                                                 new { user.versionId, code = level.code });
            // 保存用户升级待领取奖励记录
            await repository.QuerySingleOrDefaultAsync<int>($@"insert into n_user_grow_prize_order 
                                                            (versionId, userId, levelId, levelPrizeId, levelLogCode, level, status, expireTime, points, displayOrder, type, name, thumbnails, prizeId, createAt) values
                                                            (@versionId, @userId, @levelId, @levelPrizeId, @levelLogCode, @level, @status, @expireTime, @points, @displayOrder, @type, @name, @thumbnails, @prizeId, now())",
                                                            prizes.Select(e => new
                                                            {
                                                                user.versionId,
                                                                user.userId,
                                                                levelId = level.id,
                                                                levelPrizeId = e.id,
                                                                levelLogCode = code,
                                                                level = user.growLevel,
                                                                status = 0,
                                                                expireTime = DateTime.Now.AddDays(e.expirationDays),
                                                                e.points,
                                                                e.displayOrder,
                                                                e.type,
                                                                e.name,
                                                                thumbnails = e.image,
                                                                e.prizeId
                                                            }), conn, trans);

            // 更新用户等级
            await repository.QuerySingleOrDefaultAsync<int>($@"update n_user set growLevel = @growLevel, growNowExperience = @growNowExperience, growSumExperience = @growSumExperience where id = @userId", user, conn, trans);

            // 给用户发送升级通知
            _ = producer.ProduceAsync(NotificationModel.GetMsgKey(), new NotificationModel
            {
                Type = NotificationType.Common,
                Content = JsonConvert.SerializeObject(new CommonSubModel
                {
                    UserId = user.userId,
                    Title = $"等级升至Lv{user.growLevel}",
                    Message = $"Lv{user.growLevel}等级权益已解锁，立即领取 >>>",
                    ActionLink = "app://mine/grow"
                })
            });

            // 递归调用，判断是否连续升级
            await UserLevelUp(user, repository, conn, trans, producer);

            return;
        }
        #endregion

        /// <summary>
        /// 获取任务详情
        /// </summary>
        /// <param name="repository"></param>
        /// <param name="taskEvent"></param>
        /// <returns></returns>
        public static async Task<TaskInfo> GetTaskInfo(this DbRESTFulRepository repository, TaskEvent taskEvent)
        {
            // 获取版本详情
            var version = await repository.QuerySingleOrDefaultAsync<dynamic>("select * from grow_version where enabled = 1 order by enabledAt desc limit 1");
            if (version is null)
            {
                // 当前没有启用的版本，无需处理
                // LoggerManager.Logger.Error($"当前无启用的用户成长体系版本");
                return null;
            }
            // 获取任务详情
            var task = await repository.QuerySingleOrDefaultAsync<TaskInfo>($@"SELECT
                                                                              	t1.id,
                                                                              	t1.versionId,
                                                                              	t1.experience,
                                                                              	t1.taskCompleteNeedCount,
                                                                              	t1.`name` as taskName,
                                                                              	t1.image as taskImage,
                                                                              	t1.taskEventId
                                                                              FROM
                                                                              	grow_task AS t1
                                                                              	LEFT JOIN grow_task_event AS t2 ON t1.taskEventId = t2.id 
                                                                              WHERE
                                                                                t1.enabled = 1
                                                                              	AND t1.versionId = @versionId
                                                                              	AND t2.`code` = '{taskEvent}'", new { versionId = version.id });
            if (task is null)
            {
                // 该版本没有此任务，无需处理
                // LoggerManager.Logger.Error($"当前启用版本没有此任务类型，任务类型：{taskEvent}");
                return null;
            }
            return task;
        }

    }

    public class UserExperience
    {
        public int versionId { get; set; }
        public long userId { get; set; }
        public int growLevel { get; set; }
        public int growNowExperience { get; set; }
        public int growSumExperience { get; set; }
    }

    public class TaskInfo
    {
        public int versionId { get; set; }
        public int id { get; set; }
        public int experience { get; set; }
        public int taskCompleteNeedCount { get; set; }
        public string taskName { get; set; }
        public string taskImage { get; set; }
        public int taskEventId { get; set; }
    }

    public class TaskCompleteResult
    {
        /// <summary>
        /// 是否完成任务
        /// </summary>
        public bool isComplete { get; set; }

        /// <summary>
        /// 任务描述
        /// </summary>
        public string taskMessage { get; set; }

        /// <summary>
        /// 任务图标
        /// </summary>
        public string taskImage { get; set; }
    }
}
