<?php
namespace app\adminVue\controller;

use think\facade\Db;
use think\facade\Cache;
use think\facade\Filesystem;

class V2Performance extends V2Base
{
    const FIELDS='id,eid,tid,aid,item,kpi_date,create_time,update,score,status,mark,rej_mark,img';

    private function integer($value,$min=1,$max=2147483647)
    {
        if (!is_scalar($value) || !preg_match('/^[0-9]+$/D',(string)$value) || (float)$value<$min || (float)$value>$max) $this->fail('数字参数无效');
        return (int)$value;
    }
    private function day($value)
    {
        if (!is_string($value)) $this->fail('日期格式无效');
        $day=\DateTimeImmutable::createFromFormat('!Y-m-d',$value,new \DateTimeZone('Asia/Shanghai'));
        if (!$day || $day->format('Y-m-d')!==$value || $value<'2000-01-01' || $value>'2037-12-31') $this->fail('日期格式无效');
        return $day;
    }
    private function result($data) { return json(['code'=>0,'msg'=>'','data'=>$data]); }
    private function query($export=false)
    {
        $q=Db::name('kpi_record')->where('status','>',0);
        $start=$this->request->get('startDate','');$end=$this->request->get('endDate','');
        if (!is_string($start)||!is_string($end)||($export&&(!$start||!$end))) $this->fail('请选择有效日期区间');
        $first=$start!==''?$this->day($start):null;$last=$end!==''?$this->day($end):null;
        if ($first&&$last&&$first>$last) $this->fail('开始日期不能晚于结束日期');
        if ($first) $q->where('kpi_date','>=',$first->getTimestamp());
        if ($last) $q->where('kpi_date','<',$last->modify('+1 day')->getTimestamp());
        if ($export) return $q;
        $kw=$this->request->get('keyword','');if(!is_string($kw)||mb_strlen($kw)>100)$this->fail('工号格式无效');
        $kw=trim($kw);if(ctype_digit($kw))$kw=ltrim($kw,'0')?:'0';
        if($kw!=='')$q->whereLike('eid','%'.addcslashes($kw,'%_\\').'%');
        $status=$this->integer($this->request->get('status',0),0,4);if($status)$q->where('status',$status);
        $project=$this->integer($this->request->get('project',0),0);$team=$this->integer($this->request->get('team',0),0);
        if($project)$q->whereIn('tid',Db::name('team_list')->where('project',$project)->column('id') ?: [-1]);
        if($team)$q->where('tid',$team);
        return $q;
    }
    private function canDelete($row)
    {
        return ((int)$row['status']===1&&(int)$row['aid']===(int)$this->actor['id']) || ((int)$this->actor['dpt']===6&&(int)$row['status']>1);
    }
    private function stamp($value,$date=false)
    {
        return (int)$value>0?(new \DateTimeImmutable('@'.(int)$value))->setTimezone(new \DateTimeZone('Asia/Shanghai'))->format($date?'Y-m-d':'Y-m-d H:i:s'):'';
    }
    private function present($rows)
    {
        if(!$rows)return [];
        $eids=implode(',',array_map('intval',array_column($rows,'eid')));
        $employees=Db::name('member_list')->whereRaw('CAST(eid AS UNSIGNED) IN ('.$eids.')')->order('status,id desc')->field('eid,ename')->select()->toArray();
        $names=[];$numbers=[];foreach($employees as $employee)if(!isset($names[(int)$employee['eid']])){$names[(int)$employee['eid']]=$employee['ename'];$numbers[(int)$employee['eid']]=(string)$employee['eid'];}
        $teams=Db::name('team_list')->whereIn('id',array_column($rows,'tid'))->column('team','id');
        $admins=Db::name('admin_user')->whereIn('id',array_column($rows,'aid'))->column('truename','id');
        $clauses=Db::name('kpi_list')->whereIn('id',array_column($rows,'item'))->field('id,job,item,content,scores')->select()->toArray();$clauses=array_column($clauses,null,'id');
        $list=[];foreach($rows as $row){$clause=$clauses[$row['item']]??[];
            $list[]=['id'=>(int)$row['id'],'employeeNo'=>$numbers[(int)$row['eid']]??(string)$row['eid'],'employeeName'=>$names[(int)$row['eid']]??'人员资料缺失','teamName'=>$teams[$row['tid']]??'班组资料缺失',
                'assessorName'=>$admins[$row['aid']]??'评估人资料缺失','category'=>$clause?($clause['job'].' - '.$clause['item']):'条款资料缺失','content'=>$clause['content']??'条款资料缺失','standard'=>$clause['scores']??'',
                'date'=>$this->stamp($row['kpi_date'],true),'createdAt'=>$this->stamp($row['create_time']),'updatedAt'=>$this->stamp($row['update']),'score'=>(float)$row['score'],
                'status'=>(int)$row['status'],'statusName'=>[1=>'待确认',2=>'已拒绝',3=>'已确认',4=>'自动确认'][(int)$row['status']]??'未知状态',
                'mark'=>(string)$row['mark'],'rejectReason'=>(string)$row['rej_mark'],'hasImage'=>!empty($row['img']),'canDelete'=>$this->canDelete($row)];
        }return $list;
    }
    private function row($id)
    {
        $row=Db::name('kpi_record')->where('status','>',0)->field(self::FIELDS)->find($this->integer($id));
        if(!$row)$this->fail('绩效记录不存在或已删除',404);return $row;
    }
    public function options()
    {
        $departments=Db::name('admin_org')->column('title','id');
        $clauses=Db::name('kpi_list')->where('status',1)->field('id,dpt,job,item,content,scores')->order('dpt,job,item,id')->select()->toArray();
        foreach($clauses as &$clause){$clause['id']=(int)$clause['id'];$clause['departmentName']=$departments[$clause['dpt']]??'通用条款';}unset($clause);
        return $this->result(['today'=>(new \DateTimeImmutable('now',new \DateTimeZone('Asia/Shanghai')))->format('Y-m-d'),'projects'=>Db::name('team_base')->where('mid',1)->field('id,title')->select()->toArray(),
            'teams'=>Db::name('team_list')->field('id,team,project,status')->order('id')->select()->toArray(),'clauses'=>$clauses]);
    }
    public function index()
    {
        $q=$this->query();$total=(clone $q)->count();$page=$this->integer($this->request->get('page',1),1,1000000);$limit=$this->integer($this->request->get('limit',15),1,100);
        return $this->result(['list'=>$this->present($q->field(self::FIELDS)->order('id desc')->page($page,$limit)->select()->toArray()),'total'=>$total]);
    }
    public function detail() { return $this->result($this->present([$this->row($this->request->get('id'))])[0]); }
    public function summary()
    {
        if(!$this->integer($this->request->get('project',0),0)&&!$this->integer($this->request->get('team',0),0))$this->fail('请选择项目或班组');
        $rows=$this->query()->field('status,COUNT(*) AS num,SUM(CASE WHEN score>0 THEN score ELSE 0 END) AS added,SUM(CASE WHEN score<0 THEN -score ELSE 0 END) AS deducted')->group('status')->select()->toArray();
        $result=['added'=>0,'deducted'=>0,'net'=>0,'count'=>0,'statuses'=>[]];foreach($rows as $row){$result['added']+=(float)$row['added'];$result['deducted']+=(float)$row['deducted'];$result['count']+=(int)$row['num'];$result['statuses'][(int)$row['status']]=(int)$row['num'];}
        foreach(['added','deducted'] as $key)$result[$key]=round($result[$key],1);$result['net']=round($result['added']-$result['deducted'],1);return $this->result($result);
    }
    public function employee()
    {
        $eid=$this->request->get('eid');$this->integer($eid);
        $rows=Db::name('member_list')->where('eid',$eid)->field('eid,ename,tid,status')->order('status,id desc')->select()->toArray();
        if(!$rows)$this->fail('没有找到该工号',404);
        if(count(array_filter($rows,function($row){return (int)$row['status']===1;}))>1)$this->fail('工号存在重复在职档案，请先核对',409);
        return $this->result(['employeeNo'=>(string)$rows[0]['eid'],'employeeName'=>$rows[0]['ename'],'teamId'=>(int)$rows[0]['tid']]);
    }
    public function image()
    {
        $row=$this->row($this->request->get('id'));$path=str_replace('\\','/',(string)$row['img']);
        if(strpos($path,'/storage/')!==0||strpos($path,'..')!==false)$this->fail('图片路径无效',404);
        $root=realpath(Filesystem::getDiskConfig('public','root'));$file=$root?realpath($root.'/'.substr($path,9)):false;
        if(!$file||strpos($file,$root.DIRECTORY_SEPARATOR)!==0||!is_file($file))$this->fail('本地缺少图片文件，请同步storage图片目录',404);
        $info=@getimagesize($file);if(!$info||!in_array($info['mime'],['image/jpeg','image/png','image/webp'],true)||filesize($file)>10*1024*1024)$this->fail('图片格式或大小无效');
        return $this->result(['dataUrl'=>'data:'.$info['mime'].';base64,'.base64_encode(file_get_contents($file))]);
    }
    public function export()
    {
        $q=$this->query(true);$upper=$this->integer($this->request->get('upperId',(clone $q)->max('id')?:0),0);$q->where('id','<=',$upper);$total=(clone $q)->count();
        $cursor=$this->integer($this->request->get('cursor',0),0);if($cursor)$q->where('id','<',$cursor);
        $rows=$q->field(self::FIELDS)->order('id desc')->limit(201)->select()->toArray();$more=count($rows)>200;if($more)array_pop($rows);
        return $this->result(['list'=>$this->present($rows),'total'=>$total,'upperId'=>$upper,'nextCursor'=>$more?(int)end($rows)['id']:null]);
    }
    protected function notify($row,$event) { return (new V2PerformanceNotice())->sendPerformance($row,$event); }
    private function saveImage()
    {
        $file=$this->request->file('image');if(!$file)return null;
        if(!$file instanceof \think\file\UploadedFile||!$file->isValid()||$file->getSize()>5*1024*1024)$this->fail('请选择不超过5MB的图片');
        $types=['image/jpeg'=>'jpg','image/png'=>'png','image/webp'=>'webp'];$mime=$file->getMime();
        if(!isset($types[$mime])||!@getimagesize($file->getPathname()))$this->fail('请选择JPG、PNG或WebP图片');
        $path=Filesystem::disk('public')->putFileAs('wx_upload/'.date('Ymd').'/performance',$file,bin2hex(random_bytes(12)).'.'.$types[$mime]);
        if(!$path)$this->fail('图片保存失败',503);return '/storage/'.str_replace('\\','/',$path);
    }
    public function create()
    {
        $requestId=$this->request->post('requestId');if(!is_string($requestId)||!preg_match('/^[a-zA-Z0-9_-]{16,80}$/D',$requestId))$this->fail('提交标识无效，请重新打开新增窗口');
        $key='admin-v2:performance:'.$this->actor['id'].':'.$requestId;$lock='perf:'.substr(hash('sha256',$key),0,48);
        $acquired=Db::query('SELECT GET_LOCK(?,5) AS acquired',[$lock]);if((int)($acquired[0]['acquired']??0)!==1)$this->fail('正在提交，请稍后重试',409);
        try{
            $saved=Cache::get($key);if($saved)return $this->result($saved);
            $day=$this->day($this->request->post('date'));$today=new \DateTimeImmutable('today',new \DateTimeZone('Asia/Shanghai'));
            if($day<$today->modify('-2 days')||$day>$today)$this->fail('绩效日期仅可选择当天及前2天');
            $eid=$this->request->post('eid');$this->integer($eid);if(!Db::name('member_list')->where('eid',$eid)->find())$this->fail('员工工号不存在');
            $tid=$this->integer($this->request->post('teamId'));if(!Db::name('team_list')->where(['id'=>$tid,'status'=>1])->find())$this->fail('班组已失效，请重新选择');
            $item=$this->integer($this->request->post('itemId'));if(!Db::name('kpi_list')->where(['id'=>$item,'status'=>1])->find())$this->fail('条款已失效，请重新选择');
            $score=$this->request->post('score');if(!is_scalar($score)||!preg_match('/^[+-]?[0-9]+(?:\.[0-9])?$/D',(string)$score)||abs((float)$score)>9999.9)$this->fail('分数须为-9999.9至9999.9，最多1位小数');
            $mark=$this->request->post('mark','');if(!is_string($mark)||mb_strlen(trim($mark))>255)$this->fail('备注最多255字');
            $image=$this->saveImage();$row=['eid'=>$eid,'tid'=>$tid,'item'=>$item,'aid'=>$this->actor['id'],'score'=>(float)$score,'mark'=>trim($mark),'img'=>$image,'status'=>1,'create_time'=>time(),'update'=>time(),'kpi_date'=>$day->getTimestamp(),'rej_mark'=>''];
            try{$row['id']=(int)Db::name('kpi_record')->insertGetId($row);}catch(\Throwable $error){if($image)Filesystem::disk('public')->delete(substr($image,9));throw $error;}
            $saved=['id'=>$row['id'],'notification'=>['status'=>'unknown','message'=>'绩效已保存，通知结果待核对，请勿重复新增']];
            Cache::set($key,$saved,7*86400);
            $saved['notification']=$this->notify($row,'create');Cache::set($key,$saved,7*86400);
            return $this->result($saved);
        }finally{Db::query('SELECT RELEASE_LOCK(?)',[$lock]);}
    }
    public function remove()
    {
        $row=$this->row($this->request->post('id'));if(!$this->canDelete($row))$this->fail('仅可删除本人待确认记录，或由部门6删除已处理记录',403);
        $changed=Db::name('kpi_record')->where(['id'=>$row['id'],'status'=>$row['status'],'aid'=>$row['aid']])->update(['status'=>0,'update'=>time()]);
        if(!$changed)$this->fail('绩效状态已变化，请刷新后重试',409);
        return $this->result(['notification'=>$this->notify($row,'remove')]);
    }
}
