<?php
namespace app\adminVue\controller;

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

class V2Account extends V2Base
{
    private function fields() { return 'id,username,truename,dpt,job,leader,ext,phone,e_mail,face_url,eva_score,eva_num,reg_time'; }
    private function number($key,$default,$max)
    {
        $v=$this->request->get($key,$default);
        if (!is_scalar($v) || !preg_match('/^[1-9][0-9]*$/D',(string)$v) || (float)$v>$max) $this->fail('查询参数无效');
        return (int)$v;
    }
    private function present($rows)
    {
        $org=Db::name('admin_org')->column('title','id');
        $ids=array_filter(array_column($rows,'leader'));
        $names=$ids?Db::name('admin_user')->whereIn('id',$ids)->column('truename','id'):[];
        foreach ($rows as &$row) {
            $row['department']=$org[$row['dpt']] ?? '';$row['jobName']=$org[$row['job']] ?? '';$row['leaderName']=$names[$row['leader']] ?? '';
            $row['registeredAt']=(int)$row['reg_time']>0?date('Y-m-d',(int)$row['reg_time']):'';
            $url=(string)$row['face_url'];
            $row['avatar']=preg_match('~^https?://~i',$url) || (strpos($url,'/')===0 && strpos($url,'//')!==0) ? $url : '';
            unset($row['face_url'],$row['reg_time']);
        }
        unset($row);return $rows;
    }
    public function index()
    {
        $page=$this->number('page',1,1000000);$limit=$this->number('limit',15,100);
        $keyword=$this->request->get('keyword','');$department=$this->request->get('department','');
        if (!is_string($keyword) || mb_strlen($keyword)>100 || !is_scalar($department)) $this->fail('查询参数无效');
        $query=Db::name('admin_user')->where('status',1);
        if (trim($keyword)!=='') $query->whereLike('username|truename','%'.addcslashes(trim($keyword),'%_\\').'%');
        if ($department!=='') {
            if (!ctype_digit((string)$department) || (int)$department<1) $this->fail('部门参数无效');
            $query->where('dpt',(int)$department);
        }
        $total=(clone $query)->count();
        $rows=$query->field($this->fields())->order('id desc')->page($page,$limit)->select()->toArray();
        return json(['code'=>0,'msg'=>'','data'=>['list'=>$this->present($rows),'total'=>$total,'page'=>$page,'limit'=>$limit]]);
    }
    public function options()
    {
        return json(['code'=>0,'msg'=>'','data'=>['departments'=>Db::name('admin_org')->where('mid',0)->field('id,title')->order('id')->select()->toArray()]]);
    }
    public function detail()
    {
        $id=$this->number('id',0,2147483647);
        $row=Db::name('admin_user')->where(['id'=>$id,'status'=>1])->field($this->fields())->find();
        if (!$row) $this->fail('在职账号不存在',404);
        return json(['code'=>0,'msg'=>'','data'=>$this->present([$row])[0]]);
    }
    private function ticketKey($ticket)
    {
        return 'admin-account-import:'.hash('sha256',$this->requestToken().'|'.$ticket);
    }
    public function preview()
    {
        $rows=$this->request->post('rows');
        try { $plan=V2AccountImport::plan($rows); }
        catch (\InvalidArgumentException $e) { $this->fail($e->getMessage()); }
        $ticket=null;
        if (!$plan['errors'] && $plan['changes']) {
            $ticket=bin2hex(random_bytes(24));
            Cache::set($this->ticketKey($ticket),['rows'=>$rows,'snapshot'=>$plan['snapshot'],'expiresAt'=>time()+600],600);
        }
        return json(['code'=>0,'msg'=>'','data'=>['stats'=>$plan['stats'],'preview'=>$plan['preview'],'errors'=>$plan['errors'],'ticket'=>$ticket]]);
    }
    public function import()
    {
        $ticket=$this->request->post('ticket');
        if (!is_string($ticket) || !preg_match('/^[a-f0-9]{48}$/D',$ticket)) $this->fail('请先检查上传文件');
        $cached=Cache::get($this->ticketKey($ticket));
        if (!$cached || $cached['expiresAt']<time()) $this->fail('预览已过期，请重新检查文件',409);
        $config=config('database.connections.'.config('database.default'));
        $lock='account-import:'.substr(hash('sha256',$config['database'].'|'.$config['prefix']),0,48);
        $acquired=Db::query('SELECT GET_LOCK(:lockname, 5) AS acquired',['lockname'=>$lock]);
        if ((int)$acquired[0]['acquired']!==1) $this->fail('其他名单正在同步，请稍后重试',409);
        $pdo=null;
        try {
            $prefix=$config['prefix'];
            if (!preg_match('/^[A-Za-z0-9_]*$/D',$prefix)) $this->fail('数据库表配置无效',500);
            // Shared MyISAM data also has old writers that do not use GET_LOCK.
            // Hold table locks from snapshot validation through all writes.
            $pdo=Db::connect()->getConnection()->getPdo();
            $pdo->exec('LOCK TABLES `'.$prefix.'admin_user` WRITE, `'.$prefix.'admin_org` READ');
            $plan=V2AccountImport::plan($cached['rows']);
            if ($plan['errors'] || !hash_equals($cached['snapshot'],$plan['snapshot'])) $this->fail('账号或组织资料已变化，请重新预览后提交',409);
            Cache::delete($this->ticketKey($ticket));
            $result=['created'=>0,'updated'=>0,'unchanged'=>$plan['stats']['unchanged'],'skipped'=>$plan['stats']['skipped'],'failed'=>[],'reauthRequired'=>false];
            foreach ($plan['changes'] as $position=>$change) {
                try {
                    if ($change['id']!==null) {
                        $data=$change['data'];
                        if ($change['passwordChanged']) {
                            $legacyToken=Db::name('admin_user')->where('id',$change['id'])->value('token');
                            $data['token']=null;$data['sess_id']=null;
                            if ($legacyToken) Cache::delete('TokenInfo'.$legacyToken);
                        }
                        Db::name('admin_user')->where(['id'=>$change['id'],'status'=>1])->update($data);$result['updated']++;
                        if ($change['passwordChanged'] && $change['id']===(int)$this->actor['id']) $result['reauthRequired']=true;
                    } else { Db::name('admin_user')->insert($change['data']);$result['created']++; }
                } catch (\Throwable $e) {
                    // MyISAM cannot roll back: report actual success and stop, never claim atomic completion.
                    $result['failed'][]=['line'=>$change['line'],'message'=>'该行写入失败，后续'.(count($plan['changes'])-$position-1).'行未执行，请重新检查文件后重试'];break;
                }
            }
            return json(['code'=>0,'msg'=>'','data'=>$result]);
        } finally {
            if ($pdo) $pdo->exec('UNLOCK TABLES');
            Db::query('SELECT RELEASE_LOCK(:lockname)',['lockname'=>$lock]);
        }
    }
}
