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

class V2Inventory extends V2Base
{
    private function run($work) {try{return json(['code'=>0,'data'=>$work()]);}catch(\InvalidArgumentException $e){$this->fail($e->getMessage());}}
    private function binding() {return hash('sha256',$this->requestToken());}
    private function admin() {return (int)($this->actor['rightid']??0)>98;}
    public function options() {return $this->run(function(){
        list($pdo,$prefix)=V2InventoryData::connection();$policy=V2InventoryData::policy();
        return ['projects'=>Db::name('team_base')->where('mid',1)->field('id,title')->order('id')->select()->toArray(),'lines'=>Db::name('team_base')->where('mid',2)->field('id,title,mark')->order('id')->select()->toArray(),'counts'=>V2InventoryData::counts($pdo,V2InventoryData::live($prefix)),'canDeleteAll'=>$this->admin(),'locked'=>$policy['locked'],'policy'=>$policy];
    });}
    public function index() {return $this->run(function(){
        $kind=V2InventoryData::kind($this->request->get('kind','address'));$keyword=$this->request->get('keyword','');$project=$this->request->get('project','');
        if(!is_string($keyword)||mb_strlen($keyword)>100||!is_scalar($project)||($project!==''&&!ctype_digit((string)$project)))throw new \InvalidArgumentException('筛选参数无效');
        $page=max(1,min(100000,(int)$this->request->get('page',1)));$limit=max(1,min(100,(int)$this->request->get('limit',30)));$q=Db::name(V2InventoryData::TABLES[$kind]);
        if($kind!=='base'&&$project!=='')$q->where('project',(int)$project);
        if(trim($keyword)!==''){$term='%'.addcslashes(trim($keyword),'%_\\').'%';$q->where(function($query)use($kind,$term,$keyword){$query->whereLike($kind==='address'?'kanban|rack|location':($kind==='super'?'kanban|children':'kanban'),$term);if(ctype_digit($keyword))$query->whereOr('id',(int)$keyword);});}
        $total=(clone $q)->count();$rows=$q->order('id')->limit(($page-1)*$limit,$limit)->select()->toArray();foreach($rows as &$r)$r['version']=V2InventoryData::version($r);unset($r);return ['list'=>$rows,'total'=>$total];
    });}
    public function upload() {return $this->run(function(){
        @set_time_limit(180);$file=$this->request->file('file');if(!$file||strtolower($file->getOriginalExtension())!=='xlsx'||$file->getSize()>30*1024*1024)throw new \InvalidArgumentException('请选择不超过30MB的xlsx文件');
        return V2InventoryImport::prepare($file->getPathname(),$this->actor['id'],$this->binding());
    });}
    public function commit() {return $this->run(function(){return V2InventoryImport::commit($this->request->post('ticket'),$this->binding());});}
    public function discard() {return $this->run(function(){return V2InventoryImport::discard($this->request->post('ticket'),$this->binding());});}
    public function update() {return $this->run(function(){
        $d=$this->request->post();$kind=V2InventoryData::kind($d['kind']??null);$row=V2InventoryData::normalize($kind,$d['row']??[],$this->actor['id']);
        return V2InventoryData::lock(function()use($d,$kind,$row){
            V2InventoryData::guard();$q=Db::name(V2InventoryData::TABLES[$kind]);$old=(clone $q)->where('id',$row['id'])->find();
            if(!$old||!is_string($d['version']??null)||!hash_equals(V2InventoryData::version($old),$d['version']))throw new \InvalidArgumentException('记录已变化或被全量替换，请刷新后重新编辑');
            foreach($kind==='address'?['project','line']:($kind==='super'?['project','kanban']:['kanban']) as $f)if((string)$row[$f]!== (string)$old[$f])throw new \InvalidArgumentException('项目、产线及物料主键请通过内网全量同步调整');
            if($kind==='super'){
                if(!Db::name('onsite_inventory_base')->where('kanban',$row['children'])->count()&&!Db::name('onsite_inventory_super')->where(['project'=>$row['project'],'kanban'=>$row['children']])->count())throw new \InvalidArgumentException('子物料不在基础清单中且没有同项目下级BOM');
                if($row['children']!==$old['children']){$graph=[];foreach(Db::name('onsite_inventory_super')->where('project',$row['project'])->select()->toArray() as $b){if((int)$b['id']===$row['id'])$b=$row;$graph[$b['project'].':'.$b['kanban']][]=$b;}V2InventoryRecord::expand($graph,$row['project'],$row['kanban']);}
                if($row['children']!==$old['children']&&(clone $q)->where(['project'=>$row['project'],'kanban'=>$row['kanban'],'children'=>$row['children']])->where('id','<>',$row['id'])->count())throw new \InvalidArgumentException('同一超级看板的子物料不能重复');
            }elseif($kind==='address'){
                $special=false;foreach(['super','compose','pull_line','copper','wire'] as $f)if((int)$row[$f]===1)$special=true;
                if((int)$row['super']===1&&!Db::name('onsite_inventory_super')->where(['project'=>$row['project'],'kanban'=>$row['kanban']])->count())throw new \InvalidArgumentException('未找到对应项目的超级看板BOM');
                if(!$special&&!Db::name('onsite_inventory_base')->where('kanban',$row['kanban'])->count())throw new \InvalidArgumentException('物料不存在');
                $dup=(clone $q)->where(['project'=>$row['project'],'line'=>$row['line'],'rack'=>$row['rack'],'location'=>$row['location']])->where('id','<>',$row['id']);if(strpos($row['location'],'批量')!==false)$dup->where('kanban',$row['kanban']);if(($row['rack']!==$old['rack']||$row['location']!==$old['location']||$row['kanban']!==$old['kanban'])&&$dup->count())throw new \InvalidArgumentException('地址位置重复');
            }
            V2InventoryData::guard();(clone $q)->where('id',$row['id'])->update($row);return ['saved'=>true];
        });
    });}
    public function deletePreview() {return $this->run(function(){
        $project=$this->request->post('project');$all=$this->request->post('all',false);if(!is_bool($all))throw new \InvalidArgumentException('删除范围无效');if($all&&!$this->admin())$this->fail('仅管理员可删除全部',403);
        if(!$all&&(!is_scalar($project)||!ctype_digit((string)$project)||(int)$project<1||!Db::name('team_base')->where(['mid'=>1,'id'=>(int)$project])->count()))throw new \InvalidArgumentException('请选择有效项目');
        return V2InventoryData::lock(function($pdo,$prefix)use($project,$all){V2InventoryData::guard();$live=V2InventoryData::live($prefix);$counts=[];foreach($live as $k=>$t)$counts[$k]=($k==='base'&&!$all)?0:(int)$pdo->query('SELECT COUNT(*) FROM '.V2InventoryData::quote($t).($all?'':' WHERE project='.(int)$project))->fetchColumn();return ['counts'=>$counts,'version'=>V2InventoryData::fingerprint($pdo,$live)];});
    });}
    public function remove() {return $this->run(function(){
        $d=$this->request->post();$all=$d['all']??false;if(!is_bool($all))throw new \InvalidArgumentException('删除范围无效');if($all&&!$this->admin())$this->fail('仅管理员可删除全部',403);$project=$d['project']??null;
        if(!$all&&(!is_scalar($project)||!ctype_digit((string)$project)||(int)$project<1||!Db::name('team_base')->where(['mid'=>1,'id'=>(int)$project])->count()))throw new \InvalidArgumentException('请选择有效项目');
        return V2InventoryData::lock(function($pdo,$prefix)use($d,$all,$project){
            V2InventoryData::guard();$live=V2InventoryData::live($prefix);if(!is_string($d['version']??null)||!hash_equals(V2InventoryData::fingerprint($pdo,$live),$d['version']))throw new \InvalidArgumentException('数据已变化，请重新检查删除范围');$stages=[];
            try{foreach($live as $k=>$t){$stages[$k]=$prefix.'v2inv_delete_'.bin2hex(random_bytes(4)).'_'.$k;$pdo->exec('CREATE TABLE '.V2InventoryData::quote($stages[$k]).' LIKE '.V2InventoryData::quote($t));if(!$all)$pdo->exec('INSERT INTO '.V2InventoryData::quote($stages[$k]).' SELECT * FROM '.V2InventoryData::quote($t).($k==='base'?'':' WHERE project<>'.(int)$project.' OR project IS NULL'));}V2InventoryData::guard();V2InventoryImport::swap($pdo,$prefix,$stages);return ['removed'=>true];}
            finally{foreach($stages as $t)$pdo->exec('DROP TABLE IF EXISTS '.V2InventoryData::quote($t));}
        });
    });}
}
