<?php

declare(strict_types=1);

namespace app\control\controller\exam;

use app\common\model\account\Department;
use app\common\model\account\Duty;
use app\common\model\account\User as UserModel;
use app\common\model\Base;
use app\common\model\exam\paper\Record;
use app\common\model\exam\Paper;
use app\common\model\exam\Question;
use app\control\model\User;
use app\Request;
use mb\helper\Collection;
use think\Exception;
use think\response\Json;

/**
 * Class Process
 * @package app\control\controller\exam
 */
class Process
{
    /**
     * @param Request $request
     * @return Json
     * @api {post} /exam/process/search 过程管理列表
     * @apiGroup Exam-process
     * @apiName sort1
     * @apiVersion 1.0.0
     *
     * @apiDescription 过程管理列表
     *
     * @apiParam {String} type 考试类型<br>competition -- 竞赛级别<br>promote -- 提升级<br>beginner -- 入门级<br>simulate -- 模拟考
     * @apiParam {Number} [current]  页码
     * @apiParam {Number} [pageSize]  页数
     * @apiParam {String} [title]  试卷名称
     * @apiParam {String} [timeStart]  显示起始时间
     * @apiParam {String} [timeEnd]  显示结束时间
     * @apiParam {String} [mode]  组卷方式 hand -- 手工组卷 random -- 随机组卷
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function search(Request $request)
    {
        $input = $request->post();
        $pageIndex = empty($input['current']) ? 1 : intval($input['current']);
        $pageSize = empty($input['pageSize']) ? 10 : intval($input['pageSize']);
        $total = 0;
        $filters = [];
        $type = empty($input['type']) ? '' : $input['type'];
        $filters['type'] = $type;
        if (!empty($input['title'])) {
            $filters['title'] = $input['title'];
        }
        if (!empty($input['mode'])) {
            $filters['mode'] = $input['mode'];
        }
        if (!empty($input['timeStart'])) {
            $filters['timeStart'] = $input['timeStart'];
        }
        if (!empty($input['timeEnd'])) {
            $filters['timeEnd'] = $input['timeEnd'];
        }
        $dataSet = Paper::search($filters, $pageIndex, $pageSize, $total);
        $dataSet = array_map(
            function ($val) {
                $val['joinNum'] = Record::recordNum(['paper' => $val['id']]);
                unset($val['questionType']);
                return $val;
            },
            $dataSet
        );
        return payload(['dataSet' => $dataSet, 'total' => $total]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws Exception
     * @api {post} /exam/process/detail 答卷记录
     * @apiGroup Exam-process
     * @apiName sort2
     * @apiVersion 1.0.0
     *
     * @apiDescription 答卷记录
     *
     * @apiParam {Number} id 试卷id
     * @apiParam {Number} [current]  页码
     * @apiParam {Number} [pageSize]  页数
     * @apiParam {String} [name]  姓名
     * @apiParam {String} [uid]  账号
     * @apiParam {String} [departmentId]  部门id
     * @apiParam {String} [status]  答卷状态 start -- 答卷中 end -- 已交卷
     * @apiParam {String} [markStatus]  评卷状态 await -- 未评卷 end -- 已评卷
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     * @apiSuccess {Number} total    返回数据总数
     * @apiSuccess {Number} userNum    应考人数
     * @apiSuccess {Number} joinNum    实考人数
     * @apiSuccess {Number} missingNum    缺考人数
     * @apiSuccess {Number} joinPoint    参考百分比
     * @apiSuccess {Number} markNum    已评卷人数
     * @apiSuccess {Number} noMarkNum    未评卷人数
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function detail(Request $request)
    {
        $input = $request->post();
        $pageIndex = empty($input['current']) ? 1 : intval($input['current']);
        $pageSize = empty($input['pageSize']) ? 10 : intval($input['pageSize']);
        $total = 0;
        $filters = [];
        if (empty($input['id'])) {
            return payload(error(-1, '参数不完整'));
        }
        $filters['paperId'] = intval($input['id']);
        if (!empty($input['departmentId'])) {
            $filters['departmentId'] = intval($input['departmentId']);
        }
        if (!empty($input['userId'])) {
            $filters['uid'] = $input['userId'];
        }
        if (!empty($input['name'])) {
            $filters['name'] = $input['name'];
        }
        if (!empty($input['status'])) {
            $filters['status'] = $input['status'];
        }
        if (!empty($input['markStatus'])) {
            $filters['markStatus'] = $input['markStatus'];
        }
        $dataSet = Record::search($filters, $pageIndex, $pageSize, $total);
        $markNum = 0; //已评卷数
        $noMark = 0; //未评卷数
        foreach ($dataSet as &$val) {
            if (($val['status'] != 'start') && ($val['markStatus'] == 'await')) {
                ++$noMark;
            }
            if ($val['markId']) {
                $userInfo = UserModel::fetch($val['markId']);
                $val['markTitle'] = isset($userInfo['uid']) ? $userInfo['uid'] : '';
                ++$markNum;
            } else {
                $val['markTitle'] = '';
            }
        }
        $userNum = 0; //应该参考人数
        $watch = Paper::watchUsers(intval($input['id']));
        if ($watch == 'allUser') {
            UserModel::search([], 1, 1, $userNum);
        } else {
            $userNum = count($watch);
        }
        $joinNum = Record::recordNum(['paper' => intval($input['id'])]); //实际参考人数
        $missingNum = (($userNum - $joinNum) < 0) ? 0 : ($userNum - $joinNum);
        if ($joinNum > 0) {
            $jionPoint = intval(sprintf(($joinNum / $userNum) * 100));
        } else {
            $jionPoint = 0;
        }
        return payload(
            [
                'dataSet' => $dataSet,
                'total' => $total,
                'userNum' => $userNum,
                'joinNum' => $joinNum,
                'missingNum' => $missingNum,
                'joinPoint' => $jionPoint,
                'markNum' => $markNum,
                'noMarkNum' => $noMark
            ]
        );
    }

    /**
     * @param Request $request
     * @return Json
     * @throws Exception
     * @api {post} /exam/process/missing 缺考账户
     * @apiGroup Exam-process
     * @apiName sort3
     * @apiVersion 1.0.0
     *
     * @apiDescription 缺考账户
     *
     * @apiParam {Number} id 试卷id
     * @apiParam {Number} [current]  页码
     * @apiParam {Number} [pageSize]  页数
     * @apiParam {String} [role]  权限
     * @apiParam {Number} [departmentId]  部门id
     * @apiParam {Number} [dutyId]  职务id
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     * @apiSuccess {Number} total    返回数据总数
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function missing(Request $request)
    {
        $input = $request->post();
        if (empty($input['id'])) {
            return payload(error(-1, '参数不完整'));
        }
        $watch = Paper::watchUsers(intval($input['id'])); //应该参加考试的人
        $joinUser = Record::joinUser(['paper' => intval($input['id'])]); //实际参加考试的人
        $missingUser = array_diff($watch, $joinUser);
        if (empty($missingUser)) {
            return payload(['dataSet' => [], 'total' => 0]);
        }
        $pageIndex = empty($input['current']) ? 1 : intval($input['current']);
        $pageSize = empty($input['pageSize']) ? 10 : intval($input['pageSize']);
        $total = 0;
        $filters = [];
        $filters['ids'] = implode(',', $missingUser);
        if (!empty($input['departmentId'])) {
            $filters['department'] = intval($input['departmentId']);
        }
        if (!empty($input['dutyId'])) {
            $filters['duty'] = intval($input['dutyId']);
        }
        if (!empty($input['role'])) {
            $filters['role'] = trim($input['role']);
        }
        if (!empty($input['name'])) {
            $filters['uid'] = trim($input['name']);
        }
        $dataSet = UserModel::search($filters, $pageIndex, $pageSize, $total);
        $dataSet = array_map(
            function ($val) {
                $val = Collection::elements(
                    ['id', 'uid', 'name', 'gender', 'birthday', 'department', 'duty', 'role', 'status'],
                    $val
                );
                $department = Department::fetch($val['department']);
                $val['departmentTitle'] = empty($department) ? '' : $department['title'];
                $duty = Duty::fetch($val['duty']);
                $val['dutyTitle'] = empty($duty) ? '' : $duty['title'];
                return $val;
            },
            $dataSet
        );
        return payload(['dataSet' => $dataSet, 'total' => $total]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws Exception
     * @api {post} /exam/process/export 导出缺考账户
     * @apiGroup Exam-process
     * @apiName sort4
     * @apiVersion 1.0.0
     *
     * @apiDescription 导出缺考账户
     *
     * @apiParam {Number} id 试卷id
     * @apiParam {String} header 需要导出的信息<br>['uid', 'name', 'department', 'duty', 'gender', 'role', 'status']
     * @apiParam {String} [ids]  需要导出的账户id
     * @apiParam {String} [role]  权限
     * @apiParam {Number} [departmentId]  部门id
     * @apiParam {Number} [dutyId]  职务id
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function export(Request $request)
    {
        $input = $request->get();
        if (empty($input['id']) || empty($input['header'])) {
            return payload(error(-1, '参数不完整'));
        }
        $watch = Paper::watchUsers(intval($input['id'])); //应该参加考试的人
        $joinUser = Record::joinUser(['paper' => intval($input['id'])]); //实际参加考试的人
        $missingUser = array_diff($watch, $joinUser);
        $filters['ids'] = implode(',', $missingUser);
        if (!empty($input['ids'])) {
            $filters['ids'] = implode(',', $input['ids']);
        }
        if (!empty($input['departmentId'])) {
            $filters['department'] = intval($input['departmentId']);
        }
        if (!empty($input['dutyId'])) {
            $filters['duty'] = intval($input['dutyId']);
        }
        if (!empty($input['role'])) {
            $filters['role'] = trim($input['role']);
        }
        $header = explode(',', $input['header']);
        array_push($header, 'company');
        UserModel::export($filters, $header);
    }

    /**
     * @param Request $request
     * @return Json
     * @api {post} /exam/process/delete 删除答卷
     * @apiGroup Exam-process
     * @apiName sort5
     * @apiVersion 1.0.0
     *
     * @apiDescription 删除答卷
     *
     * @apiParam {Number} ids 试卷id
     * @apiParam {String} type single 单删 batch 多删
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function delete(Request $request)
    {
        $input = $request->post();
        if (empty($input['ids']) || empty($input['type'])) {
            return payload(error(-1, '参数不完整'));
        }
        if ($input['type'] == 'batch') {
            $ids = implode(',', $input['ids']);
        } else {
            $ids = (string)$input['ids'];
        }
        $res = Record::delete($input['type'], $ids);
        if (!$res) {
            return payload(error(-1, '删除失败'));
        }
        return payload([]);
    }

    /**
     * @param Request $request
     * @return Json
     */
    public function deleteRecord(Request $request)
    {
        $input = $request->post();
        if (empty($input['id'])) {
            return payload(error(-1, '参数不完整'));
        }
        $records = Record::search(['paperId' => $input['id']]);
        if (empty($records)) {
            return  payload(error(-11, '该试卷暂无考试记录'));
        }
        $res = Record::deleteRecord(strval($input['id']));
        if (!$res) {
            return payload(error(-1, '删除失败'));
        }
        return payload([]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws Exception
     * @api {post} /exam/process/statistics 统计
     * @apiGroup Exam-process
     * @apiName sort6
     * @apiVersion 1.0.0
     *
     * @apiDescription 统计
     *
     * @apiParam {Number} id 试卷id
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function statistics(Request $request)
    {
        $input = $request->post();
        if (empty($input['id'])) {
            return payload(error(-1, '参数不完整'));
        }
        $records = Record::fetch(intval($input['id']));
        $paper = Paper::fetch($records['paper_id']);
        $strategies = unserialize($paper['strategies']);
        $questionType = unserialize($paper['question_type']);
        $questionType = array_map(
            function ($q) {
                $q = $q['point'];
                return $q;
            },
            $questionType
        ); //题型对应分值
        $knowledge = [];
        foreach ($strategies as $val) {
            $knowledge[$val['knowledge']][] = $val;
        }
        $newKnowledge = [];
        foreach ($knowledge as $k) {
            if (!isset($newKnowledge[$k[0]['knowledge']])) {
                $newKnowledge[$k[0]['knowledge']] = [
                    'knowledge' => $k[0]['knowledge'],
                    'subject' => $k[0]['subject'],
                    'knowledgeTitle' => $k[0]['knowledgeTitle'],
                    'subjectTitle' => $k[0]['subjectTitle'],
                    'num' => 0,
                    'score' => 0,
                    'userScore' => 0,
                    'percent' => 0,
                    'pass' => 0,
                ];
                foreach ($k as $v) {
                    if ($paper['mode'] == 'hand') {
                        $num = count($v['strategy']);
                    } else {
                        $num = array_sum($v['strategy']);
                    }
                    $newKnowledge[$k[0]['knowledge']]['num'] += $num;
                    $score = $num * $questionType[$v['type']];
                    $newKnowledge[$k[0]['knowledge']]['score'] += $score;
                }
            }
        }
        $result = unserialize($records['result']);
        $arr = [];
        foreach ($result as $value) {
            $arr = array_merge($arr, $value);
        }
        $result = $arr;
        $questionNum = 0;
        foreach ($result as $know) {
            if ($know['score'] > 0) {
                $newKnowledge[$know['knowledge']]['userScore'] += $know['score'];
                $newKnowledge[$know['knowledge']]['pass']++;
            }
            $questionNum++;
        }
        $newKnowledge = array_map(
            function ($last) {
                $last['percent'] = sprintf("%.2f", $last['pass'] / $last['num']) * 100;
                return $last;
            },
            $newKnowledge
        );
        $user = UserModel::fetch($records['user_id']);
        $returnData = [
            'title' => $paper['title'],
            'totalPoints' => $paper['total_points'],
            'throughPoints' => $paper['through_points'],
            'timeEnd' => $records['time_end'],
            'score' => $records['score'],
            'userId' => $user['uid'],
            'name' => '',
            'questionNum' => $questionNum,
            'dataSet' => array_values($newKnowledge)
        ];
        if (!empty($user['name'])) {
            $returnData['name'] = $user['name'];
        }
        return payload(['data' => $returnData]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws Exception
     * @api {post} /exam/process/preview 答卷
     * @apiGroup Exam-process
     * @apiName sort7
     * @apiVersion 1.0.0
     *
     * @apiDescription 答卷
     *
     * @apiParam {Number} id 试卷id
     * @apiParam {String} type 显示模式 all --整卷 wrong--错题
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function preview(Request $request)
    {
        $input = $request->post();
        if (empty($input['id']) || empty($input['type'])) {
            return payload(error(-11, '参数不完整'));
        }
        $type = ($input['type'] == 'all') ? 'all' : 'wrong';
        $dataSet = Record::preview(intval($input['id']), $type);
        return payload(['data' => $dataSet]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws Exception
     * @api {post} /exam/process/markPreview 评卷
     * @apiGroup Exam-process
     * @apiName sort8
     * @apiVersion 1.0.0
     *
     * @apiDescription 评卷
     *
     * @apiParam {Number} id 试卷id
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function markPreview(Request $request)
    {
        $currentUser = User::fetchCurrent();
        $input = $request->post();
        if (empty($input['id'])) {
            return payload(error(-11, '参数不完整'));
        }
        $check = Record::markRole(intval($input['id']));
        if (
            !$check && (empty($currentUser['privileges'])
            || (!empty($currentUser['privileges']) && in_array('courseManger', $currentUser['privileges'])))
        ) {
            return payload(error(-12, '暂无该试卷的评卷权限'));
        }
        $record = Record::fetch(intval($input['id']));
        if ($record['status'] != 'end') {
            return payload(error(-12, '考试中，请等待交卷后再进行评卷'));
        }
        $record['result'] = unserialize($record['result']);
        $paper = Paper::fetch(intval($record['paper_id']));
        $questionType = unserialize($paper['question_type']);
        $type = array_keys($questionType);
        if (!empty($currentUser['examType'])) {
            $markType = $currentUser['examType'];
        } else {
            $markType = Question::MARKTYPE;
        }
        if ($paper['completion']) {
            unset($markType[array_search('fillIn', $markType)]);
        }
        $useMark = [];
        foreach ($type as $value) {
            if (in_array($value, $markType)) {
                array_push($useMark, $value);
            }
        }
        if (empty($useMark)) {
            return payload(error(-13, '该试卷没有需要手动评卷的题型'));
        }
        $useMarkResult = []; //需要评卷的题型
        foreach ($useMark as $val) {
            $useMarkResult[$val] = $record['result'][$val];
        }
        $question = [];
        array_map(
            function ($item) use (&$question) {
                $question = array_merge($question, $item);
            },
            $useMarkResult
        );
        $questionIds = Base::neaten($question, 'id');
        $questionArr = Question::search(['ids' => $questionIds], 0);
        $questionArr = Collection::key($questionArr, 'id');
        foreach ($useMarkResult as $key => &$item) {
            $item = array_map(
                function ($val) use ($questionArr, $record) {
                    $val = [
                        'id' => $val['id'],
                        'name' => $questionArr[$val['id']]['name'],
                        'questionAnswer' => $questionArr[$val['id']]['answer'],
                        'studentAnswer' => $val['answer'],
                        'analysis' => $questionArr[$val['id']]['analysis'],
                        'score' => $val['score'],
                        'knowledge' => $val['knowledge']
                    ];
                    if ($record['mark_id'] == 0) {
                        $val['score'] = null;
                    }
                    return $val;
                },
                $item
            );
        }
        $user = UserModel::fetch($record['user_id']);
        $data = [
            'userId' => $user['id'],
            'userName' => $user['name'],
            'results' => $useMarkResult,
            'questionType' => $questionType
        ];
        return payload(['data' => $data]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws Exception
     * @api {post} /exam/process/mark 保存评卷结果
     * @apiGroup Exam-process
     * @apiName sort9
     * @apiVersion 1.0.0
     *
     * @apiDescription 保存评卷结果
     *
     * @apiParam {Number} id 试卷id
     * @apiParam {String[]} result 试卷结果
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     *
     * @apiSuccessExample {json} Success-Response:
     * {"code":0,"message":"","dataSet":[], "total" : 0}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function mark(Request $request)
    {
        $currentUser = User::fetchCurrent();
        $input = $request->post();
        if (empty($input['id']) || empty($input['result'])) {
            return payload(error(-11, '参数不完整'));
        }
        $check = Record::markRole(intval($input['id']));
        if (!$check && !in_array('courseManger', $currentUser['privileges'])) {
            return payload(error(-12, '暂无该试卷的评卷权限'));
        }
        $record = Record::fetch(intval($input['id']));
        $recordResult = unserialize($record['result']);
        foreach ($input['result'] as $key => $value) {
            if (!isset($recordResult[$key])) {
                return payload(error(-11, '参数错误'));
            }
            $recordResult[$key] = $value;
        }
        $res = Record::updateResult(intval($input['id']), $recordResult);
        if (!$res) {
            return payload(error(-12, '评卷未作修改'));
        }
        return payload([]);
    }
}