<?php
namespace app\mobile\service;

use think\facade\Db;

/** 外网维护的方案设备归属，不参与内网基础表替换。 */
class PositionDevice
{
    public static function fail($message,$status=409) {
        throw new \think\exception\HttpResponseException(json(['code'=>1,'message'=>$message,'msg'=>$message,'data'=>null],$status));
    }
    public static function canManage($admin,$team) {
        return $admin && ((int)$team['aid']===(int)$admin['id'] || (int)($admin['rightid']??0)>98 || ((int)$admin['dpt']===2 && in_array((int)$admin['job'],[18,19],true)) || ((int)$admin['dpt']===5 && in_array((int)$admin['job'],[43,48],true)));
    }
    public static function teams($admin) {
        $names=Db::name('team_base')->column('title','id');
        $teams=array_values(array_filter(Db::name('team_list')->where('status',1)->where('type','<',4)->order('project,line,team,id')->select()->toArray(),function($t)use($admin){return self::canManage($admin,$t);}));
        foreach($teams as &$t){$t['projectName']=$names[$t['project']]??'';$t['lineName']=$names[$t['line']]??'';}unset($t);return $teams;
    }
    public static function team($id,$admin=null) {
        $t=Db::name('team_list')->find((int)$id);
        if(!$t || (int)$t['status']!==1 || (int)$t['type']>=4)self::fail('班组已停用或不存在，请刷新',422);
        if($admin!==null && !self::canManage($admin,$t))self::fail('仅班组班长或授权管理人员可管理岗位设备',403);
        return $t;
    }
    private static function hash($value) {return hash('sha256',json_encode($value,JSON_UNESCAPED_UNICODE));}
    public static function locked($work) {
        return CheckSyncGuard::run(false,function()use($work){
            $c=Db::connect()->getConfig();
            // 与人员导入使用同一把锁，保证确认期间班组方案和员工岗位不会被同步改写。
            $keys=['employee:'.substr(hash('sha256',$c['database'].'|'.$c['prefix']),0,48),'position-device:'.substr(hash('sha256',$c['database'].'|'.$c['prefix']),0,40)];$held=[];
            try {
                foreach($keys as $key){$r=Db::query('SELECT GET_LOCK(?,5) acquired',[$key]);if((int)$r[0]['acquired']!==1)self::fail('人员或岗位设备资料正在更新，请稍后重试');$held[]=$key;}
                return $work();
            }finally{foreach(array_reverse($held) as $key)Db::query('SELECT RELEASE_LOCK(?)',[$key]);}
        });
    }
    public static function bindings($planId) {
        $rows=Db::name('me_position_device')->where('plan_id',(int)$planId)->order('m_id')->select()->toArray();
        $names=Db::name('team_plan_position')->whereIn('id',array_column($rows,'poid') ?: [-1])->column('position','id');
        foreach($rows as &$r){$r['positionName']=$names[$r['poid']]??'岗位已不存在';$r['poid']=(int)$r['poid'];$r['m_id']=(int)$r['m_id'];}unset($r);
        return array_column($rows,null,'m_id');
    }
    public static function data($team) {
        $plan=Db::name('team_plan_capacity')->find((int)$team['plan_id']);
        if(!$plan)self::fail('当前班组未配置有效产能方案，请先同步或配置方案',422);
        $positions=Db::name('team_plan_position')->where(['plan_id'=>$plan['id'],'status'=>1])->field('id,position')->order('order,id')->select()->toArray();
        $devices=Db::name('me_machine_list')->where(['line'=>$team['line'],'status'=>1,'is_check'=>1])->field('id,m_name,fixed_number,type')->order('type,id')->select()->toArray();
        $bindings=self::bindings($plan['id']);$types=Db::name('me_base')->column('title','id');
        foreach($devices as &$d){$d['id']=(int)$d['id'];$d['type']=(int)$d['type'];$d['typeName']=$types[$d['type']]??'未分类';$d['poid']=$bindings[$d['id']]['poid']??0;$d['positionName']=$bindings[$d['id']]['positionName']??'';}unset($d);
        $shared=Db::name('team_list')->where(['plan_id'=>$plan['id'],'status'=>1])->order('id')->column('team');
        return ['teamId'=>(int)$team['id'],'planId'=>(int)$plan['id'],'planName'=>$plan['planname'],'positions'=>$positions,'devices'=>$devices,'sharedTeams'=>$shared,'version'=>self::hash([$team,$plan,$positions,$devices,$bindings])];
    }
    private static function change($planId,$deviceId,$old,$new,$actor,$source,$teamId) {
        if($old===$new)return;
        if($new)Db::name('me_position_device')->duplicate(['poid'=>$new,'updated_at'=>time()])->insert(['plan_id'=>$planId,'m_id'=>$deviceId,'poid'=>$new,'updated_at'=>time()]);
        else Db::name('me_position_device')->where(['plan_id'=>$planId,'m_id'=>$deviceId])->delete();
        $names=Db::name('team_plan_position')->whereIn('id',[$old,$new])->column('position','id');
        Db::name('me_position_device_log')->insert(['plan_id'=>$planId,'m_id'=>$deviceId,'old_poid'=>$old,'new_poid'=>$new,'old_name'=>$names[$old]??($old?'已不存在岗位#'.$old:''),'new_name'=>$names[$new]??'','actor'=>(string)$actor,'source'=>$source,'tid'=>$teamId,'created_at'=>time()]);
    }
    public static function save($teamId,$admin,$input,$source) {
        return self::locked(function()use($teamId,$admin,$input,$source){
            $team=self::team($teamId,$admin);$data=self::data($team);$poid=(int)($input['positionId']??0);$ids=$input['deviceIds']??null;
            if(!is_array($ids) || count($ids)>10000 || count($ids)!==count(array_unique($ids,SORT_REGULAR)))self::fail('设备选择无效或重复',422);
            foreach($ids as $id)if(!is_int($id)||$id<=0)self::fail('设备编号无效',422);
            if(!in_array($poid,array_map('intval',array_column($data['positions'],'id')),true))self::fail('岗位已停用或不属于当前方案',422);
            if(($input['version']??'')!==$data['version'])self::fail('方案、设备或关联已变化，请刷新后重新选择');
            $byId=array_column($data['devices'],null,'id');$conflicts=[];
            foreach($ids as $id){if(!isset($byId[$id]))self::fail('设备已停用或不属于当前班组产线');if($byId[$id]['poid'] && $byId[$id]['poid']!==$poid)$conflicts[]=$byId[$id]['m_name'].'（'.$byId[$id]['positionName'].'）';}
            if($conflicts && ($input['confirmTransfer']??false)!==true)self::fail('请先确认转移其他岗位设备：'.implode('、',$conflicts));
            return Db::transaction(function()use($ids,$poid,$data,$admin,$source,$teamId){
                foreach($data['devices'] as $d){$old=$d['poid'];$new=in_array($d['id'],$ids,true)?$poid:($old===$poid?0:$old);self::change($data['planId'],$d['id'],$old,$new,$admin['truename'].' (#'.$admin['id'].')',$source,$teamId);}
                return self::data(self::team($teamId,$admin));
            });
        });
    }
    public static function employee($memberId,$team,$deviceId=0) {
        $member=Db::name('member_list')->where(['id'=>$memberId,'status'=>1])->find();$poid=(int)($member['poid']??0);
        $position=$poid?Db::name('team_plan_position')->where(['id'=>$poid,'plan_id'=>(int)$team['plan_id'],'status'=>1])->find():null;
        $plan=Db::name('team_plan_capacity')->find((int)$team['plan_id']);
        $valid=$member && (int)$member['tid']===(int)$team['id'] && $position && $plan;
        $binding=$deviceId?(self::bindings($team['plan_id'])[$deviceId]??null):null;
        return ['planId'=>(int)$team['plan_id'],'planName'=>$plan['planname']??'未配置方案','positionId'=>$binding['poid']??0,'positionName'=>$binding['positionName']??'','ownPositionId'=>$valid?$poid:0,'ownPositionName'=>$valid?$position['position']:'','canAssociate'=>(bool)$valid,'reason'=>$valid?'':'未配置有效岗位，或岗位不属于当前班组方案，请联系班长更新','version'=>self::hash([$member?$member['tid']:0,$poid,$team['plan_id'],$valid?$position:null,$binding])];
    }
    /** 在调用方的设备锁、关联锁及点检事务内调用，失败不写点检记录。 */
    public static function checkChoice($context,$input) {
        if((isset($input['associationVersion']) || $context['canAssociate']) && ($input['associationVersion']??'')!==$context['version'])self::fail('岗位或设备关联已变化，请刷新核对后再保存');
        if(!$context['canAssociate'])return;
        if($context['positionId'] && $context['positionId']!==$context['ownPositionId'] && !in_array($input['associationMode']??'',['transfer','checkOnly'],true))self::fail('该设备已关联其他岗位，请选择更改关联或仅点检');
    }
    public static function afterCheck($context,$deviceId,$input,$member,$team,$actor) {
        if(!$context['canAssociate'] || ($input['associationMode']??'')==='checkOnly')return;
        self::change($context['planId'],$deviceId,$context['positionId'],$context['ownPositionId'],$actor,'点检补充关联',(int)$team['id']);
    }
    public static function logs($team) {
        $rows=Db::name('me_position_device_log')->where('plan_id',(int)$team['plan_id'])->order('id desc')->limit(50)->select()->toArray();
        $devices=Db::name('me_machine_list')->whereIn('id',array_column($rows,'m_id') ?: [-1])->column('m_name','id');
        foreach($rows as &$r){$r['deviceName']=$devices[$r['m_id']]??'设备已不存在';$r['time']=date('Y-m-d H:i:s',$r['created_at']);}unset($r);return $rows;
    }
}
