<?php
/**
 * 更新用户常规任务进度
 *
 * 按照 /Frontend/Index/UpdateUserTask 接口改来
 */

namespace Frontend\Controller\Crontab;

use Common\Common\Constant;
use Common\Common\Integral;
use Common\Common\Msg;
use Common\Common\TaskCenter;
use Common\Service\CustomtaskService;
use Common\Service\DailytaskService;
use Common\Service\UserActionService;
use Common\Service\UserContentService;
use Common\Service\UserNoticeService;
use Common\Service\UserRewardService;
use Common\Service\UserTaskService;
use VcySDK\Member;
use VcySDK\Service;

/**
 * Class UpdateUserTaskController
 * @package Frontend\Controller\Crontab
 * @property UserTaskService $userTaskServ
 * @property UserContentService $userContentServ
 * @property UserActionService $useractionServ
 * @property CustomtaskService $customtaskServ
 * @property DailytaskService $dailyTaskServ
 * @property UserRewardService $userRewardServ
 * @property UserNoticeService $userNoticeServ
 */
class UpdateUserTaskController extends AbstractController
{
    protected $userTaskServ = null;
    protected $userContentServ = null;
    protected $useractionServ = null;
    protected $customtaskServ = null;
    protected $dailyTaskServ = null;
    protected $userRewardServ = null;
    protected $userNoticeServ = null;

    /**
     * 单次处理人数
     */
    const DEAL_MEMBER_COUNT = 100;

    /**
     * 任务完成进度数值
     */
    const TASK_COMPLETE_PROGRESS = 100;

    /**
     * 当前企业标识
     * @var string
     */
    private $domain = '';

    public function Index()
    {
        set_time_limit(0);

        $taskCenter = TaskCenter::instance();

        $uids = [];
        // 获取用户ID
        while (count($uids) < self::DEAL_MEMBER_COUNT) {
            $uid = $taskCenter->pop(TaskCenter::CACHE_KEY);
            if ($uid === false) {
                break;
            }

            $uids[] = $uid;
        }
        if (empty($uids)) {
            exit('OK');
        }

        $this->userTaskServ = new UserTaskService();
        $this->userContentServ = new UserContentService();
        $this->useractionServ = new UserActionService();
        $this->customtaskServ = new CustomtaskService();
        $this->dailyTaskServ = new DailytaskService();
        $this->userRewardServ = new UserRewardService();
        $this->userNoticeServ = new UserNoticeService();

        // 初始化 SDK (减少重复)
        $config = array(
            'apiUrl' => cfg('UC_APIURL'),
            'enumber' => '',
            'pluginIdentifier' => 'yuanquan',
            'thirdIdentifier' => cfg('SDK_THIRD_IDENTIFIER'),
            'logPath' => RUNTIME_PATH . '/Logs/VcySDK/',
            'apiSecret' => cfg('API_SECRET'),
            'apiSigExpire' => cfg('API_SIG_EXPIRE'),
            'fileConvertApiUrl' => cfg('FILE_CONVERT_API_URL')
        );
        Service::instance()->initSdk($config);

        // 同步人员进度
        $this->syncProgress($uids);
        // 检查每日任务
        $this->checkDailytaskProgress($uids);

        exit('OK');
    }

    /**
     * 同步人员进度
     * @param $uids
     * @return bool
     */
    private function syncProgress($uids)
    {
        // 获取进行中未完成的任务列表
        $usertaskList = $this->userTaskServ->listWithOutDomian([
            'uid' => $uids,
            'complete_status' => Constant::USER_TASK_COMPLETE_STATUS_PROCESS,
            'task_status' => Constant::CUSTOMTASK_STATUS_PROCESS,
        ], null, [], implode(',', [
            'user_task_id',
            'uid',
            'username',
            'customtask_id',
            'progress',
            'domain',
        ]));
        if (empty($usertaskList)) {
            return true;
        }

        // 按照企业标识分组
        $domainWithUserTask = [];
        foreach ($usertaskList as $item) {
            $domainWithUserTask[$item['domain']][] = $item;
        }
        unset($usertaskList);

        $service = Service::instance();
        foreach ($domainWithUserTask as $this->domain => $item) {
            // 初始化 SDK
            $config['enumber'] = $this->domain;
            $service->setConfig($config);

            // 每个企业标识单独处理
            try {
                $this->dealWithSingleDomain($item);
            } catch (\Exception $e) {
            }
        }
    }

    /**
     * 处理单个 Domain 下的用户任务数据
     * @param $item
     */
    private function dealWithSingleDomain($item)
    {
        /**
         * 当前企业下未完成的任务 ID 为 key 每个任务下对应的人员 (注意: 对应的人员数据不是该任务下的全部人员)
         * 第一步 ($item):
         * {
         *      "任务ID" => {
         *          "用户ID" => {
         *              用户任务数据
         *          }
         *          ...
         *      }
         *      ...
         * customtask_id, uid
         * }
         */
        $customtaskIdList = array_unique(array_column($item, 'customtask_id'));
        $uidList = array_unique(array_column($item, 'uid'));
        $temp = [];
        foreach ($item as $value) {
            $temp[$value['customtask_id']][$value['uid']] = $value;
        }
        $item = $temp;
        unset($temp);

        /**
         * 第二步 获取用户动作 ($userActions):
         * {
         *      "用户ID" => {
         *          "任务ID" => {
         *              "app 标识" => {
         *                  "app data id" => "数据创建时间"
         *                  ...
         *              }
         *              ...
         *          }
         *          ...
         *      }
         * }
         */
        $useractionList = $this->useractionServ->listWithOutDomian([
            'uid' => $uidList,
            'customtask_id' => $customtaskIdList,
        ], null, [], implode(',', ['customtask_id', 'app', 'app_data_id', 'created', 'uid',]));
        // 格式化动作数据
        $userActions = [];
        foreach ($useractionList as $action) {
            $userActions[$action['uid']][$action['customtask_id']][$action['app']][$action['app_data_id']] =
                $action['created'];
        }
        unset($useractionList);

        /**
         * 第三步: 获取任务内容 (把任务内容放入第一步的数组内, 并且根据第二步的用户动作来判断是否完成任务)
         * {
         *      "任务ID" => {
         *          "用户ID" => {
         *              用户任务数据
         *              "用户任务内容" => [
         *
         *              ]
         *          }
         *          ...
         *      }
         *      ...
         * }
         */
        $userContentList = $this->userContentServ->listWithOutDomian([
            'uid' => $uidList,
            'customtask_id' => $customtaskIdList,
        ], null, [], implode(',', [
            'customtask_id',
            'uid',
            'username',
            'app',
            'app_data_id',
            'user_content_id',
            'content_status',
            'complete_time'
        ]));
        // 任务内容 放入用户任务中
        foreach ($userContentList as $userContent) {
            // 初始化用户任务完成度
            if (!isset($item[$userContent['customtask_id']][$userContent['uid']]['completeCount'])) {
                $item[$userContent['customtask_id']][$userContent['uid']]['completeCount'] = 0;
            }

            // 如果完成
            if ($userContent['content_status'] == Constant::USER_CONTENT_STATUS_COMPLETE) {
                // 增加完成任务数
                $item[$userContent['customtask_id']][$userContent['uid']]['completeCount'] ++;
            } else {
                $customTaskId = $userContent['customtask_id'];
                $app = $userContent['app'];
                $appDataId = $userContent['app_data_id'];

                // 用户动作已存在
                if (isset($userActions[$userContent['uid']][$customTaskId][$app][$appDataId])) {
                    $completeTime = $userActions[$userContent['uid']][$customTaskId][$app][$appDataId];
                    // 更新内容完成状态为:已完成
                    $this->userContentServ->updateWithOutDomain($userContent['user_content_id'], [
                        'content_status' => Constant::USER_CONTENT_STATUS_COMPLETE,
                        'complete_time' => $completeTime,
                    ]);

                    // 标记完成时间
                    $userContent['complete_time'] = $completeTime;
                    // 增加完成任务数
                    $item[$userContent['customtask_id']][$userContent['uid']]['completeCount'] ++;
                }
            }

            // 放入统计数组内
            $item[$userContent['customtask_id']][$userContent['uid']]['contents'][] = $userContent;
        }

        // 最后: 统计任务进度 (计算人员完成情况 并更新)
        $completedTask = [];
        foreach ($item as $taskId => $task) {
            foreach ($task as $userId => $userTask) {
                // 计算任务进度,精确到小数点后两位
                $progress = round($userTask['completeCount'] / count($userTask['contents']), 2) * 100;
                // 任务进度有变更
                if ($progress != $userTask['progress']) {
                    $updateData = ['progress' => $progress];

                    // 如果完成了
                    if ($progress == self::TASK_COMPLETE_PROGRESS) {
                        $updateData['complete_status'] = Constant::USER_TASK_COMPLETE_STATUS_COMPLETE;
                        $updateData['complete_time'] = max(array_column($userTask['contents'], 'complete_time'));
                        // 记录有人完成了 的任务ID
                        $completedTask[$userTask['customtask_id']][$userId] = [
                            'username' => $userTask['username'],
                            'complete_time' => $updateData['complete_time']
                        ];
                    }

                    $this->userTaskServ->updateWithOutDomain($userTask['user_task_id'], $updateData);
                    // 单人完成所有任务后增加任务完成数
                    if ($progress == 100) {
                        $this->customtaskServ->addCompleteTotal($userTask['customtask_id']);
                    }
                }
            }
        }

        // 发放激励
        try {
            $this->sendCustomtaskReward($completedTask);
        } catch (\Exception $e) {}
    }

    /**
     * 发放激励
     * @param $completedTask
     * {
     *      "任务ID" => {
     *          "人员ID" => {
     *              "人员姓名",
     *              "完成时间"
     *          }
     *      }
     * }
     * @return bool
     */
    private function sendCustomtaskReward($completedTask)
    {
        if (empty($completedTask)) {
            return true;
        }

        $taskIdArr = array_keys($completedTask);
        $taskList = $this->customtaskServ->listWithOutDomian(
            [
                'customtask_id' => $taskIdArr
            ], null, [],
            implode(',', [
                'customtask_id',
                'reward_setting',
                'task_name'
            ])
        );
        $taskList = array_combine_by_key($taskList, 'customtask_id');

        $integralServ = &Integral::instance();
        $msgServ = &Msg::instance();
        foreach ($completedTask as $taskId => $completeValue) {
            // 任务详情
            $taskDetail = $taskList[$taskId];
            $rewardSetting = unserialize($taskDetail['reward_setting']);

            foreach ($completeValue as $userId => $completeItem) {
                switch ($rewardSetting['type']) {
                    // 勋章
                    case Constant::REWARD_TYPE_MEDAL:
                        // 获取勋章数据
                        $medal = $this->getMedal($rewardSetting['medal_id']);
                        if (empty($medal)) {
                            break;
                        }

                        // 发放勋章
                        $rpcUrl = sprintf(
                            '%s%s/%s%s',
                            cfg('PROTOCAL'),
                            $_SERVER['HTTP_HOST'],
                            $this->domain,
                            '/Integral/Rpc/Medal/Endow'
                        );
                        $rpcParams = [
                            $rewardSetting['medal_id'],
                            $userId,
                            $completeItem['username']
                        ];
                        $rpcRe = \Com\Rpc::phprpc($rpcUrl)->invoke('Index', $rpcParams);
                        if ($rpcRe) {
                            $msg_data = [
                                [
                                    'title' => "恭喜您,获得【{$medal['name']}】勋章",
                                    'description' => "获取渠道:员圈任务-{$taskDetail['task_name']}\n获取时间:" .
                                        rgmdate($completeItem['complete_time']),
                                    'url' => oaUrl('Frontend/Index/Medal/Index', [], $this->domain),
                                ],
                            ];

                            $msgServ->sendNews($userId, null, null, $msg_data);
                        }
                        break;

                    // 积分
                    case Constant::REWARD_TYPE_INTEGRAL:
                        $integralServ->asynUpdateIntegral([
                            'memUid' => $userId,
                            'irKey' => 'taskcenter_customtask_complete',
                            'msgIdentifier' => 'yuanquan',
                            'integral' => $rewardSetting['integral'],
                            'remark' => '完成常规任务获得激励',
                            'businessKey' => 'task_center',
                            'businessAct' => 'normal_task',
                        ]);

                        break;
                }
            }
        }

        return true;
    }

    /**
     * 检查每日任务进度,如果完成则通知用户
     * @param $uids
     * @return bool
     */
    private function checkDailytaskProgress($uids)
    {
        // 获取用户今日动作
        $time = rstrtotime(rgmdate(MILLI_TIME, 'Y-m-d'), 1);
        $actionList = $this->useractionServ->listWithOutDomian([
            'uid' => $uids,
            'created >= ?' => $time
        ]);
        // 按照 uid 为 key 重组
        $temp = [];
        foreach ($actionList as $value) {
            $temp[$value['domain']][$value['uid']][] = $value;
        }
        $actionList = $temp;
        unset($temp);

        $service = Service::instance();
        foreach ($actionList as $this->domain => $userAction) {
            // 初始化 SDK
            $config['enumber'] = $this->domain;
            $service->setConfig($config);

            // 每个企业标识单独处理
            $this-> dealWithSingleDomainDeaily($userAction);
        }

        return true;
    }

    /**
     * 每个企业标识单独处理 每日任务
     * @param $userAction array 人员 - 动作
     * @return bool
     */
    private function dealWithSingleDomainDeaily($userAction)
    {
        // 构建每日任务列表
        $ruleList = $this->buildDailytaskList();
        if (empty($ruleList)) {
            return true;
        }
        $uids = array_keys($userAction);

        // 获取通知列表
        $noticeList = $this->userNoticeServ->listWithOutDomian([
            'uid' => $uids,
            'created >= ?' => rstrtotime(rgmdate(MILLI_TIME, 'Y-m-d')),
        ]);
        // 格式化
        $temp = [];
        foreach ($noticeList as $item) {
            $temp[$item['uid']][$item['app']][] = $item['rule_name'];
        }
        $noticeList = $temp;
        unset($temp);

        // 获取用户今日激励领取数据
        $ruleNames = array_keys($ruleList);
        $time = rstrtotime(rgmdate(MILLI_TIME, 'Y-m-d'), 1);
        $rewardList = $this->userRewardServ->listWithOutDomian([
            'uid' => $uids,
            'rule_name' => $ruleNames,
            'created >= ?' => $time,
        ]);
        $temp = [];
        foreach ($rewardList as $item) {
            $temp[$item['uid']][$item['app']][] = $item['rule_name'];
        }
        $rewardList = $temp;
        unset($temp);

        // 获取人员数据
        $userList = (new Member(Service::instance()))->listAll(['memUids' => $uids], 1, self::DEAL_MEMBER_COUNT);
        $userList = array_combine_by_key($userList['list'], 'memUid');

        // 需要发送消息
        $noticeInsertList = [];
        // 按一个个人来
        foreach ($userList as $uid => $user) {
            foreach ($ruleList as $name => $rule) {
//                // 允许重复性 $unique true: 多条数据算一条 false: 多条数据算多条
//                $unique = isset($rule['config']['unique']) ? $rule['config']['unique'] : false;
                // 人员行为
                foreach ($userAction[$uid] as $action) {
                    if (!in_array($action['action_key'], $rule['action_keys'])) {
                        continue;
                    }
                    $user['completed'][$action['action_key']] ++;

                    // 完成任务
                    if ($user['completed'][$action['action_key']] >= $rule['count'] &&
                            // 没有发送过消息
                            (!in_array($action['action_key'], $noticeList[$uid][$rule['app']])) &&
                            // 没有获取过奖励
                            (!in_array($action['action_key'], $rewardList[$uid][$rule['app']]))) {
                        $noticeInsertList[] = [
                            'uid' => $uid,
                            'username' => $userList[$uid]['memUsername'],
                            'app' => $action['app'],
                            'rule_name' => $action['action_key'],
                            // insert_all 补全字段信息 时就不会去自动补全了
                            'domain' => $this->domain
                        ];

                        // 发送消息
                        $this->sendDailytaskCompleteMsg([
                            'app' => $action['app'],
                            'integral' => $rule['integral'],
                            'complete_time' => rgmdate(max(array_column($userAction[$uid], 'created')), 'Y-m-d H:i'),
                            'task_title' => $rule['task_title']
                        ], $uid);
                    }
                }
            }
        }

        // 记录发送过的消息
        $this->userNoticeServ->insert_all($noticeInsertList);

        return true;
    }

    /**
     * 构建每日任务列表
     * @author zhonglei
     * @param array $user 用户信息
     * @return array
     *          + string app 应用
     *          + string app_url 应用Url
     *          + string rule_name 规则名称
     *          + string task_title 任务名称
     *          + int integral 积分
     *          + int require_total 需要完成总数
     *          + int complete_total 已完成总数
     *          + int is_get 是否已领取激励(1=未领取;2=已领取)
     */
    private function buildDailytaskList()
    {
        $configList = $this->dailyTaskServ->listWithOutDomian([
            'app' => [
                Constant::APP_WORKMATE,
                Constant::APP_ANSWER
            ],
            'domain' => $this->domain
        ]);

        // 获取已启用的任务规则
        $ruleList = [];
        foreach ($configList as $config) {
            // 任务未开启
            if ($config['is_open'] != Constant::DAILYTASK_IS_OPEN_TRUE) {
                continue;
            }

            // 格式化任务名称
            $config['rules'] = unserialize($config['rules']);
            foreach ($config['rules'] as $name => $rule) {
                $taskTitle = '';

                switch ($name) {
                    // 员圈发表话题
                    case Constant::RULE_NAME_WORKMATE_CIRCLE:
                        $taskTitle = '在员圈发表%s个话题';
                        break;
                    // 员圈添加评论
                    case Constant::RULE_NAME_WORKMATE_COMMENT:
                        $unique = isset($rule['config']['unique']) ? $rule['config']['unique'] : false;
                        $taskTitle = $unique ? '在员圈不同话题下添加%s条评论' : '在员圈添加%s条评论';
                        break;
                    // 员圈点赞
                    case Constant::RULE_NAME_WORKMATE_LIKE:
                        $unique = isset($rule['config']['unique']) ? $rule['config']['unique'] : false;
                        $taskTitle = $unique ? '在员圈对话题进行%s个点赞' : '在员圈进行%s个点赞';
                        break;
                    // 问答中心发起提问
                    case Constant::RULE_NAME_ANSWER_QUESTION:
                        $taskTitle = '在问答中心发起%s个提问';
                        break;
                    // 问答中心添加回答
                    case Constant::RULE_NAME_ANSWER_ANSWER:
                        $unique = isset($rule['config']['unique']) ? $rule['config']['unique'] : false;
                        $taskTitle = $unique ? '在问答中心不同问题下添加%s个回答' : '在问答中心添加%s个回答';
                        break;
                }

                $rule['app'] = $config['app'];
                $rule['task_title'] = sprintf($taskTitle, $rule['count']);
                $ruleList[$name] = $rule;
            }
        }

        return $ruleList;
    }

    /**
     * 获取勋章详情
     * @param int    $imId 勋章ID
     * @return array
     */
    private function getMedal($imId = 0)
    {
        $rpcUrl = sprintf(
            '%s%s/%s%s',
            cfg('PROTOCAL'),
            $_SERVER['HTTP_HOST'],
            $this->domain,
            '/Integral/Rpc/Medal/List'
        );
        $list = call_user_func(array(\Com\Rpc::phprpc($rpcUrl), 'Index'));

        if (!empty($list)) {
            $list = array_combine_by_key($list, 'im_id');
        }

        $medalInfo = isset($list[$imId]) ? $list[$imId] : [];
        return $medalInfo;
    }

    /**
     * 发送每日任务完成消息
     * @author zhonglei
     * @param array $dailytask 每日任务数据
     * @param string $uid 用户ID
     * @return bool
     */
    private function sendDailytaskCompleteMsg($dailytask, $uid)
    {
        // 消息通知前缀
        $prefix = cfg('NOTICE_PREFIX', null, '');

        // 应用名称
        $app_names = [
            Constant::APP_WORKMATE => '员圈',
            Constant::APP_ANSWER => '问答中心',
        ];
        $app_name = isset($app_names[$dailytask['app']]) ? $app_names[$dailytask['app']] : '';
        $reward = "+{$dailytask['integral']}积分";

        $msgServ = &Msg::instance();
        $msg_data = [
            [
                'title' => "{$prefix}您已完成了{$app_name}的日常任务,快来领取奖励吧",
                'description' => "任务名称:{$dailytask['task_title']}\n积分奖励:{$reward}\n完成时间:{$dailytask['complete_time']}",
                'url' => oaUrl('Frontend/Index/TaskIndex', [], $this->domain),
            ],
        ];

        try {
            $msgServ->sendNews($uid, null, null, $msg_data);
        } catch (\Exception $e) {
            // 不影响流程
        }

        return true;
    }
}