<?php
namespace app\adminVue\controller;

use think\facade\Db;
use think\facade\Cache;

class V2Employee extends V2Base
{
    private function number($key,$default,$max=2147483647)
    {
        try { return V2EmployeeImport::integer($this->request->get($key,$default),$key,1,$max); }
        catch (\InvalidArgumentException $e) { $this->fail('查询参数无效'); }
    }

    private function dictionaries()
    {
        return ['projects'=>Db::name('team_base')->where('mid',1)->field('id,title')->order('id')->select()->toArray(),
            'teams'=>Db::name('team_list')->field('id,team,project,status')->order('id')->select()->toArray()];
    }

    public function options()
    {
        return json(['code'=>0,'msg'=>'','data'=>$this->dictionaries()]);
    }

    private function present($rows,$options)
    {
        $teams=array_column($options['teams'],null,'id');$projects=array_column($options['projects'],'title','id');
        $ids=array_filter(array_column($rows,'poid'));
        $positions=$ids?Db::name('team_plan_position')->whereIn('id',$ids)->column('position','id'):[];
        foreach ($rows as &$row) {
            $team=$teams[$row['tid']] ?? null;
            $row['teamName']=$team?$team['team']:'班组缺失（'.$row['tid'].'）';
            $row['projectName']=$team?($projects[$team['project']] ?? '项目缺失'): '—';
            $row['positionName']=(int)$row['poid']>0?($positions[$row['poid']] ?? '岗位缺失（'.$row['poid'].'）'):'—';
            $row['hireDate']=(int)$row['hiredate']>0?date('Y-m-d',(int)$row['hiredate']):'';
            $row['leaveDate']=(int)$row['leavedate']>0?date('Y-m-d',(int)$row['leavedate']):'';
            foreach (['id','status','sex','indirect','tid'] as $field) $row[$field]=(int)$row[$field];
        }
        unset($row);return $rows;
    }

    public function index()
    {
        $page=$this->number('page',1,1000000);$limit=$this->number('limit',15,100);$status=$this->number('status',1,2);
        $keyword=$this->request->get('keyword','');$project=$this->request->get('project','');$team=$this->request->get('team','');
        if (!is_string($keyword) || mb_strlen($keyword)>100 || !is_scalar($project) || !is_scalar($team)) $this->fail('查询参数无效');
        $options=$this->dictionaries();$query=Db::name('member_list')->where('status',$status);
        if ($project!=='') {
            try { $project=V2EmployeeImport::integer($project,'项目',1); } catch (\InvalidArgumentException $e) { $this->fail($e->getMessage()); }
            $teams=array_column(array_filter($options['teams'],function($t)use($project){return (int)$t['project']===$project;}),'id');
            $query->whereIn('tid',$teams ?: [-1]);
        }
        if ($team!=='') {
            try { $team=V2EmployeeImport::integer($team,'班组',1); } catch (\InvalidArgumentException $e) { $this->fail($e->getMessage()); }
            $query->where('tid',$team);
        }
        $keyword=trim($keyword);
        if ($keyword!=='') {
            $teamIds=array_column(array_filter($options['teams'],function($t)use($keyword){return mb_stripos($t['team'],$keyword)!==false;}),'id');
            $query->where(function($q)use($keyword,$teamIds){
                $q->whereLike('eid|ename','%'.addcslashes($keyword,'%_\\').'%');
                if ($teamIds) $q->whereOr('tid','in',$teamIds);
            });
        }
        $total=(clone $query)->count();
        $rows=$query->field('id,eid,ename,sex,tid,poid,indirect,hiredate,position_mark,leavedate,status')->order('id desc')->page($page,$limit)->select()->toArray();
        return json(['code'=>0,'msg'=>'','data'=>['list'=>$this->present($rows,$options),'total'=>$total]]);
    }

    private function raw($id)
    {
        $main=Db::name('member_list')->where('id',$id)->field(implode(',',V2EmployeeImport::MAIN))->find();
        if (!$main) $this->fail('员工档案不存在',404);
        $details=Db::name('member_interview')->where('m_id',$id)->field(implode(',',V2EmployeeImport::DETAIL))->select()->toArray();
        if (count($details)>1) $this->fail('该员工存在重复详情关联，请先核对',409);
        return [$main,$details[0] ?? null];
    }

    public function detail()
    {
        list($main,$personal)=$this->raw($this->number('id',0));
        $version=V2EmployeeImport::version($main,$personal);
        if ($personal) {
            $personal['sourceLabel']=V2EmployeeImport::sourceLabel($personal['source_type']);
            $personal['educationLabel']=V2EmployeeImport::educationLabel($personal['edu']);
            foreach (['annual_start','transfer_date','contract_date'] as $field) $personal[$field]=(int)$personal[$field]>0?date('Y-m-d',(int)$personal[$field]):'';
        }
        return json(['code'=>0,'msg'=>'','data'=>['main'=>$this->present([$main],$this->dictionaries())[0],'personal'=>$personal,'version'=>$version,'nameConflict'=>$personal && $main['ename']!==$personal['ename']]]);
    }

    public function update()
    {
        $input=$this->request->post();
        try { $id=V2EmployeeImport::integer($input['id'] ?? null,'员工ID',1); }
        catch (\InvalidArgumentException $e) { $this->fail($e->getMessage()); }
        $version=$input['version'] ?? '';
        if (!is_string($version) || !preg_match('/^[a-f0-9]{64}$/D',$version)) $this->fail('请重新读取员工资料',409);
        try {
            return V2EmployeeImport::locked(function()use($id,$version,$input){
                list($main,$personal)=$this->raw($id);
                if ((int)$main['status']!==1) $this->fail('只允许修改在职员工资料',409);
                if (!hash_equals(V2EmployeeImport::version($main,$personal),$version)) $this->fail('员工资料已变化，请重新加载后编辑',409);
                try { $data=V2EmployeeImport::editValues($input['values'] ?? null,array_merge($personal ?: [],['ename'=>$main['ename']])); }
                catch (\InvalidArgumentException $e) { $this->fail($e->getMessage()); }
                $mainWritten=false;
                try {
                    if ($data['ename']!==$main['ename']) { Db::name('member_list')->where('id',$id)->update(['ename'=>$data['ename']]);$mainWritten=true; }
                    if ($personal) Db::name('member_interview')->where('id',$personal['id'])->update($data);
                    else Db::name('member_interview')->insert(array_merge(['m_id'=>$id,'sex'=>$main['sex']],$data));
                } catch (\Throwable $e) {
                    $this->fail($mainWritten?'主档姓名已保存，但详细资料保存失败，请重新加载核对后重试':'资料保存失败，请重新加载核对后重试',500);
                }
                return json(['code'=>0,'msg'=>'员工资料已保存','data'=>null]);
            });
        } catch (\think\exception\HttpResponseException $e) { throw $e; }
        catch (\RuntimeException $e) { $this->fail('保存未完成，请重新加载核对后重试',409); }
    }

    private function ticketKey($ticket)
    {
        return 'admin-employee-import:'.hash('sha256',$this->requestToken().'|'.$ticket);
    }

    public function preview()
    {
        $mode=$this->request->post('mode');
        if (!in_array($mode,['new','sync'],true)) $this->fail('请使用新员工导入，或上传完整的内网人员岗位同步文件',409);
        $rows=$this->request->post($mode==='sync'?'sheets':'rows');
        try { $plan=$mode==='sync'?V2EmployeeImport::planWorkbook($rows):V2EmployeeImport::plan($rows,$mode); }
        catch (\InvalidArgumentException $e) { $this->fail($e->getMessage()); }
        $ticket=null;
        if (!$plan['errors'] && ($plan['changes'] || !empty($plan['dictionaryChanges']))) {
            $ticket=bin2hex(random_bytes(24));
            Cache::set($this->ticketKey($ticket),['rows'=>$rows,'mode'=>$mode,'snapshot'=>$plan['snapshot'],'expiresAt'=>time()+600],600);
        }
        return json(['code'=>0,'msg'=>'','data'=>['stats'=>$plan['stats'],'preview'=>$plan['preview'],'errors'=>$plan['errors'],'leavers'=>$plan['leavers'] ?? [],'dictionaries'=>$plan['dictionaries'] ?? null,'ticket'=>$ticket]]);
    }

    public function import()
    {
        $ticket=$this->request->post('ticket');
        if (!is_string($ticket) || !preg_match('/^[a-f0-9]{48}$/D',$ticket)) $this->fail('请先校验上传文件');
        try {
            return V2EmployeeImport::locked(function()use($ticket){
                $key=$this->ticketKey($ticket);$cached=Cache::get($key);
                if (!$cached || $cached['expiresAt']<time()) $this->fail('预览已过期或已提交，请重新检查文件',409);
                if (!in_array($cached['mode'],['new','sync'],true)) $this->fail('请重新选择合并导出的内网文件',409);
                $plan=$cached['mode']==='sync'?V2EmployeeImport::planWorkbook($cached['rows']):V2EmployeeImport::plan($cached['rows'],$cached['mode']);
                if ($plan['errors'] || ($cached['mode']==='new' && !hash_equals($cached['snapshot'],$plan['snapshot']))) $this->fail('资料或文件结构已变化，请重新读取文件后提交',409);
                Cache::delete($key);
                $result=['created'=>0,'updated'=>0,'unchanged'=>$plan['stats']['unchanged'],'departed'=>0,'failed'=>[],'unexecuted'=>0];
                if (isset($plan['dictionaries'])) {
                    $result['dictionaries']=[];
                    foreach ($plan['dictionaries'] as $sheet=>$stats) $result['dictionaries'][$sheet]=['created'=>0,'updated'=>0,'unchanged'=>$stats['unchanged']];
                }
                $work=array_merge($plan['dictionaryChanges'] ?? [],$plan['changes']);
                foreach ($work as $index=>$change) {
                    $stage='未写入';
                    try {
                        if (isset($change['table'])) {
                            if (!empty($change['leave'])) {
                                $result['departed']+=Db::name('member_list')->where(['id'=>$change['id'],'status'=>1])->update($change['data']);
                                continue;
                            }
                            if ($change['new']) Db::name($change['table'])->insert($change['data']);
                            else Db::name($change['table'])->where('id',$change['id'])->update($change['data']);
                            $result[$change['new']?'created':'updated']++;
                            if (isset($result['dictionaries'][$change['sheet']])) $result['dictionaries'][$change['sheet']][$change['new']?'created':'updated']++;
                            continue;
                        }
                        if ($change['main']) {
                            if ($change['mainNew']) Db::name('member_list')->insert($change['main']);
                            else Db::name('member_list')->where('id',$change['employeeId'])->update($change['main']);
                            $stage='主档已写入';
                        }
                        if ($change['detail']) {
                            if ($change['detailNew']) Db::name('member_interview')->insert($change['detail']);
                            else Db::name('member_interview')->where('id',$change['detail']['id'])->update($change['detail']);
                            $stage=$stage==='主档已写入'?'主档和详情已写入':'详情已写入';
                        }
                        $isNew=$change['mainNew'];
                        $result[$isNew?'created':'updated']++;
                    } catch (\Throwable $e) {
                        $result['unexecuted']=count($work)-$index-1;
                        $result['failed'][]=['sheet'=>$change['sheet'] ?? 'list/detail','line'=>$change['line'],'listLine'=>$change['listLine'] ?? null,'detailLine'=>$change['detailLine'] ?? null,'employeeId'=>$change['employeeId'] ?? $change['id'],'message'=>$stage.'；本项未完整完成，请按工作表行号重新预览核对，不会自动回滚'];
                        break;
                    }
                }
                \app\mobile\service\MemberPosition::purgeInvalid();
                return json(['code'=>0,'msg'=>'','data'=>$result]);
            });
        } catch (\think\exception\HttpResponseException $e) { throw $e; }
        catch (\RuntimeException $e) { $this->fail('同步未完成，请重新加载资料并预览后重试',409); }
    }
}
