<?php
namespace app\mobile\controller;

use think\facade\Db;
use think\facade\Filesystem;
use think\facade\Log;

/** Daily Issue：工程师提报，配置主管编辑并发布；发布后工程师只读。 */
class OmsDailyIssue extends OmsBase
{
    public function messageItems($account,$since,\app\mobile\service\MessageInbox $inbox) {
        if(!$inbox->allowsSource($this))$this->fail('接口不存在',404);
        $admin=OmsVisitor::adminFor($account);if(!$admin||!\app\mobile\service\MobileFeatureAccess::allows('wirApply',\app\mobile\service\MobileFeatureAccess::context($account)))return [];$supervisor=$this->isSupervisor($admin);$items=[];
        $rows=Db::name('quality_wir_list')->where(function($q)use($admin,$supervisor,$since){$q->where(function($r)use($admin,$since){$r->where('owner',$admin['id'])->where('status',2)->where('published_at','>=',$since);});if($supervisor)$q->whereOr('status',1)->whereOr(function($r)use($admin,$since){$r->where('publisher_id',$admin['id'])->where('status',2)->where('published_at','>=',$since);});})->select()->toArray();
        foreach($rows as $row){$p=$this->present($row,$admin);$fields=['提报人'=>$p['ownerName'],'项目'=>$p['project'],'生产日期'=>$p['issueDate'],'问题描述'=>$p['desc']];$route='/pages/daily-issue/detail?id='.$row['id'];
            if($p['canPublish'])$items[]=\app\mobile\service\MessageInbox::item('daily-issue',$row['id'],'publish','Daily Issue待审核发布','待发布',$fields,$route,'pending',$row['update']);
            if((int)$row['status']===2 && (int)$row['publisher_id']===(int)$admin['id'])$items[]=\app\mobile\service\MessageInbox::item('daily-issue',$row['id'],'publish','Daily Issue发布已处理','已发布',$fields,$route,'done',$row['published_at']);
            if((int)$row['status']===2 && (int)$row['owner']===(int)$admin['id'])$items[]=\app\mobile\service\MessageInbox::item('daily-issue',$row['id'],'result','Daily Issue已发布','已发布',$fields,$route,'notice',$row['published_at']);
        }return $items;
    }
    private function result($data=null) { return json(['code'=>0,'message'=>'成功','data'=>$data]); }
    private function supervisorId() { return (int)$this->app->config->get('daily_issue.supervisor_id',0); }
    private function isSupervisor($admin) { return (int)$admin['id']===$this->supervisorId(); }
    private function supervisor() {
        $supervisor=Db::name('admin_user')->where(['id'=>$this->supervisorId(),'status'=>1])->find();
        if (!$supervisor) $this->fail('负责主管未配置或已停用，请联系管理员',409);
        return $supervisor;
    }
    private function canEdit($row,$admin) { return $this->isSupervisor($admin) || ((int)$row['owner']===(int)$admin['id'] && (int)$row['status']===1); }
    private function admin() {
        $account = $this->current();
        $admin = (int)($account['admin_id'] ?? 0)>0 ? Db::name('admin_user')->where(['id'=>$account['admin_id'],'username'=>$account['username'],'status'=>1])->find() : null;
        if (!$admin) $this->fail('需要有效管理人员身份',403);
        if (!\app\mobile\service\MobileFeatureAccess::allows('wirApply',\app\mobile\service\MobileFeatureAccess::context($account))) $this->fail('当前账号无权使用售后问题反馈，请联系管理员核对菜单可见范围',403);
        return $admin;
    }
    private function integer($value, $label, $min=0) {
        if ((!is_string($value) && !is_int($value)) || !preg_match('/^(0|[1-9][0-9]{0,9})$/',(string)$value) || (float)$value>2147483647 || (int)$value<$min) $this->fail($label.'须为有效整数');
        return (int)$value;
    }
    private function text($value, $length, $label, $optional=false) {
        if (!is_string($value) || mb_strlen($value)>$length || (!$optional && trim($value)==='')) $this->fail('请正确填写'.$label.'，最多'.$length.'字');
        return trim($value);
    }
    private function dateValue($value, $label, $optional=false) {
        if ($optional && $value==='') return 0;
        if (!is_string($value)) $this->fail($label.'格式不正确');
        $date = \DateTimeImmutable::createFromFormat('!Y-m-d',$value,new \DateTimeZone('Asia/Shanghai'));
        if (!$date || $date->format('Y-m-d')!==$value || $value<'2000-01-01' || $value>'2037-12-31') $this->fail($label.'格式不正确');
        return $date->getTimestamp();
    }
    private function formatted($time, $format='Y-m-d') {
        return $time>0 ? (new \DateTimeImmutable('@'.$time))->setTimezone(new \DateTimeZone('Asia/Shanghai'))->format($format) : '';
    }
    private function form() {
        $data = [];
        foreach (['project'=>[50,'项目'], 'dpn'=>[50,'线束号'], 'station'=>[50,'工段'], 'desc'=>[2000,'问题描述']] as $field=>$rule)
            $data[$field] = $this->text($this->request->post($field),$rule[0],$rule[1]);
        foreach (['repair'=>'现场维修方法','reason'=>'初步原因','remark'=>'备注'] as $field=>$label)
            $data[$field] = $this->text($this->request->post($field,''),1000,$label,true);
        if (!in_array($data['project'],$this->projects(),true)) $this->fail('请选择有效项目，暂无选项时请等待项目列表更新');
        foreach (['checkQty'=>'排查数量','allNgQty'=>'NG数量'] as $field=>$label) {
            $value=$this->request->post($field);
            $data[$field]=$this->text(is_int($value)?(string)$value:$value,40,$label);
        }
        $quantity=$this->request->post('ngQty');
        $data['ngQty']=$quantity==='批量' ? '批量' : (string)$this->integer($quantity,'缺陷数量');
        foreach (['back'=>'缺陷件返回选项','duty'=>'职责类型'] as $field=>$label) {
            $data[$field] = $this->integer($this->request->post($field),$label,1);
            if (!in_array($data[$field],$field==='duty' ? [1,3,2] : [1,2],true)) $this->fail($label.'不正确');
        }
        $data['wirDate'] = $this->dateValue($this->request->post('wirDate'),'反馈日期');
        $data['issueDate'] = $this->dateValue($this->request->post('issueDate',''),'生产日期',true);
        return $data;
    }
    private function row($id, $admin) {
        $query=Db::name('quality_wir_list')->where('id',$this->integer($id,'记录编号',1))->whereIn('status',[1,2]);
        if (!$this->isSupervisor($admin)) $query->where('owner',$admin['id']);
        $row=$query->find();
        if (!$row) $this->fail('反馈不存在或已作废',404);
        return $row;
    }
    private function images($id) { return Db::name('img_list')->where(['mid'=>55,'mark'=>$id,'status'=>1])->order('id')->select()->toArray(); }
    private function present($row, $admin, $detail=false) {
        $result = array_intersect_key($row,array_flip(['id','project','dpn','station','desc','repair','reason','remark','checkQty','allNgQty','ngQty','back','duty','revision']));
        foreach (['id','back','duty','revision'] as $field) $result[$field]=(int)$result[$field];
        foreach (['checkQty','allNgQty','ngQty'] as $field) $result[$field]=(string)$result[$field];
        $result['wirDate']=$this->formatted($row['wirDate']); $result['issueDate']=$this->formatted($row['issueDate']);
        $result['createdAt']=$this->formatted($row['update'],'Y-m-d H:i'); $result['updatedAt']=$this->formatted($row['updated_at'],'Y-m-d H:i');
        $result['ownerName']=Db::name('admin_user')->where('id',$row['owner'])->value('truename') ?: '人员已变更'; $result['images']=[];
        $result['status']=(int)$row['status']; $result['statusName']=$result['status']===2?'已发布':'待发布';
        $result['publishedAt']=$this->formatted($row['published_at'],'Y-m-d H:i');
        $result['canEdit']=$this->canEdit($row,$admin);
        $result['canDelete']=(int)$row['owner']===(int)$admin['id'] && (int)$row['status']===1;
        $result['canPublish']=$this->isSupervisor($admin) && (int)$row['status']===1;
        if ($detail) foreach ($this->images($row['id']) as $image) {
            $path=str_replace('\\','/',(string)$image['img']);
            if (strpos($path,'/storage/')===0 && strpos($path,'..')===false) $result['images'][]=['id'=>(int)$image['id'],'url'=>'https://www.cqoms.com'.$path];
        }
        return $result;
    }
    private function projects() { return $this->app->config->get('daily_issue.projects',[]); }
    public function options() { return $this->guarded(function() {
        $this->requireMethod('GET'); $admin=$this->admin();
        return $this->result(['today'=>$this->formatted(time()),'projects'=>$this->projects(),'canReview'=>$this->isSupervisor($admin),'imageLimit'=>3,'imageMaxBytes'=>5*1024*1024]);
    }); }
    public function records() { return $this->guarded(function() {
        $this->requireMethod('GET'); $admin=$this->admin(); $page=$this->integer($this->request->get('page',1),'页码',1);
        $scope=$this->request->get('scope','mine');
        if (!in_array($scope,['mine','review'],true)) $this->fail('列表类型不正确');
        if ($scope==='review' && !$this->isSupervisor($admin)) $this->fail('仅负责主管可查看待发布记录',403);
        $query=Db::name('quality_wir_list')->whereIn('status',[1,2]);
        if ($scope==='mine') $query->where('owner',$admin['id']);
        $keyword=$this->text($this->request->get('keyword',''),50,'关键词',true);
        if ($keyword!=='') $query->whereLike('project|dpn','%'.addcslashes($keyword,'%_\\').'%');
        $start=$this->dateValue($this->request->get('startDate',''),'开始日期',true); $end=$this->dateValue($this->request->get('endDate',''),'结束日期',true);
        if ($start && $end && $start>$end) $this->fail('开始日期不能晚于结束日期');
        if ($start) $query->where('wirDate','>=',$start); if ($end) $query->where('wirDate','<=',$end);
        $total=(clone $query)->count();
        $items=array_map(function($row)use($admin){return $this->present($row,$admin);},$query->order($scope==='review'?'status,update desc,id desc':'update desc,id desc')->page($page,15)->select()->toArray());
        return $this->result(['items'=>$items,'total'=>$total,'page'=>$page]);
    }); }
    public function detail() { return $this->guarded(function() {
        $this->requireMethod('GET'); $admin=$this->admin(); return $this->result($this->present($this->row($this->request->get('id'),$admin),$admin,true));
    }); }
    protected function stagingDirectory() { return $this->app->getRuntimePath().'daily-issue-uploads/'; }
    public function upload() { return $this->guarded(function() {
        $this->requireMethod('POST'); $admin=$this->admin(); $file=$this->request->file('image');
        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图片');
        $dir=$this->stagingDirectory(); if (!is_dir($dir) && !mkdir($dir,0700,true) && !is_dir($dir)) $this->fail('图片暂存失败',503);
        foreach (glob($dir.'*') ?: [] as $old) if (preg_match('/^[a-f0-9]{48}\.(jpg|png|webp)$/',basename($old)) && is_file($old) && filemtime($old)<time()-86400) @unlink($old);
        $reference=bin2hex(random_bytes(24)); $name=$reference.'.'.$types[$mime]; $file->move($dir,$name);
        try { $this->cache()->set($this->cacheKey('daily:upload:'.$reference),['owner'=>(int)$admin['id'],'name'=>$name],86400); }
        catch (\Throwable $error) { @unlink($dir.$name); throw $error; }
        return $this->result(['reference'=>$reference]);
    }); }
    private function locked($admin, $action) {
        $key='daily:'.substr(hash('sha256',$this->cacheKey((string)$admin['id'])),0,48);
        $lock=Db::query('SELECT GET_LOCK(?,5) acquired',[$key]); if ((int)$lock[0]['acquired']!==1) $this->fail('正在保存，请稍后重试',409);
        try { return $action(); } finally { Db::query('SELECT RELEASE_LOCK(?)',[$key]); }
    }
    private function save($editing) {
        return $this->guarded(function()use($editing) {
            $this->requireMethod('POST'); $admin=$this->admin();
            $lockOwner=$editing ? $this->row($this->request->post('id'),$admin)['owner'] : $admin['id'];
            return $this->locked(['id'=>$lockOwner],function()use($admin,$editing) {
                $this->admin(); $data=$this->form(); $old=$editing ? $this->row($this->request->post('id'),$admin) : null;
                if ($editing && !$this->canEdit($old,$admin)) $this->fail('已发布，工程师不可再修改',403);
                if (!$editing) $this->supervisor();
                $keep=$this->request->post('imageIds',[]); $refs=$this->request->post('references',[]);
                if (!is_array($keep) || !is_array($refs) || count($keep)+count($refs)>3 || (!$editing && $keep)) $this->fail('最多上传3张图片');
                $keep=array_map(function($id){return $this->integer($id,'图片编号',1);},array_values($keep));
                if (count(array_unique($keep))!==count($keep) || count(array_unique($refs,SORT_REGULAR))!==count($refs)) $this->fail('图片重复');
                foreach ($refs as $ref) if (!is_string($ref) || !preg_match('/^[a-f0-9]{48}$/',$ref)) $this->fail('图片引用不正确');
                $revision=$editing ? $this->integer($this->request->post('revision'),'记录版本',1) : 0;
                $submission=$editing ? '' : $this->text($this->request->post('submissionId'),64,'提交标识');
                $hash=hash('sha256',json_encode([$data,$refs]));
                if (!$editing) {
                    $existing=Db::name('quality_wir_list')->where(['owner'=>$admin['id'],'submission_id'=>$submission])->find();
                    if ($existing) {
                        if ($existing['submission_hash']!==$hash || (int)$existing['status']===0) $this->fail('提交信息已变化，请重新提报',409);
                        return $this->result(['id'=>(int)$existing['id'],'notificationSent'=>!empty($existing['notified_at'])]);
                    }
                } elseif ((int)$old['revision']!==$revision) $this->fail('记录已修改，请刷新后重试',409);
                $oldImages=$editing ? $this->images($old['id']) : [];
                if (array_diff($keep,array_map('intval',array_column($oldImages,'id')))) $this->fail('图片不属于当前记录');
                $uploads=[];
                foreach ($refs as $ref) {
                    $upload=$this->cache()->get($this->cacheKey('daily:upload:'.$ref));
                    if (!$upload || $upload['owner']!==(int)$admin['id'] || !is_file($this->stagingDirectory().$upload['name'])) $this->fail('图片已过期或不属于本人，请重新选择');
                    $uploads[$ref]=$upload;
                }
                $paths=[]; $imageIds=[]; $id=$old ? (int)$old['id'] : 0;
                try {
                    foreach ($uploads as $ref=>$upload) {
                        $path=Filesystem::disk('public')->putFileAs('wx_upload/'.date('Ymd').'/daily_issue',new \think\File($this->stagingDirectory().$upload['name']),$upload['name']);
                        if (!$path) $this->fail('图片保存失败',503); $paths[]=$path;
                    }
                    if (!$editing) $id=(int)Db::name('quality_wir_list')->insertGetId($data+['owner'=>$admin['id'],'status'=>1,'update'=>time(),'updated_at'=>0,'revision'=>1,'submission_id'=>$submission,'submission_hash'=>$hash]);
                    foreach ($paths as $path) $imageIds[]=(int)Db::name('img_list')->insertGetId(['mid'=>55,'mark'=>$id,'img'=>'/storage/'.str_replace('\\','/',$path),'status'=>1]);
                    if ($editing) {
                        $changed=Db::name('quality_wir_list')->where(['id'=>$id,'owner'=>$old['owner'],'status'=>$old['status'],'revision'=>$revision])->update($data+['updated_at'=>time(),'revision'=>$revision+1]);
                        if (!$changed) $this->fail('记录已变化，请刷新后重试',409);
                        $removed=array_diff(array_column($oldImages,'id'),$keep);
                        if ($removed) Db::name('img_list')->where(['mid'=>55,'mark'=>$id])->whereIn('id',$removed)->update(['status'=>0]);
                    }
                } catch (\Throwable $error) {
                    // MyISAM不支持回滚：撤销本次写入，暂存图片保留供重试。
                    if ($imageIds) Db::name('img_list')->where(['mid'=>55,'mark'=>$id])->whereIn('id',$imageIds)->delete();
                    if ($editing) {
                        Db::name('quality_wir_list')->where('id',$id)->update($old);
                        if ($oldImages) Db::name('img_list')->where(['mid'=>55,'mark'=>$id])->whereIn('id',array_column($oldImages,'id'))->update(['status'=>1]);
                    } elseif ($id) Db::name('quality_wir_list')->where(['id'=>$id,'owner'=>$admin['id']])->delete();
                    foreach ($paths as $path) Filesystem::disk('public')->delete($path);
                    throw $error;
                }
                foreach ($uploads as $ref=>$upload) {
                    @unlink($this->stagingDirectory().$upload['name']);
                    try { $this->cache()->delete($this->cacheKey('daily:upload:'.$ref)); } catch (\Throwable $error) { Log::warning('daily issue upload cleanup unavailable'); }
                }
                $notificationSent=true;
                if (!$editing) {
                    $notificationSent=$this->notify($data+['id'=>$id,'owner'=>$admin['id']]);
                    if ($notificationSent) try { Db::name('quality_wir_list')->where('id',$id)->update(['notified_at'=>time()]); } catch (\Throwable $error) { Log::warning('daily issue notification state unavailable'); }
                }
                return $this->result(['id'=>$id,'notificationSent'=>$notificationSent]);
            });
        });
    }
    public function create() { return $this->save(false); }
    public function update() { return $this->save(true); }
    public function publish() { return $this->guarded(function() {
        $this->requireMethod('POST'); $admin=$this->admin();
        if (!$this->isSupervisor($admin)) $this->fail('仅负责主管可发布',403);
        $row=$this->row($this->request->post('id'),$admin);
        return $this->locked(['id'=>$row['owner']],function()use($admin) {
            $this->admin(); $row=$this->row($this->request->post('id'),$admin);
            $revision=$this->integer($this->request->post('revision'),'记录版本',1);
            if ((int)$row['status']!==1 || (int)$row['revision']!==$revision) $this->fail('记录已发布或已修改，请刷新后重试',409);
            if (!Db::name('quality_wir_list')->where(['id'=>$row['id'],'status'=>1,'revision'=>$revision])->update(['status'=>2,'published_at'=>time(),'publisher_id'=>$admin['id'],'revision'=>$revision+1])) $this->fail('记录已变化，请刷新后重试',409);
            return $this->result();
        });
    }); }
    public function remove() { return $this->guarded(function() {
        $this->requireMethod('POST'); $admin=$this->admin();
        return $this->locked($admin,function()use($admin) {
            $this->admin(); $row=$this->row($this->request->post('id'),$admin);
            if ((int)$row['owner']!==(int)$admin['id'] || (int)$row['status']!==1) $this->fail('仅本人可作废未发布记录',403);
            $revision=$this->integer($this->request->post('revision'),'记录版本',1);
            if ((int)$row['revision']!==$revision) $this->fail('记录已修改，请刷新后重试',409);
            if (!Db::name('quality_wir_list')->where(['id'=>$row['id'],'owner'=>$admin['id'],'status'=>1,'revision'=>$revision])->update(['status'=>0,'updated_at'=>time(),'revision'=>$revision+1])) $this->fail('记录已变化，请刷新后重试',409);
            return $this->result();
        });
    }); }
    protected function notify($row) {
        try {
            $service=$this->templateService();
            return $service->send('workflow_pending',$service->admins([$this->supervisorId()]),[
                'name'=>'Daily Issue审核发布','initiator'=>$service->adminName($row['owner']),'time'=>$service::time(time()),
                'reason'=>$row['desc'],'step'=>'主管审核发布',
            ],'pages/daily-issue/detail?id='.$row['id']);
        } catch (\Throwable $error) { Log::warning('daily issue notification unavailable: '.$error->getMessage()); return false; }
    }
}
