<?php
namespace app\mobile\controller;

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

/** 售后返修台账：登录人为维修人员，协助人员选填且仅限有效质量部人员。 */
class OmsAfterSalesRepair extends OmsBase
{
    private function result($data=null) { return json(['code'=>0,'message'=>'成功','data'=>$data]); }
    private function admin() {
        $account=$this->current();
        $admin=Db::name('admin_user')->where(['id'=>(int)($account['admin_id'] ?? 0),'username'=>$account['username'],'status'=>1])->find();
        if (!$admin) $this->fail('需要有效管理人员身份',403);
        if (!\app\mobile\service\MobileFeatureAccess::allows('afterSalesRepair',\app\mobile\service\MobileFeatureAccess::context($account))) $this->fail('当前账号无权使用售后返修管理，请联系管理员核对菜单可见范围',403);
        return $admin;
    }
    private function integer($value,$label,$min=1) {
        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,$max,$label,$optional=false) {
        if (!is_string($value)||mb_strlen($value)>$max||(!$optional&&trim($value)==='')) $this->fail('请正确填写'.$label.'，最多'.$max.'字');
        return trim($value);
    }
    private function projects() { return $this->app->config->get('daily_issue.projects',[]); }
    private function areas() { return ['内饰区','底盘区','完成线/终线区','返修调试区','其他区域']; }
    protected function now() { return (new \DateTimeImmutable('now',new \DateTimeZone('Asia/Shanghai')))->format('Y-m-d H:i:s'); }
    private function dateValue($value,$time=false) {
        $format=$time?'Y-m-d H:i':'Y-m-d';
        if (!is_string($value)) $this->fail('返修时间格式不正确');
        $date=\DateTimeImmutable::createFromFormat('!'.$format,$value,new \DateTimeZone('Asia/Shanghai'));
        if (!$date||$date->format($format)!==$value||substr($value,0,10)<'2000-01-01'||substr($value,0,10)>'2099-12-31') $this->fail('返修时间格式不正确');
        return $date->format($time?'Y-m-d H:i:s':'Y-m-d');
    }
    private function form() {
        $data=[];
        foreach (['traceNo'=>['trace_no',100,'车架号/其他追溯号'],'project'=>['project',50,'项目'],'harnessName'=>['harness_name',100,'线束名称'],
            'problemDescription'=>['problem_description',2000,'返修问题描述'],'repairMethod'=>['repair_method',2000,'返修方法'],'repairArea'=>['repair_area',30,'返修区域']] as $key=>$rule)
            $data[$rule[0]]=$this->text($this->request->post($key),$rule[1],$rule[2]);
        if (!in_array($data['project'],$this->projects(),true)) $this->fail('请选择有效项目');
        if (!in_array($data['repair_area'],$this->areas(),true)) $this->fail('请选择有效返修区域');
        $data['repair_at']=$this->dateValue($this->request->post('repairAt'),true);
        $data['remark']=$this->text($this->request->post('remark',''),1000,'备注',true);
        return $data;
    }
    private function row($id,$admin) {
        $row=Db::name('after_sales_repair')->where(['id'=>$this->integer($id,'记录编号'),'creator_id'=>$admin['id']])->whereNull('deleted_at')->find();
        if (!$row) $this->fail('记录不存在或已删除',404);
        return $row;
    }
    private function images($id) { return Db::name('after_sales_repair_image')->where('repair_id',$id)->order('image_type,id')->select()->toArray(); }
    private function present($row,$detail=false) {
        $result=['id'=>(int)$row['id'],'recordNo'=>$row['record_no'],'repairAt'=>substr($row['repair_at'],0,16),'traceNo'=>$row['trace_no'],
            'project'=>$row['project'],'harnessName'=>$row['harness_name'],'problemDescription'=>$row['problem_description'],'repairMethod'=>$row['repair_method'],
            'repairArea'=>$row['repair_area'],'remark'=>$row['remark'],'revision'=>(int)$row['revision'],'createdAt'=>$row['created_at'],'updatedAt'=>$row['updated_at'],
            'creatorId'=>(int)$row['creator_id'],'creatorName'=>Db::name('admin_user')->where('id',$row['creator_id'])->value('truename') ?: '人员已变更','people'=>[],'images'=>[]];
        foreach (Db::name('after_sales_repair_person')->alias('p')->leftJoin('admin_user a','a.id=p.admin_id')->where('p.repair_id',$row['id'])->where('p.admin_id','<>',$row['creator_id'])->field('p.admin_id,a.truename')->select() as $person)
            $result['people'][]=['id'=>(int)$person['admin_id'],'name'=>$person['truename'] ?: '人员已变更'];
        if ($detail) foreach ($this->images($row['id']) as $image) $result['images'][]=['id'=>(int)$image['id'],'type'=>(int)$image['image_type'],'url'=>'https://www.cqoms.com'.$image['file_path']];
        return $result;
    }
    public function options() { return $this->guarded(function() {
        $this->requireMethod('GET'); $admin=$this->admin();
        $people=[];
        foreach (Db::name('admin_user')->where(['dpt'=>3,'status'=>1])->where('id','<>',$admin['id'])->field('id,truename')->order('truename,id')->select() as $person)
            $people[]=['id'=>(int)$person['id'],'name'=>$person['truename']];
        return $this->result(['now'=>substr($this->now(),0,16),'projects'=>$this->projects(),'areas'=>$this->areas(),'people'=>$people,
            'defaultPersonIds'=>[]]);
    }); }
    public function records() { return $this->guarded(function() {
        $this->requireMethod('GET'); $admin=$this->admin(); $page=$this->integer($this->request->get('page',1),'页码');
        $query=Db::name('after_sales_repair')->where('creator_id',$admin['id'])->whereNull('deleted_at');
        $keyword=$this->text($this->request->get('keyword',''),100,'关键词',true);
        if ($keyword!=='') $query->whereLike('record_no|trace_no|project|harness_name','%'.addcslashes($keyword,'%_\\').'%');
        $start=$this->request->get('startDate',''); $end=$this->request->get('endDate','');
        if ($start!=='') $start=$this->dateValue($start); if ($end!=='') $end=$this->dateValue($end);
        if ($start&&$end&&$start>$end) $this->fail('开始日期不能晚于结束日期');
        if ($start) $query->where('repair_at','>=',$start.' 00:00:00'); if ($end) $query->where('repair_at','<=',$end.' 23:59:59');
        $area=$this->text($this->request->get('repairArea',''),30,'返修区域',true);
        if ($area!=='') { if (!in_array($area,$this->areas(),true)) $this->fail('返修区域不正确'); $query->where('repair_area',$area); }
        $total=(clone $query)->count();
        $items=array_map(function($row){return $this->present($row);},$query->order('repair_at 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'); return $this->result($this->present($this->row($this->request->get('id'),$this->admin()),true)); }); }
    protected function stagingDirectory() { return $this->app->getRuntimePath().'after-sales-repair-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);
        $ref=bin2hex(random_bytes(24)); $name=$ref.'.'.$types[$mime]; $file->move($dir,$name);
        try { $this->cache()->set($this->cacheKey('repair:upload:'.$ref),['owner'=>(int)$admin['id'],'name'=>$name],86400); }
        catch (\Throwable $error) { @unlink($dir.$name); throw $error; }
        return $this->result(['reference'=>$ref]);
    }); }
    private function locked($action) {
        // 返修写入短时间串行化，保证年度编号和暂存图片只被消费一次。
        $key='repair:'.substr(hash('sha256',$this->cacheKey('write')),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();
        return $this->locked(function()use($editing,$admin) {
            $this->admin(); $data=$this->form(); $old=$editing?$this->row($this->request->post('id'),$admin):null;
            $revision=$editing?$this->integer($this->request->post('revision'),'版本号'):0;
            if ($old&&(int)$old['revision']!==$revision) $this->fail('记录已修改，请刷新后重试',409);
            $ids=$this->request->post('personIds',[]);
            if (!is_array($ids)||count($ids)>100) $this->fail('协助维修人员不正确');
            $ids=array_map(function($id){return $this->integer($id,'协助维修人员');},array_values($ids));
            if (count(array_unique($ids))!==count($ids)) $this->fail('协助维修人员重复');
            if (in_array((int)$admin['id'],$ids,true)) $this->fail('登录人已作为维修人员记录，无需重复添加');
            $people=$ids?Db::name('admin_user')->where(['dpt'=>3,'status'=>1])->whereIn('id',$ids)->column('truename','id'):[];
            if (count($people)!==count($ids)) $this->fail('协助维修人员须为有效质量部人员');
            $groups=$this->request->post('imageGroups',[]);
            if (!is_array($groups)||count($groups)!==2) $this->fail('图片分组不正确');
            $oldImages=$old?$this->images($old['id']):[]; $keep=[]; $uploads=[]; $normalized=[];
            foreach ([1,2] as $type) {
                $group=$groups[$type] ?? null; $kept=$group['imageIds'] ?? null; $refs=$group['references'] ?? null;
                if (!is_array($kept)||!is_array($refs)||count($kept)+count($refs)>3||(!$old&&$kept)) $this->fail('每组最多3张图片');
                $kept=array_map(function($id){return $this->integer($id,'图片编号');},array_values($kept)); $refs=array_values($refs);
                foreach ($kept as $id) {
                    $found=false; foreach ($oldImages as $image) if ((int)$image['id']===$id&&(int)$image['image_type']===$type) $found=true;
                    if (!$found||in_array($id,$keep,true)) $this->fail('图片不属于当前记录或分组'); $keep[]=$id;
                }
                foreach ($refs as $ref) {
                    if (!is_string($ref)||!preg_match('/^[a-f0-9]{48}$/',$ref)||isset($uploads[$ref])) $this->fail('图片引用重复或不正确');
                    $uploads[$ref]=['type'=>$type];
                }
                $normalized[$type]=['imageIds'=>$kept,'references'=>$refs];
            }
            $submission=$editing?'':$this->text($this->request->post('submissionId'),64,'提交标识');
            $hash=hash('sha256',json_encode([$data,$ids,$normalized]));
            if (!$editing) {
                $existing=Db::name('after_sales_repair')->where(['creator_id'=>$admin['id'],'submission_id'=>$submission])->find();
                if ($existing) { if ($existing['submission_hash']!==$hash||$existing['deleted_at']!==null) $this->fail('提交内容已变化，请重新保存',409); return $this->result(['id'=>(int)$existing['id'],'recordNo'=>$existing['record_no']]); }
            }
            foreach ($uploads as $ref=>&$upload) {
                $staged=$this->cache()->get($this->cacheKey('repair:upload:'.$ref));
                if (!$staged||$staged['owner']!==(int)$admin['id']||!is_file($this->stagingDirectory().$staged['name'])) $this->fail('图片已过期或不属于本人，请重新选择');
                $upload+=$staged;
            } unset($upload);
            $paths=[]; $now=$this->now(); $id=$old?(int)$old['id']:0; $number=$old?$old['record_no']:'';
            try {
                foreach ($uploads as $ref=>$upload) {
                    $path=Filesystem::disk('public')->putFileAs('wx_upload/'.str_replace('-','',substr($now,0,10)).'/after_sales_repair',new \think\File($this->stagingDirectory().$upload['name']),$upload['name']);
                    if (!$path) $this->fail('图片保存失败',503); $paths[$ref]=$path;
                }
                Db::transaction(function()use($editing,$old,$data,$ids,$normalized,$paths,$keep,$admin,$revision,$submission,$hash,$now,&$id,&$number) {
                    if ($editing) {
                        if (!Db::name('after_sales_repair')->where(['id'=>$id,'creator_id'=>$admin['id'],'revision'=>$revision])->whereNull('deleted_at')->update($data+['updated_at'=>$now,'revision'=>$revision+1])) $this->fail('记录已变化，请刷新后重试',409);
                        Db::name('after_sales_repair_person')->where('repair_id',$id)->delete();
                        $query=Db::name('after_sales_repair_image')->where('repair_id',$id); if ($keep) $query->whereNotIn('id',$keep); $query->delete();
                    } else {
                        $day=substr($now,0,10); $year=substr($day,0,4);
                        $sequence=(int)Db::name('after_sales_repair')->whereBetween('number_date',[$year.'-01-01',$year.'-12-31'])->max('sequence_no')+1;
                        if ($sequence>99999) $this->fail('本年度流水号已达99999条，请联系管理员',409);
                        $number=str_replace('-','',$day).str_pad((string)$sequence,5,'0',STR_PAD_LEFT);
                        $id=(int)Db::name('after_sales_repair')->insertGetId($data+['record_no'=>$number,'number_date'=>$day,'sequence_no'=>$sequence,'creator_id'=>$admin['id'],'revision'=>1,'submission_id'=>$submission,'submission_hash'=>$hash,'created_at'=>$now,'updated_at'=>$now]);
                    }
                    foreach ($ids as $person) Db::name('after_sales_repair_person')->insert(['repair_id'=>$id,'admin_id'=>$person]);
                    foreach ($normalized as $type=>$group) {
                        foreach ($group['references'] as $ref) Db::name('after_sales_repair_image')->insert(['repair_id'=>$id,'image_type'=>$type,'file_path'=>'/storage/'.str_replace('\\','/',$paths[$ref])]);
                    }
                });
            } catch (\Throwable $error) { 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('repair:upload:'.$ref)); } catch (\Throwable $error) { Log::warning('repair upload cleanup unavailable'); }
            }
            return $this->result(['id'=>$id,'recordNo'=>$number]);
        });
    }); }
    public function create() { return $this->save(false); }
    public function update() { return $this->save(true); }
    public function remove() { return $this->guarded(function() {
        $this->requireMethod('POST'); $admin=$this->admin();
        return $this->locked(function()use($admin) {
            $this->admin(); $row=$this->row($this->request->post('id'),$admin); $revision=$this->integer($this->request->post('revision'),'版本号');
            if (!Db::name('after_sales_repair')->where(['id'=>$row['id'],'revision'=>$revision])->whereNull('deleted_at')->update(['deleted_at'=>$this->now(),'updated_at'=>$this->now(),'revision'=>$revision+1])) $this->fail('记录已修改，请刷新后重试',409);
            return $this->result();
        });
    }); }
}
