<?php

declare(strict_types=1);

namespace app\control\controller\exam;

use app\BaseController;
use app\common\model\account\Department;
use app\common\model\account\User as UserModel;
use app\common\model\exam\Knowledge;
use app\common\model\exam\paper\Record;
use app\common\model\exam\Subject;
use app\control\model\User;
use app\Request;
use mb\helper\Collection;
use think\response\Json;
use app\common\model\exam\Paper as ModelPaper;
use app\common\model\exam\Question;

/**
 * Class Paper
 * @package app\control\controller\exam
 */
class Paper extends BaseController
{
    /**
     * @param Request $request
     * @return Json
     * @api {post} /exam/paper/search 试卷列表
     * @apiGroup Exam-paper
     * @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']) ? 'competition' : $input['type'];
//        $filters['type'] = $type;
        if (!empty($input['title'])) {
            $filters['title'] = $input['title'];
        }
        if (!empty($input['timeStart'])) {
            $filters['timeStart'] = $input['timeStart'];
        }
        if (!empty($input['timeEnd'])) {
            $filters['timeEnd'] = $input['timeEnd'];
        }
        if (!empty($input['mode'])) {
            $filters['mode'] = $input['mode'];
        }
        $dataSet = ModelPaper::search($filters, $pageIndex, $pageSize, $total);
        $dataSet = array_map(
            function ($val) {
                $val['questionType'] = unserialize($val['questionType']);
                $totalNum = 0;
                foreach ($val['questionType'] as $item) {
                    $totalNum += $item['questionNum'];
                }
                unset($val['questionType']);
                $val['totalQuestionNum'] = $totalNum;
                return $val;
            },
            $dataSet
        );
        return payload(['dataSet' => $dataSet, 'total' => $total]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws \think\Exception
     * @api {post} /exam/paper/add 试卷新增/修改
     * @apiGroup Exam-paper
     * @apiName sort2
     * @apiVersion 1.0.0
     *
     * @apiDescription 试卷新增/修改
     *
     * @apiParam {Number} [id] 试卷id
     * @apiParam {String} type 考试类型<br>competition -- 竞赛级别<br>promote -- 提升级<br>beginner -- 入门级<br>simulate -- 模拟考
     * @apiParam {String} title  试卷名称
     * @apiParam {String} mode  组卷方式 random -- 随机组卷 <br> hand -- 手工组卷
     * @apiParam {String} way  出题方式 fixed -- 题序固定 <br> random -- 题序随机 <br> question -- 试题随机
     * @apiParam {Number} display  显示模式 1 -- 整卷 2 -- 逐题
     * @apiParam {Number} time  考试时间
     * @apiParam {Number} totalPoints  试卷总分
     * @apiParam {Number} throughPoints  通过分数
     * @apiParam {String} timeStart  答题起始时间
     * @apiParam {String} timeEnd  答题结束时间
     * @apiParam {String[]} strategies  策略 <br>随机组卷时传['type' => 'judge','knowledge' => 2,'subject' => 3,'strategy' => [
     * ['level' => 'easy', 'num' => 0], ['level' => 'moreEasily', 'num' => 1],
     * ['level' => 'medium', 'num' => 0], ['level' => 'moreDifficult', 'num' => 0], ['level' => 'difficult', 'num' => 0]
     * ]],<br> 手工组卷时传['type' => 'judge', 'knowledge' => 2, 'subject' => 3, 'strategy' => [1, 2, 3]],
     * @apiParam {String[]} questionType  题型设置 type -- 类型 point -- 分值 questionNum 题量 order -- 顺序 <br>
     * ['type' => 'judge','point' => 1,'questionNum' => 2,'order' => 1],
     * @apiParam {Number} definition 分数定义 1 -- 使用试卷定义分值 2 -- 使用题库定义分值 默认是1
     * @apiParam {Number} multiple 允许多次考试 0 -- 不允许 1 -- 允许
     * @apiParam {Number} result 允许查看结果 0 -- 不允许 1 -- 允许
     * @apiParam {Number} completion 允许填空类自动判卷 0 -- 不允许 1 -- 允许
     * @apiParam {Number} save 自动保存答卷时长
     * @apiParam {String[]} watch 查看人员
     * @apiParam {String[]} mark 批卷人
     *
     * @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 add(Request $request)
    {
        $currentUser = User::fetchCurrent();
        $input = $request->post();
        $data = Collection::elements(
            [
                'title',
                'type',
                'mode',
                'way',
                'display',
                'time',
                'totalPoints',
                'throughPoints',
                'strategies',
                'questionType',
                'timeStart',
                'timeEnd'
            ],
            $input
        );
        foreach ($data as $v) {
            if (empty($v)) {
                return payload(error(-1, '参数不完整'));
            }
        }
        if (
            !in_array($data['mode'], ModelPaper::MODE) ||
            !in_array($data['type'], ModelPaper::TYPE) || !in_array($data['way'], ModelPaper::WAY) ||
            !is_array($data['strategies']) || !is_array($data['questionType']) ||
            !in_array($data['display'], [1, 2])
        ) {
            return payload(error(-2, '参数错误'));
        }
        if (($data['mode'] == 'hand') && ($data['way'] == 'question')) {
            return payload(error(-9, '手工组卷不支持试题随机'));
        }
        $id = empty($input['id']) ? 0 : intval($input['id']);
        if (!empty($id)) {
            $record = Record::recordNum(['paper' => intval($id)]);
            if (!empty($record)) {
                return payload(error(-1, '该考卷已被使用，请先删除答卷再进行修改'));
            }
        }
        if (!empty($id) && !ModelPaper::check($id)) {
            return payload(error(-11, '您暂无操作权限'));
        }
        $data['definition'] = !empty($input['definition']) ? intval($input['definition']) : 1;
        $data['multiple'] = !empty($input['multiple']) ? intval($input['multiple']) : 0;
        $data['result'] = !empty($input['result']) ? intval($input['result']) : 0;
        $data['completion'] = !empty($input['completion']) ? intval($input['completion']) : 0;
        $data['save'] = !empty($input['save']) ? intval($input['save']) : 0;
        $data['question_type'] = Collection::key($data['questionType'], 'type');
        $questionType = [];
        //判断题目数量是否正确
        foreach ($data['strategies'] as &$str) {
            if (!isset($questionType[$str['type']])) {
                $questionType[$str['type']] = [
                    'type' => $str['type'],
                    'num' => 0
                ];
            }
            $questionNum = 0;
            if ($data['mode'] == 'hand') {
                $questionNum += count($str['strategy']);
            } else {
                foreach ($str['strategy'] as $v) {
                    $questionNum += $v;
                }
            }
            $questionType[$str['type']]['num'] += $questionNum;
        }
        $totalPoint = 0;
        foreach ($questionType as $que) {
            if ($data['question_type'][$que['type']]['questionNum'] != $que['num']) {
                return payload(error(-20, '题型数量有误'));
            }
            $totalPoint += $data['question_type'][$que['type']]['point'] * $que['num'];
        }
        if ($totalPoint != $data['totalPoints']) {
            return payload(error(-88, '总分不一致'));
        }
        $data['founder'] = $currentUser['id'];
        $data['watch'] = $input['watch'];
        $data['mark'] = $input['mark'];
        $data['timeStart'] = strtotime($data['timeStart']);
        $data['timeEnd'] = strtotime($data['timeEnd']);
        $id = ModelPaper::modify($data, $id);
        if (is_error($id)) {
            return payload(error(-10, '题库策略发生变更，请修改试题策略'));
        }
        if (!$id) {
            return payload(error(-99, '新增失败'));
        }
        return payload(['id' => $id]);
    }

    /**
     * @param Request $request
     * @return Json
     * @api {post} /exam/paper/strategy 生成策略
     * @apiGroup Exam-paper
     * @apiName sort3
     * @apiVersion 1.0.0
     *
     * @apiDescription 生成策略
     *
     * @apiParam {String} mode 组卷方式 random -- 随机组卷 hand -- 手工组卷
     * @apiParam {Number} [subjectId]  科目id
     * @apiParam {String[]} [knowledgeId]  知识点id 传数组[1]
     * @apiParam {String} [type]  题型名称 judge choice
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    手工组卷返回数据
     * @apiSuccess {Number} dataSet.id 试题id 手工组卷时返回
     * @apiSuccess {String} dataSet.subjectTitle 科目名称 公共部分
     * @apiSuccess {String} dataSet.knowledgeTitle 知识点名称 公共部分
     * @apiSuccess {String} dataSet.type 题型名称 公共部分
     * @apiSuccess {String} dataSet.name 试题内容 手工组卷时返回
     * @apiSuccess {Number} dataSet.createdTime 试题创建时间 手工组卷时返回
     * @apiSuccess {String} dataSet.score 试题分数 手工组卷时返回
     * @apiSuccess {String} dataSet.difficultyLevel 试题难度 手工组卷时返回
     * @apiSuccess {String} dataSet.founderTitle 创建人 手工组卷时返回
     * @apiSuccess {Number} dataSet.knowledge 知识点id 随机组卷时返回
     * @apiSuccess {Number} dataSet.subject 科目id 随机组卷时返回
     * @apiSuccess {String[]} dataSet.strategy 策略 随机组卷时返回
     * @apiSuccess {String} dataSet.strategy.level 难度 随机组卷时返回
     * @apiSuccess {Number} dataSet.strategy.questionNum 题目数量 随机组卷时返回
     *
     * @apiSuccessExample {json} 手工组卷结果:
     * {"errCode": 0,"errMsg": "ok","dataSet": [
     * {"id": 13,"name": "测试判断题","options": "a:0:{}","answer": "b:1;","type": "judge","score": "2.00",
     * "founder": 1,"difficultyLevel": "medium","knowledge": 3,"subject": 3,"analysis": "判断题解析","createdTime": 0,
     * "subjectTitle": "","knowledgeTitle": "","founderTitle": "root" }]}
     *
     * @apiSuccessExample {json} 随机组卷结果:
     * {"errCode": 0,"errMsg": "ok","dataSet": [{
     * "type": "judge","knowledge": 2,"subject": 3,"strategy": {"easy": 1,"moreEasily": 0,"medium": 0,
     * "moreDifficult": 0,"difficult": 0},"subjectTitle": "","knowledgeTitle": "测试知识点1"}]}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function strategy(Request $request)
    {
        $input = $request->post();
        if (empty($input['mode'])) {
            return payload(error(-1, '参数不完整'));
        }
        $filter = [];
        if (!empty($input['type'])) {
            $filter['type'] = $input['type'];
        }
        if (!empty($input['subject']) && !empty($input['knowledge'])) {
            if (!is_array($input['knowledge'])) {
                return payload(error(-2, '参数类型有误'));
            }
            $filter['subject'] = $input['subject'];
        }
        if (empty($filter)) {
            return payload(['dataSet' => []]);
        }
        $result = [];
        if (!empty($input['subject']) && !empty($input['knowledge'])) {
            foreach ($input['knowledge'] as $val) {
                $filter['knowledge'] = $val;
                $result[] = ModelPaper::strategy($filter, $input['mode']);
            }
        } else {
            $result[] = ModelPaper::strategy($filter, $input['mode']);
        }
        $dataSet = [];
        foreach ($result as $res) {
            $dataSet = array_merge($dataSet, $res);
        }
        $returnSet = array_map(
            function ($row) {
                $subjectInfo = Subject::fetch(intval($row['subject']));
                $row['subjectTitle'] = empty($subjectInfo['title']) ? '' : $subjectInfo['title'];
                $knowledgeInfo = Knowledge::fetch(intval($row['knowledge']));
                $row['knowledgeTitle'] = empty($knowledgeInfo['title']) ? '' : $knowledgeInfo['title'];
                if (isset($row['founder'])) {
                    $user = UserModel::fetch(intval($row['founder']));
                    $row['founderTitle'] = empty($user) ? '' : $user['uid'];
                }
                return $row;
            },
            $dataSet
        );
        return payload(['dataSet' => $returnSet]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws \think\Exception
     * @api {post} /exam/paper/detail 试卷详情
     * @apiGroup Exam-paper
     * @apiName sort4
     * @apiVersion 1.0.0
     *
     * @apiDescription 试卷详情
     *
     * @apiParam {Number} id 试卷id
     *
     * @apiSuccess {Number} code    状态码，0：请求成功
     * @apiSuccess {String} message   提示信息
     * @apiSuccess {Object} dataSet    返回数据
     *
     * @apiSuccessExample {json} Success-Response:
     * {"errCode": 0,"errMsg": "ok","dataSet": {"id": 1,"title": "测试卷1","type": "competition","mode": "hand",
     * "way": "fixed","display": 1,"time": 40,"totalPoints": "6.00","throughPoints": "2.00","definition": 1,
     * "multiple": 0,"result": 0,"completion": 0,"save": 0,"strategies": [{"type": "judge","knowledge": 2,"subject": 3,
     * "strategy": [1,2,3]},{"type": "choice","knowledge": 2,"subject": 3,"strategy": [4]},{"type": "judge",
     * "knowledge": 3,"subject": 16,"strategy": [5]}],"questionType": {"judge": {"type": "judge","point": 1,
     * "questionNum": 4,"order": 1},"choice": {"type": "choice","point": 2,"questionNum": 1,"order": 2}},
     * "timeCreated": 1591597450,"timeStart": 1591372800,"timeEnd": 1593532800,"founder": 1,
     * "watch": "","mark": "","founderTitle": "root"}}
     *
     * @apiErrorExample {json} Error-Response:
     * {"code":5001,"message":"接口异常"}
     */
    public function detail(Request $request)
    {
        $input = $request->post();
        if (empty($input['id'])) {
            return payload(error(-1, '参数不完整'));
        }
        $detail = ModelPaper::detail(intval($input['id']));
        $detail['questionType'] = array_values($detail['questionType']);
        unset($detail['question']);
        if (!empty($detail['watch']['user'])) {
            $detail['watch']['user'] = array_map(
                function ($v) {
                    $user = UserModel::fetch(intval($v));
                    if (!empty($user)) {
                        $department = Department::fetch($user['department']);
                        $user = [
                            'id' => $user['id'],
                            'uid' => $user['uid'],
                            'name' => $user['name'],
                            'department' => $department['id'],
                            'departmentTitle' => $department['title'],
                        ];
                    }
                    return $user;
                },
                $detail['watch']['user']
            );
        }
        if (!empty($detail['mark']['user'])) {
            $detail['mark']['user'] = array_map(
                function ($v) {
                    $user = UserModel::fetch(intval($v));
                    if (!empty($user)) {
                        $department = Department::fetch($user['department']);
                        $user = [
                            'id' => $user['id'],
                            'uid' => $user['uid'],
                            'name' => $user['name'],
                            'department' => $department['id'],
                            'departmentTitle' => $department['title'],
                        ];
                    }
                    return $user;
                },
                $detail['mark']['user']
            );
        }
        if (empty($detail['watch']['user']) && empty($detail['watch']['department'])) {
            $detail['watch'] = "";
        }
        if (empty($detail['mark']['user']) && empty($detail['mark']['department'])) {
            $detail['mark'] = "";
        }
        $userInfo = UserModel::fetch($detail['founder']);
        $detail['founderTitle'] = isset($userInfo['uid']) ? $userInfo['uid'] : '';
        return payload(['dataSet' => $detail]);
    }

    /**
     * @param Request $request
     * @return Json
     * @api {post} /exam/paper/delete 试卷删除
     * @apiGroup Exam-paper
     * @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'];
        }
        $record = ModelPaper::paperUsed(explode(',', $ids));
        if (!$record) {
            return payload(error(-1, '试卷已被使用，请先删除答题记录'));
        }
        $res = ModelPaper::delete($input['type'], $ids);
        if (!$res) {
            return payload(error(-1, '删除失败'));
        }
        return payload([]);
    }

    /**
     * @param Request $request
     * @return Json
     * @throws \think\Exception
     * @api {post} /exam/paper/preview 试卷预览
     * @apiGroup Exam-paper
     * @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 preview(Request $request)
    {
        $input = $request->post();
        if (empty($input['id'])) {
            return payload(error(-1, '参数不完整'));
        }
        $dataSet = ModelPaper::preview(intval($input['id']));
        if (is_error($dataSet)) {
            return payload(error(-11, $dataSet->getMessage()));
        }
        $question = [];
        foreach ($dataSet['question'] as $v) {
            $question = array_merge($question, $v);
        }
        $questionArr = Question::search(['ids' => $question], 0);
        $questionArr = Collection::key($questionArr, 'id');
        foreach ($dataSet['question'] as $k => &$val) {
            foreach ($val as &$v) {
                if (isset($questionArr[$v])) {
                    $v = $questionArr[$v];
//                    $v['analysis'] = unserialize($v['analysis']);
                    $v = Collection::elements(['id', 'name', 'options', 'answer', 'analysis'], $v);
                } else {
                    continue;
                }
            }
        }
        return payload(['dataSet' => $dataSet]);
    }

}