<?php

declare(strict_types=1);

namespace app\common\model\exam\paper;

use app\common\model\account\Department;
use app\common\model\account\User as UserModel;
use app\common\model\Base;
use app\common\model\exam\Paper;
use app\common\model\exam\Question;
use app\control\model\User;
use Exception;
use mb\helper\Collection;
use think\facade\Db;
use think\facade\Log;

/**
 * Class Record
 * @package app\common\model\exam\paper
 */
class Record
{
    public const EXPORT_FIELDS = [
        '' => '',
        'id' => '序号',
        'uid' => '账号',
        'name' => '姓名',
        'gender' => '性别',
        'score' => '成绩',
        'pass' => '结果',
        'department' => '部门',
        'duty' => '职务',
        'company' => '单位',
        'birthday' => '出生年月日',
        'phone' => '电话',
        'cardType' => '证件类型',
        'cardNum' => '证件号码',
    ];
    /**
     * @param $filter
     * @param $pIndex
     * @param $pSize
     * @param $total
     * @return array
     */
    public static function search(array $filter, int $pIndex = 1, int $pSize = 10, &$total = 0)
    {
        $where = [];
        if (!empty($filter['paperId'])) {
            $where[] = ['rec.paper_id', '=', $filter['paperId']];
        }
        if (!empty($filter['departmentId'])) {
            $where[] = ['dep.id', '=', $filter['departmentId']];
        }
        if (!empty($filter['dutyId'])) {
            $where[] = ['user.duty', '=', $filter['dutyId']];
        }
        if (!empty($filter['userId'])) { //查某人的卷子
            $where[] = ['rec.user_id', '=', $filter['userId']];
        }
        if (!empty($filter['uid'])) { //按用户uid查询
            $where[] = ['user.uid', 'like', "%{$filter['uid']}%"];
        }
        if (!empty($filter['name'])) {
            $where[] = ['user.name', 'like', "%{$filter['name']}%"];
        }
        if (!empty($filter['status'])) {
            $where[] = ['rec.status', '=', $filter['status']];
        }
        if (!empty($filter['markStatus'])) {
            $where[] = ['rec.mark_status', '=', $filter['markStatus']];
        }
        if (!empty($filter['symbol'])) {
            $where[] = ['rec.score', $filter['symbol'], $filter['score']];
        }
        if (!empty($filter['timeStart'])) {
            $where[] = ['rec.time_end', '>=', strtotime($filter['timeStart'])];
        }
        if (!empty($filter['timeEnd'])) {
            $where[] = ['rec.time_end', '<=', strtotime($filter['timeEnd'])];
        }
        if (!empty($filter['type'])) {
            $where[] = ['paper.type', '=', $filter['type']];
        }
        if (!empty($filter['personal'])) {
            $where[] = ['paper.time_end', '<=', time()];
        }
        try {
            $total = Db::table('exam_paper_records')->alias('rec')->join('exam_papers paper', 'rec.paper_id = paper.id')
                ->leftJoin('account_users user', 'rec.user_id = user.id')
                ->leftJoin('account_departments dep', 'user.department = dep.id')->where($where)->count();
            $query = Db::table('exam_paper_records')->alias('rec')->join('exam_papers paper', 'rec.paper_id = paper.id')
                ->leftJoin('account_users user', 'rec.user_id = user.id')
                ->leftJoin('account_departments dep', 'user.department = dep.id')->where($where)
                ->field(
                    'rec.*,user.uid,user.name,user.gender,user.department,user.duty,
                    user.company,user.phone,user.birthday,user.card_type,user.card_num,paper.title,paper.through_points'
                );
            if (!empty($pIndex)) {
                $query->page($pIndex, $pSize);
            }
            $dataSet = $query->select()->toArray();
            return array_map(
                function ($val) {
                    return Collection::keyStyle($val, Collection::NAME_STYLE_JAVA);
                },
                $dataSet
            );
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return [];
    }

    /**
     * @param array $data
     * @return int|string
     */
    public static function add(array $data)
    {
        $newData = Collection::keyStyle($data, Collection::NAME_STYLE_C);
        try {
            return Db::table('exam_paper_records')->insertGetId($newData);
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return 0;
    }

    /**
     * @param $filter
     * @param array $data
     * @return bool
     * @throws \think\Exception
     */
    public static function update($filter, array $data)
    {
        $filter = self::parseFilters($filter);
        $newData = Collection::keyStyle($data, Collection::NAME_STYLE_C);
        try {
            $res = Db::table('exam_paper_records')->where($filter)->update($newData);
            if ($res === 1) {
                return true;
            }
            return false;
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return false;
    }

    /**
     * @param $filter
     * @return float|int|string
     * @throws \think\Exception
     */
    public static function recordNum($filter)
    {
        $filter = self::parseFilters($filter);
        try {
            $num = Db::table('exam_paper_records')->where($filter)->group('user_id')->count();
            return $num;
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return 0;
    }

    /**
     * @param $filter
     * @return array|int
     * @throws \think\Exception
     */
    public static function joinUser($filter)
    {
        $filter = self::parseFilters($filter);
        try {
            $user = Db::table('exam_paper_records')->where($filter)->group('user_id')->field('user_id')->select();
            if (empty($user)) {
                return [];
            }
            return Base::neaten($user, 'user_id');
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return 0;
    }

    /**
     * @param $filters
     * @return array
     * @throws \think\Exception
     */
    public static function parseFilters($filters)
    {
        $newFilters = [];
        if (is_array($filters)) {
            if (!empty($filters['id'])) {
                $newFilters[] = ['id', '=', $filters['id']];
            }
            if (empty($filters)) {
                throw error(-19, '缺少必填参数ID');
            }
            if (!empty($filters['paper'])) {
                $newFilters[] = ['paper_id', '=', $filters['paper']];
            }
            if (!empty($filters['user'])) {
                $newFilters[] = ['user_id', '=', $filters['user']];
            }
        } else {
            $newFilters[] = ['id', '=', intval($filters)];
        }
        return $newFilters;
    }

    /**
     * @param string $type
     * @param string $ids
     * @return bool
     */
    public static function delete(string $type, string $ids)
    {
        if ($type == 'single') {
            $where[] = ['id', '=', $ids];
        } else {
            $where[] = ['id', 'in', "{$ids}"];
        }
        try {
            $offect = Db::table('exam_paper_records')
                ->where($where)
                ->delete();
            if ($offect) {
                return true;
            }
            return false;
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return false;
    }

    /**
     * 通过试卷id删除记录
     * @param string $id
     * @return bool
     */
    public static function deleteRecord(string $id)
    {
        $where[] = ['paper_id', '=', $id];
        try {
            $offect = Db::table('exam_paper_records')
                ->where($where)
                ->delete();
            if ($offect) {
                return true;
            }
            return false;
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return false;
    }

    /**
     * @param $filter
     * @return array
     * @throws \think\Exception
     */
    public static function fetch($filter)
    {
        $filter = self::parseFilters($filter);
        try {
            return Db::table('exam_paper_records')->where($filter)->find();
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return [];
    }

    /**
     * @param $filter
     * @return array
     * @throws \think\Exception
     */
    public static function fetchRec($filter)
    {
        $filter = self::parseFilters($filter);
        try {
            return Db::table('exam_paper_records')->where($filter)->order('id', 'desc')->find();
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return [];
    }

    /**
     * @param $filter
     * @param $type
     * @return float|int|mixed|string
     * @throws \think\Exception
     */
    public static function score($filter, $type)
    {
        if (!empty($filter['paper'])) {
            $where[] = ['paper_id', '=', $filter['paper']];
        }
        if (!empty($filter['department'])) {
            $where[] = ['department', '=', $filter['department']];
        }
        $query = Db::table('exam_paper_records')->alias('rec')->join('account_users user', 'rec.user_id = user.id', 'LEFT')->where($where);
        $score = 0;
        try {
            switch ($type) {
                case 'avg':
                    $score = $query->avg('score');
                    break;
                case 'max':
                    $score = $query->max('score');
                    break;
                case 'min':
                    $score = $query->min('score');
                    break;
                default:
                    return 0;
            }
            return sprintf("%.1f", $score);
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return $score;
    }

    public static function examPass($filter)
    {
        try {
            $where = "paper.`id` = {$filter['paper']} and rec.status = 'end' 
                and rec.score > paper.through_points and paper.time_end <= " . time();
            if (!empty($filter['department'])) {
                $where .= " and user.department = {$filter['department']}";
            }
            $query = Db::table('exam_paper_records')->alias('rec')->join('exam_papers paper', 'rec.paper_id = paper.id')->join('account_users user', 'rec.user_id = user.id', 'left')
                ->where($where)->field(
                    'rec.*,paper.title,paper.through_points'
                );
            return $query->count();
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return 0;
    }

    /**
     * @param string $type
     * @return float|int|string
     */
    public static function passNum($type = '')
    {
        $user = User::fetchCurrent();
        try {
            $where = "user_id = {$user['id']} and rec.status = 'end' 
                and rec.score > paper.through_points and paper.time_end <= " . time();
            if (!empty($type)) {
                $where .= " and paper.`type` = '{$type}'";
            }
            $query = Db::table('exam_paper_records')->alias('rec')->join('exam_papers paper', 'rec.paper_id = paper.id')
                ->where($where)->field(
                    'rec.*,paper.title,paper.through_points'
                );
            return $query->count();
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return 0;
    }

    /**
     * 获取用户考试排名
     * @param int $paperId
     * @param int $recordId
     * @return int
     */
    public static function serial(int $paperId, int $recordId)
    {
//        $query = "SELECT t4.* FROM (
//              select t1.*,(@num := @num+1) as rank
//                  from exam_paper_records as t1,(select (@num := 0)) as t3 where score = (
//                      select max(score) from exam_paper_records as t2 where t2.user_id = t1.user_id)
//                  AND t1.paper_id = 22 ORDER BY score desc) as t4 where user_id = 1"; //相同成绩按照id排
        /*$query = "select *,
                        (
                            select count(DISTINCT score)
                                from (
                                    select  t1.* from exam_paper_records as t1 where score =  (
                                            select max(score) from exam_paper_records as t2 where t2.user_id = t1.user_id
                                        ) AND t1.paper_id = 22) AS b WHERE a.score<b.score)+1 AS rank
                        from (
                            select  t1.* from exam_paper_records as t1 where score =  (
                                select max(score) from exam_paper_records as t2 where t2.user_id = t1.user_id
                            ) AND t1.paper_id = 22) AS a ORDER BY rank";*/

        try {
            /*$query = "select * from (
                        select *,
                        (
                            select count(DISTINCT score)
                                from (
                                    select  t1.* from exam_paper_records as t1 where score =  (
                                            select max(score) 
                                            from exam_paper_records as t2 where t2.user_id = t1.user_id
                                        ) AND t1.paper_id = $paperId ) AS b WHERE a.score<b.score)+1 AS rank
                        from (
                            select  t1.* from exam_paper_records as t1 where score =  (
                                select max(score) from exam_paper_records as t2 where t2.user_id = t1.user_id
                            ) AND t1.paper_id = $paperId ) AS a ORDER BY rank
                        ) as t5
                    where t5.user_id = $userId";*/
            /*$query = "select *,
                        (
                            select count(DISTINCT score)
                                from (
                                    select  t1.* from exam_paper_records as t1 where score =  (
                                            select max(score) from exam_paper_records as t2 where 
                                            t2.user_id = t1.user_id
                                        ) AND t1.paper_id = $paperId ) AS b WHERE a.score<b.score)+1 AS rank
                        from (
                            select  t1.* from exam_paper_records as t1 where t1.paper_id = $paperId ) 
                            AS a where id = $recordId ORDER BY rank";
            $res = Db::query($query);*/
            $res = Db::table('exam_paper_records')
                ->where(['paper_id' => $paperId, 'status' => 'end'])
                ->order('score', 'desc')->field('id')->select()->toArray();
            $ids = Base::neaten($res, 'id');
            $rank = array_search($recordId, $ids);
            return $rank + 1;
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return 0;
    }

    /**
     * 更新成绩和结果
     * @param $filter
     * @param $result
     * @return bool
     * @throws \think\Exception
     */
    public static function updateResult($filter, $result)
    {
        $record = self::fetch($filter);
        $paper = Paper::fetch($record['paper_id']);
        $questionType = unserialize($paper['question_type']);
        $questionType = array_map(
            function ($val) {
                $val = $val['point'];
                return $val;
            },
            $questionType
        );
        $fillIn = $paper['completion'] ? true : false;
        $res = self::grade($result, $questionType, $fillIn);
        $res['result'] = serialize($res['result']);
        $user = User::fetchCurrent();
        $res['mark_id'] = $user['id'];
        $res['mark_status'] = 'end';
        try {
            $filter = self::parseFilters($filter);
            $offect = Db::table('exam_paper_records')->where($filter)->update($res);
            if ($offect === 1) {
                return true;
            }
        } catch (Exception $e) {
            Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
        }
        return false;
    }

    /**
     * 计算并返回成绩和结果
     * @param array $result 试题结果
     * @param array $questionType 题型和对应分值
     * @param bool $fillIn 填空题是否自动评
     * @return array
     */
    public static function grade(array $result, array $questionType, $fillIn = false)
    {
        $array = ['judge', 'choice', 'choices'];
        if ($fillIn) {
            array_push($array, 'fillIn');
        }
        $score = 0;
        foreach ($result as $key => &$item) {
            foreach ($item as &$val) {
                if (in_array($key, $array)) {
                    $res = Question::verify($val['id'], $val['answer']);
                    if ($res) {
                        $val['score'] = $questionType[$key];
                    }
                }
                $score += $val['score'];
            }
        }
        return ['score' => $score, 'result' => $result];
    }

    /**
     * 检测是否有评卷权限
     * @param $filter
     * @return bool
     * @throws \think\Exception
     */
    public static function markRole($filter)
    {
        $user = User::fetchCurrent();
        if (!in_array($user['role'], ['root', 'manager'])) {
            return false;
        }
        $record = self::fetch($filter);
        if (empty($record)) {
            return false;
        }
        if ($user['role'] != 'root') {
            $where = "`paper_id` = {$record['paper_id']}";
            $department = Department::fetch($user['department']);
            if (!empty($department)) {
                $where .= " and  (`type` = ''
                or (`type` = 'user' and `binding_id` = {$user['id']})
                or (`type` = 'department' and `binding_id` = {$department['first']})
                or (`type` = 'department' and `binding_id` = {$user['department']}))";
            } else {
                $where .= " and  (`type` = ''
                or (`type` = 'user' and `binding_id` = {$user['id']})
                or (`type` = 'department' and `binding_id` = {$user['department']}))";
            }
            try {
                $res = Db::table('exam_paper_mark')->where($where)->find();
                if (empty($res)) {
                    return false;
                }
                return true;
            } catch (Exception $e) {
                Log::channel('myError')->write($e->getMessage(), \think\Log::ERROR);
            }
            return false;
        }
        return true;
    }

    /**
     * 试卷预览
     * @param int $recordId 考试记录id
     * @param string $type 类型 all -- 全部展示 wrong -- 错题展示
     * @return array
     * @throws \think\Exception
     */
    public static function preview(int $recordId, string $type)
    {
        $record = self::fetch($recordId);
        $record['result'] = unserialize($record['result']);
        $paper = Paper::fetch(intval($record['paper_id']));
        $show = Paper::sort(unserialize($paper['question_type']), $record['result'], $paper['way']);
        $question = [];
        $questionNum = 0;
        foreach ($record['result'] as $val) {
            $question = array_merge($question, $val);
            array_map(
                function ($i) use (&$questionNum) {
                    $questionNum++;
                },
                $val
            );
        }
        $questionIds = Base::neaten($question, 'id');
        $questionArr = Question::search(['ids' => $questionIds], 0);
        $questionArr = Collection::key($questionArr, 'id');
        $show = array_filter(
            array_map(
                function ($item) use ($questionArr, $type) {
                    $item = array_map(
                        function ($value) use ($questionArr, $type) {
                            if ((($type == 'wrong') && empty($value['score'])) || ($type != 'wrong')) {
                                if (!isset($questionArr[$value['id']])) {
                                    return [];
                                }
                                $value = [
                                    'id' => $value['id'],
                                    'name' => $questionArr[$value['id']]['name'],
                                    'options' => $questionArr[$value['id']]['options'],
                                    'questionAnswer' => $questionArr[$value['id']]['answer'],
                                    'studentAnswer' => $value['answer'],
                                    'analysis' => $questionArr[$value['id']]['analysis'],
                                    'score' => $value['score'],
                                ];
                            } else {
                                $value = [];
                            }
                            return $value;
                        },
                        $item
                    );
                    $item = array_values(array_filter(array_values($item)));
                    return $item;
                },
                $show
            )
        );
        $user = UserModel::fetch($record['user_id']);
        $dataSet = [
            'title' => $paper['title'],
            'userId' => $user['uid'],
            'userName' => $user['name'],
            'questionNum' => $questionNum,
            'totalPoints' => $paper['total_points'],
            'throughPoints' => $paper['through_points'],
            'score' => $record['score'],
            'timeEnd' => $record['time_end'],
            'markId' => $record['mark_id'],
            'show' => $show,
            'questionType' => unserialize($paper['question_type'])
        ];
        if ($dataSet['markId']) {
            $userInfo = UserModel::fetch($dataSet['markId']);
            $dataSet['markTitle'] = isset($userInfo['uid']) ? $userInfo['uid'] : '';
        } else {
            $dataSet['markTitle'] = '';
        }
        return $dataSet;
    }

}