<?php
namespace app\adminVue\controller;

use think\facade\Db;

/** Pure normalization plus a complete preflight before any MyISAM write. */
abstract class V2AccountImport
{
    public static function key($value)
    {
        if (!is_string($value)) throw new \InvalidArgumentException('工号须为文本格式，请在Excel中将工号列设为文本，保留前导零');
        $value=trim($value);
        if ($value==='' || mb_strlen($value)>20 || preg_match('/[\s<>\x00-\x1F]/u',$value)) throw new \InvalidArgumentException('工号须为1至20个字符，且不能包含空白');
        return $value;
    }

    public static function plan($rows)
    {
        if (!is_array($rows) || !$rows || count($rows)>5000) throw new \InvalidArgumentException('请上传1至5000行账号数据');
        $fields=['id','username','truename','password','status','dpt','job','leader','ext','phone','e_mail','eva_score','eva_num'];
        $users=Db::name('admin_user')->field(implode(',',$fields))->order('id')->select()->toArray();
        $org=Db::name('admin_org')->field('id,mid,title')->order('id')->select()->toArray();
        $byId=[]; $byKey=[];
        foreach ($users as $user) { $byId[(int)$user['id']]=$user; $byKey[strtolower(trim($user['username']))][]=$user; }
        $aliases=['ID'=>'id','工号'=>'username','姓名'=>'truename','密码'=>'password','部门'=>'dpt','职位'=>'job','岗位'=>'job','直属主管'=>'leader','电话号码'=>'phone','手机'=>'phone','手机号'=>'phone','分机'=>'ext','邮箱'=>'e_mail','邮件'=>'e_mail','状态'=>'status','评估分数'=>'eva_score','评估次数'=>'eva_num'];
        $stats=['total'=>count($rows),'new'=>0,'update'=>0,'unchanged'=>0,'skipped'=>0,'errors'=>0];
        $errors=[];$preview=[];$changes=[];$seen=[];$seenIds=[];
        foreach ($rows as $index=>$input) {
            $line=$index+2;
            try {
                if (!is_array($input)) throw new \InvalidArgumentException('数据格式不正确');
                $row=[];
                foreach ($input as $column=>$value) {
                    $field=$aliases[trim((string)$column)] ?? trim((string)$column);
                    if (!in_array($field,$fields,true)) continue;
                    if (array_key_exists($field,$row) && $row[$field]!==$value) throw new \InvalidArgumentException('存在重复或冲突的列：'.$field);
                    $row[$field]=$value;
                }
                $status=$row['status'] ?? 1;
                if (!is_scalar($status)) throw new \InvalidArgumentException('状态格式无效');
                if (in_array((string)$status,['0','2','待审核','离职'],true)) { $stats['skipped']++;continue; }
                if (!in_array((string)$status,['1','在职'],true)) throw new \InvalidArgumentException('状态无效；留空时请删除状态列，默认按在职处理');
                $username=self::key($row['username'] ?? null);$key=strtolower($username);
                if (isset($seen[$key])) throw new \InvalidArgumentException('工号在文件中重复，首次出现在第'.$seen[$key].'行');
                $seen[$key]=$line;
                $password=$row['password'] ?? null;
                if (!is_string($password) || !preg_match('/^[a-f0-9]{32}$/iD',trim($password))) throw new \InvalidArgumentException('密码列必须是32位MD5字符串');
                $existing=array_values(array_filter($byKey[$key] ?? [],function($u){return (int)$u['status']===1;}));
                if (count($existing)>1) throw new \InvalidArgumentException('系统中存在重复在职工号，需先核对');
                $old=$existing[0] ?? null;
                if (!$old && !empty($byKey[$key])) throw new \InvalidArgumentException('该工号存在历史账号，请先核对，系统不会自动恢复或新建重复账号');
                $id=null;
                if (isset($row['id']) && $row['id']!=='') {
                    if (!is_scalar($row['id']) || !preg_match('/^[1-9][0-9]{0,9}$/D',(string)$row['id']) || (float)$row['id']>2147483647) throw new \InvalidArgumentException('ID须为有效正整数');
                    $id=(int)$row['id'];
                    if (isset($seenIds[$id])) throw new \InvalidArgumentException('ID在文件中重复，首次出现在第'.$seenIds[$id].'行');
                    $seenIds[$id]=$line;
                    if (($old && $id!==(int)$old['id']) || (!$old && isset($byId[$id]))) throw new \InvalidArgumentException('ID与工号对应关系冲突');
                }
                $data=['password'=>strtolower(trim($password))];
                if (!$old || array_key_exists('truename',$row)) $data['truename']=self::text($row['truename'] ?? '',20,'姓名',true);
                foreach (['ext'=>11,'phone'=>11,'e_mail'=>50,'eva_num'=>10] as $field=>$length) {
                    if (array_key_exists($field,$row)) $data[$field]=self::text($row[$field],$length,$field);
                }
                if (!empty($data['e_mail']) && !filter_var($data['e_mail'],FILTER_VALIDATE_EMAIL)) throw new \InvalidArgumentException('邮箱格式不正确');
                foreach (['dpt','job'] as $field) {
                    if (array_key_exists($field,$row)) $data[$field]=self::organization($row[$field],$org,$field);
                }
                if (array_key_exists('leader',$row)) {
                    $value=$row['leader'];
                    if ($value==='' || $value===null || (string)$value==='0') $data['leader']=0;
                    elseif (is_scalar($value) && ctype_digit((string)$value) && isset($byId[(int)$value])) $data['leader']=(int)$value;
                    else throw new \InvalidArgumentException('直属主管须为已存在的账号ID');
                }
                if (array_key_exists('eva_score',$row)) {
                    if ($row['eva_score']==='' || $row['eva_score']===null) $data['eva_score']=null;
                    elseif (is_numeric($row['eva_score']) && abs((float)$row['eva_score'])<=999.99) $data['eva_score']=(float)$row['eva_score'];
                    else throw new \InvalidArgumentException('评估分数无效');
                }
                $changed=[];
                if ($old) {
                    foreach ($data as $field=>$value) if ((string)($old[$field] ?? '')!==(string)$value) $changed[]=$field;
                    $action=$changed?'update':'unchanged';
                } else {
                    $data=array_merge(['username'=>$username,'status'=>1,'notice'=>1,'reg_time'=>time(),'job'=>0,'leader'=>0,'e_mail'=>null],$data);
                    if ($id!==null) $data['id']=$id;
                    $changed=array_keys($data);$action='new';
                }
                $stats[$action]++;
                $preview[]=['line'=>$line,'username'=>$username,'name'=>$data['truename'] ?? $old['truename'],'action'=>$action,'passwordUpdated'=>$action==='new' || in_array('password',$changed,true)];
                if ($action!=='unchanged') $changes[]=['line'=>$line,'username'=>$username,'id'=>$old?(int)$old['id']:null,'data'=>$data,'passwordChanged'=>in_array('password',$changed,true)];
            } catch (\InvalidArgumentException $error) { $errors[]=['line'=>$line,'message'=>$error->getMessage()]; }
        }
        $stats['errors']=count($errors);
        // No password or credential fingerprint is returned to the browser.
        return ['stats'=>$stats,'preview'=>$preview,'errors'=>$errors,'changes'=>$changes,'snapshot'=>hash('sha256',json_encode([$users,$org]))];
    }

    private static function text($value,$length,$name,$required=false)
    {
        if ($value===null) $value='';
        if (!is_scalar($value)) throw new \InvalidArgumentException($name.'格式不正确');
        $value=trim((string)$value);
        if (($required && $value==='') || mb_strlen($value)>$length || preg_match('/[<>\x00-\x1F]/u',$value)) throw new \InvalidArgumentException($name.'内容为空或超过长度限制');
        return $value;
    }
    private static function organization($value,$org,$field)
    {
        if ($value===null || $value==='' || (string)$value==='0') return 0;
        if (!is_scalar($value)) throw new \InvalidArgumentException('部门或职位格式无效');
        $matches=array_values(array_filter($org,function($o)use($value,$field){return ($field!=='dpt' || (int)$o['mid']===0) && (ctype_digit((string)$value)?(int)$o['id']===(int)$value:$o['title']===trim((string)$value));}));
        if (count($matches)!==1 || (int)$matches[0]['id']>127) throw new \InvalidArgumentException('部门或职位不存在、重名或超出字段范围');
        return (int)$matches[0]['id'];
    }
}
