<?php
namespace app\adminVue\controller;
use think\facade\Db;

class V2ExamSet extends V2Base
{
    private function response($work) {
        try { return json(['code'=>0,'data'=>$work()]); }
        catch (\InvalidArgumentException $e) { $this->fail($e->getMessage()); }
    }
    private static function version($base,$sets) {
        return V2QuestionBank::version(['base'=>json_encode(array_map('strval',$base)), 'sets'=>json_encode(array_map(function($r){return array_map(function($v){return $v===null?null:(string)$v;},$r);},$sets))]);
    }
    public static function rules($input,$counts) {
        $out=[];$questions=0;$points=0;
        foreach([1,2,3] as $i){
            $n=$input['num'.$i]??0;if($n==='')$n=0;$s=$input['score'.$i]??null;if((string)$n==='0'&&($s===null||$s===''))$s=0;
            if(!is_scalar($n)||!preg_match('/^(0|[1-9][0-9]*)$/D',(string)$n))throw new \InvalidArgumentException('抽题量须为非负整数，不使用的题型可留空或填0');
            if(!is_scalar($s)||!preg_match('/^(0|[1-9][0-9]*)(\.[0-9])?$/D',(string)$s)||(float)$s>99.9)throw new \InvalidArgumentException('每题分值须为0至99.9，最多1位小数');
            $n=(int)$n;$tenths=(int)round((float)$s*10);
            if($n>0&&$tenths<=0)throw new \InvalidArgumentException('参与抽题的题型每题分值必须大于0');
            $out['num'.$i]=$n;$out['score'.$i]=$tenths/10;$questions+=$n;$points+=$n*$tenths;
        }
        if($questions<1||$questions>60)throw new \InvalidArgumentException('总抽题量须为1至60道');
        $pass=$input['pass']??null;
        if(!is_scalar($pass)||!preg_match('/^[1-9][0-9]*$/D',(string)$pass)||(float)$pass*10>$points)throw new \InvalidArgumentException('合格分须为正整数且不能超过总分');
        $out['pass']=(int)$pass;
        foreach([1=>'单选题',2=>'多选题',3=>'判断题'] as $i=>$name)if($out['num'.$i]>($counts[$i]??0))throw new \InvalidArgumentException($name.'题库不足：现有'.($counts[$i]??0).'道，要求'.$out['num'.$i].'道');
        return $out;
    }
    private static function item($base,$sets,$counts) {
        $set=count($sets)===1?$sets[0]:null;$reasons=[];$state='normal';
        if(!$sets){$state='unconfigured';$reasons[]='尚未配置，请设置抽题量、分值和合格分';}
        elseif(count($sets)>1){$state='invalid';$reasons[]='同一考卷存在重复配置，请先核对历史数据';}
        else {
            if(!in_array((string)$set['type'],['1','2'],true)){$state='invalid';$reasons[]='考题类型异常，请先核对历史数据';}
            try{self::rules($set,[1=>PHP_INT_MAX,2=>PHP_INT_MAX,3=>PHP_INT_MAX]);}catch(\InvalidArgumentException $e){$state='invalid';$reasons[]=$e->getMessage();}
            foreach([1=>'单选题',2=>'多选题',3=>'判断题'] as $i=>$name)if((int)$set['num'.$i]>$counts[$i]){if($state==='normal')$state='shortage';$reasons[]=$name.'题库不足';}
        }
        if(array_sum($counts)===0)$reasons[]='暂无有效试题，请先到技能题库导入';
        $values=[];$total=0;$num=0;
        foreach([1,2,3] as $i){$values['num'.$i]=$set?(int)($set['num'.$i]??0):null;$values['score'.$i]=$set?(float)($set['score'.$i]??0):null;$total+=(float)($values['num'.$i]??0)*(float)($values['score'.$i]??0);$num+=(int)($values['num'.$i]??0);}
        return array_merge($values,['id'=>$set?(int)$set['id']:null,'item_type'=>(int)$base['id'],'name'=>$base['content'],'type'=>$set?(int)$set['type']:2,'typeName'=>$set?((int)$set['type']===1?'PPI':((int)$set['type']===2?'新考技能':'未知类型')):'新考技能','pass'=>isset($set['pass'])?(int)$set['pass']:null,'counts'=>$counts,'total'=>round($total,1),'questions'=>$num,'state'=>$state,'reasons'=>$reasons,'editable'=>count($sets)<=1&&(!$set||in_array((string)$set['type'],['1','2'],true)),'version'=>self::version($base,$sets)]);
    }
    public function index() {
        return $this->response(function(){
            $keyword=$this->request->get('keyword','');$type=$this->request->get('type','');$state=$this->request->get('state','');
            if(!is_string($keyword)||mb_strlen($keyword)>100||!is_scalar($type)||!in_array((string)$type,['','1','2'],true)||!is_string($state)||!in_array($state,['','normal','unconfigured','shortage','invalid'],true))$this->fail('筛选参数无效');
            $bases=Db::name('quality_base')->where(['mid'=>4,'status'=>1])->order('id')->select()->toArray();$ids=array_column($bases,'id');$sets=[];$counts=[];
            if($ids){foreach(Db::name('quality_exam_set')->whereIn('item_type',$ids)->order('id')->select()->toArray() as $s)$sets[$s['item_type']][]=$s;
            foreach(Db::name('quality_ppi_exam')->where('status',1)->whereIn('item_type',$ids)->field('item_type,exam_type,COUNT(*) as count')->group('item_type,exam_type')->select()->toArray() as $c)$counts[$c['item_type']][(int)$c['exam_type']]=(int)$c['count'];}
            $list=[];foreach($bases as $base){$row=self::item($base,$sets[$base['id']]??[],array_replace([1=>0,2=>0,3=>0],$counts[$base['id']]??[]));if(trim($keyword)!==''&&mb_stripos((string)$row['name'],trim($keyword))===false)continue;if($type!==''&&(string)$row['type']!==(string)$type)continue;if($state!==''&&$row['state']!==$state)continue;$list[]=$row;}
            $page=$this->request->get('page',1);$limit=$this->request->get('limit',15);foreach(['page'=>$page,'limit'=>$limit] as $key=>$value)if(!is_scalar($value)||!ctype_digit((string)$value)||(int)$value<1||(int)$value>($key==='page'?100000:100))$this->fail('分页参数无效');
            return ['list'=>array_slice($list,((int)$page-1)*(int)$limit,(int)$limit),'total'=>count($list)];
        });
    }
    public function save() {
        return $this->response(function(){
            $input=$this->request->post();$id=$input['item_type']??null;$stamp=$input['version']??null;
            if(!is_scalar($id)||!ctype_digit((string)$id)||(int)$id<1||!is_string($stamp))$this->fail('考卷参数无效');$id=(int)$id;
            Db::query('SELECT 1');$conn=Db::connect()->getConnection();$prefix=$conn->getConfig('prefix');if(!preg_match('/^[a-zA-Z0-9_]+$/D',$prefix))throw new \RuntimeException('表前缀无效');
            $pdo=$conn->getPdo();$base='`'.$prefix.'quality_base`';$set='`'.$prefix.'quality_exam_set`';$exam='`'.$prefix.'quality_ppi_exam`';
            $pdo->exec('LOCK TABLES '.$set.' WRITE, '.$base.' READ, '.$exam.' READ');
            try{
                $q=$pdo->prepare('SELECT * FROM '.$base.' WHERE id=? AND mid=4 AND status=1');$q->execute([$id]);$b=$q->fetch(\PDO::FETCH_ASSOC);if(!$b)throw new \InvalidArgumentException('考卷已停用或不存在，请刷新');
                $q=$pdo->prepare('SELECT * FROM '.$set.' WHERE item_type=? ORDER BY id');$q->execute([$id]);$old=$q->fetchAll(\PDO::FETCH_ASSOC);
                if(count($old)>1)throw new \InvalidArgumentException('同一考卷存在重复配置，禁止保存');
                if(!hash_equals(self::version($b,$old),$stamp))throw new \InvalidArgumentException('配置已被修改，请关闭弹窗并刷新后重试');
                if($old&&!in_array((string)$old[0]['type'],['1','2'],true))throw new \InvalidArgumentException('考题类型异常，请先核对历史数据');
                $q=$pdo->prepare('SELECT exam_type,COUNT(*) as count FROM '.$exam.' WHERE item_type=? AND status=1 GROUP BY exam_type');$q->execute([$id]);$counts=array_replace([1=>0,2=>0,3=>0],array_column($q->fetchAll(\PDO::FETCH_ASSOC),'count','exam_type'));
                $data=self::rules($input,$counts);$values=array_values($data); // All seven fields are written together in one SQL statement.
                if($old){$q=$pdo->prepare('UPDATE '.$set.' SET num1=?,score1=?,num2=?,score2=?,num3=?,score3=?,pass=? WHERE id=?');$values[]=$old[0]['id'];}
                else{$q=$pdo->prepare('INSERT INTO '.$set.' (num1,score1,num2,score2,num3,score3,pass,item_type,type) VALUES(?,?,?,?,?,?,?,?,2)');$values[]=$id;}
                $q->execute($values);return ['saved'=>true];
            }finally{$pdo->exec('UNLOCK TABLES');}
        });
    }
}
