<?php
namespace app\mobile\controller;

use think\facade\Db;
use think\facade\Log;

class OmsLeaveCancel extends OmsBase
{
    public function messageItems($account,$since,\app\mobile\service\MessageInbox $inbox) {
        if(!$inbox->allowsSource($this))$this->fail('接口不存在',404);
        $leader=self::leaderFor($account);$member=OmsLeaveApply::memberFor($account);$items=[];
        if($leader){
            foreach(Db::name('member_perleave')->where(['aid'=>$leader['id'],'status'=>1])->select()->toArray() as $row){$p=$this->present($row,$leader);
                if($p['canCommunicate'] && trim((string)$row['communicate'])==='')$items[]=\app\mobile\service\MessageInbox::item('leave',$row['id'],'communicate','离职申请待沟通','待登记沟通结果',['员工'=>$p['name'],'工号'=>$p['employeeNo'],'班组'=>$p['team'],'预离职日期'=>$p['expectedLeaveDate'],'离职原因'=>$p['reason']],'/pages/leave-cancel/detail?id='.$row['id'],'pending',$row['update']);
                elseif($p['canCommunicate'] && trim((string)$row['communicate'])!=='')$items[]=\app\mobile\service\MessageInbox::item('leave',$row['id'],'communicate','离职沟通已登记','已登记沟通结果',['员工'=>$p['name'],'班组'=>$p['team'],'沟通结果'=>$row['communicate']],'/pages/leave-cancel/detail?id='.$row['id'],'done',$row['update']);
            }
        }
        if($member)foreach(Db::name('member_perleave')->where('mid',$member['id'])->whereIn('status',[2,3])->where('update','>=',$since)->select()->toArray() as $row){$label=(int)$row['status']===3?'已撤销':'已办理';$items[]=\app\mobile\service\MessageInbox::item('leave',$row['id'],'result-'.$row['status'],'离职申请'.$label,$label,['员工'=>$member['ename'],'离职原因'=>$row['reason'],'说明'=>$row['mark']],'/pages/leave-apply/index','notice',$row['update']);}
        return $items;
    }
    public static function leaderFor($account)
    {
        if ((int)($account['admin_id'] ?? 0) <= 0) return null;
        return Db::name('admin_user')->where(['id'=>$account['admin_id'], 'username'=>$account['username'], 'status'=>1, 'job'=>9])->find();
    }
    private function leader($account)
    {
        $leader = self::leaderFor($account);
        if (!$leader) $this->fail('仅班长可以处理离职撤销', 403);
        return $leader;
    }
    private function result($data = null) { return json(['code'=>0, 'message'=>'成功', 'data'=>$data]); }
    private function id($value)
    {
        if (!is_scalar($value) || !preg_match('/^[1-9][0-9]{0,9}$/', (string)$value) || (float)$value > 2147483647) $this->fail('申请编号不正确');
        return (int)$value;
    }
    private function text($value, $max, $required = true)
    {
        if (!is_string($value) || mb_strlen($value) > $max || ($required && trim($value) === '')) $this->fail('请填写有效内容（最多'.$max.'字）');
        return trim($value);
    }
    private function row($id)
    {
        $row = Db::name('member_perleave')->find($this->id($id));
        if (!$row) $this->fail('申请不存在', 404);
        return $row;
    }
    private function blockReason($row, $member)
    {
        if ((int)$row['status'] !== 1) return '申请已办理或已取消，请返回列表刷新';
        if (!$member || (int)$member['status'] !== 1) return '员工已离职或关联异常，请联系HR核对';
        if ((int)$row['leave_date'] > 0 || (int)$row['submit_date'] > 0) return '申请已有离职办理信息，请联系HR核对';
        return '';
    }
    private function auditPrefix($leader)
    {
        return '由'.$leader['truename'].'（后台ID:'.$leader['id'].'）于'.date('Y-m-d H:i:s').'撤销，原因：';
    }
    private function reasonLimit($row, $leader)
    {
        return max(0, 255 - mb_strlen($this->auditPrefix($leader)) - (trim((string)$row['mark']) === '' ? 0 : mb_strlen((string)$row['mark']) + 1));
    }
    private function present($row, $leader)
    {
        $member = Db::name('member_list')->find($row['mid']);
        $team = Db::name('team_list')->find($row['tid']);
        $type = Db::name('member_base')->where(['id'=>$row['type'], 'mid'=>1])->find();
        $reason = $this->blockReason($row, $member);
        return [
            'id'=>(int)$row['id'], 'name'=>$member['ename'] ?? '员工信息缺失', 'employeeNo'=>$member['eid'] ?? '',
            'team'=>$team['team'] ?? '班组信息缺失', 'leaderName'=>Db::name('admin_user')->where('id',$row['aid'])->value('truename') ?: '班长未设置',
            'typeName'=>$type ? trim($type['title'].' '.$type['content']) : '离职类型未设置',
            'reason'=>(string)$row['reason'], 'communicate'=>(string)$row['communicate'], 'mark'=>(string)$row['mark'],
            'expectedLeaveDate'=>empty($row['perleave_date']) ? '' : date('Y-m-d',$row['perleave_date']),
            'updatedAt'=>empty($row['update']) ? '' : date('Y-m-d H:i:s',$row['update']),
            'canCommunicate'=>$reason === '', 'canCancel'=>$reason === '' && $this->reasonLimit($row,$leader) > 0,
            'blockReason'=>$reason ?: ($this->reasonLimit($row,$leader) > 0 ? '' : '原备注已满，请联系HR核对'),
            'cancelReasonLimit'=>$this->reasonLimit($row,$leader),
        ];
    }
    public function records()
    {
        return $this->guarded(function () {
            $this->requireMethod('GET'); $this->leader($this->current());
            $page = $this->id($this->request->get('page',1)); $size = 10;
            $keyword = $this->text($this->request->get('keyword',''),50,false);
            $query = Db::name('member_perleave')->alias('p')
                ->leftJoin('member_list m','m.id=p.mid')->leftJoin('team_list t','t.id=p.tid')->leftJoin('admin_user a','a.id=p.aid')->where('p.status',1);
            if ($keyword !== '') {
                $pattern = '%'.str_replace(['\\','%','_'],['\\\\','\\%','\\_'],$keyword).'%';
                $query->where(function ($q) use ($pattern) { $q->whereLike('m.ename|m.eid|t.team|a.truename',$pattern); });
            }
            $total = (clone $query)->count();
            $rows = $query->field('p.id,p.reason,p.communicate,p.perleave_date,m.ename as name,m.eid as employeeNo,t.team,a.truename as leaderName')->order('p.id desc')->page($page,$size)->select()->toArray();
            foreach ($rows as &$row) {
                $row['id'] = (int)$row['id']; $row['name'] = $row['name'] ?: '员工信息缺失'; $row['team'] = $row['team'] ?: '班组信息缺失';
                $row['employeeNo'] = (string)$row['employeeNo']; $row['leaderName'] = $row['leaderName'] ?: '班长未设置';
                $row['communicate'] = (string)$row['communicate']; $row['expectedLeaveDate'] = empty($row['perleave_date']) ? '' : date('Y-m-d',$row['perleave_date']); unset($row['perleave_date']);
            }
            unset($row);
            return $this->result(['items'=>$rows,'total'=>$total,'page'=>$page,'pageSize'=>$size]);
        });
    }
    public function detail()
    {
        return $this->guarded(function () {
            $this->requireMethod('GET'); $leader = $this->leader($this->current()); $row = $this->row($this->request->get('id'));
            if ((int)$row['status'] !== 1) $this->fail('申请已办理或已取消，请返回列表刷新',409);
            return $this->result($this->present($row,$leader));
        });
    }
    private function writeLock($action)
    {
        $prefix = $this->app->config->get('database.connections.'.$this->app->config->get('database.default').'.prefix');
        if (!preg_match('/^[a-zA-Z0-9_]+$/',$prefix)) throw new \RuntimeException('Invalid prefix');
        // MyISAM跨表核验使用表锁；锁内只读取身份、员工和申请并更新，不发送通知。
        Db::query('SELECT 1'); $connection = Db::connect()->getConnection(); $pdo = $connection->getPdo();
        $pdo->exec("LOCK TABLES `{$prefix}member_perleave` WRITE, `{$prefix}member_list` READ, `{$prefix}admin_user` READ");
        try { return $action(); } finally { $connection->free(); $pdo->exec('UNLOCK TABLES'); }
    }
    public function communicate()
    {
        return $this->guarded(function () {
            $this->requireMethod('POST'); $account = $this->current(); $this->leader($account);
            $content = $this->text($this->request->post('communicate'),255);
            $expected = $this->request->post('expectedCommunicate');
            if (!is_string($expected) || mb_strlen($expected) > 255) $this->fail('请刷新后再保存沟通记录');
            $updatedAt = $this->writeLock(function () use ($account,$content,$expected) {
                $this->leader($account); $row = $this->row($this->request->post('id')); $member = Db::name('member_list')->find($row['mid']);
                $reason = $this->blockReason($row,$member); if ($reason !== '') $this->fail($reason,409);
                if ((string)$row['communicate'] !== $expected) $this->fail('沟通记录已被更新，请刷新后重新编辑',409);
                $now = time();
                Db::name('member_perleave')->where(['id'=>$row['id'],'status'=>1])->update(['communicate'=>$content,'update'=>$now]);
                return date('Y-m-d H:i:s',$now);
            });
            return $this->result(['updatedAt'=>$updatedAt]);
        });
    }
    public function cancel()
    {
        return $this->guarded(function () {
            $this->requireMethod('POST'); $account = $this->current(); $this->leader($account);
            $reason = $this->text($this->request->post('reason'),255);
            $saved = $this->writeLock(function () use ($account,$reason) {
                $leader = $this->leader($account); $row = $this->row($this->request->post('id')); $member = Db::name('member_list')->find($row['mid']);
                $blocked = $this->blockReason($row,$member); if ($blocked !== '') $this->fail($blocked,409);
                if (mb_strlen($reason) > $this->reasonLimit($row,$leader)) $this->fail('撤销原因过长，含原备注合计最多255字');
                $mark = $this->auditPrefix($leader).$reason.(trim((string)$row['mark']) === '' ? '' : "\n".$row['mark']);
                $changed = Db::name('member_perleave')->where(['id'=>$row['id'],'status'=>1])->update(['status'=>3,'update'=>time(),'mark'=>$mark]);
                if ($changed !== 1) $this->fail('申请状态已变化，请刷新',409);
                return ['row'=>$row,'member'=>$member,'leader'=>$leader,'reason'=>$reason];
            });
            $this->notifyCancellation($saved);
            return $this->result();
        });
    }
    protected function notifyCancellation($saved)
    {
        try {
            $accounts = Db::name('auth')->where(['user_id'=>$saved['member']['id'],'username'=>$saved['member']['eid'],'status'=>1])->select()->toArray();
            $recipients = [];
            foreach ($accounts as $account) {
                try { $this->assertActive($account); if (trim((string)$account['gzhOpenid'])) $recipients[] = $account['gzhOpenid']; } catch (\Throwable $error) { continue; }
            }
            $recipients = array_unique($recipients);
            if (!$recipients) { Log::warning('oms leave cancellation recipient unavailable'); return; }
            $key = $this->cacheKey('leave-cancel:gzh-token'); $token = $this->cache()->get($key);
            if (!$token) {
                $appid = env('wxoms.gzh_appid',''); $secret = env('wxoms.gzh_secret','');
                if (!$appid || !$secret) throw new \RuntimeException('GZH configuration missing');
                $result = $this->postWechat('https://api.weixin.qq.com/cgi-bin/stable_token',['grant_type'=>'client_credential','appid'=>$appid,'secret'=>$secret]);
                if (empty($result['access_token'])) throw new \RuntimeException('Token unavailable');
                $token = $result['access_token']; $this->cache()->set($key,$token,max(60,(int)$result['expires_in']-120));
            }
            foreach ($recipients as $recipient) {
                try {
                    $result = $this->postWechat('https://api.weixin.qq.com/cgi-bin/message/template/send?access_token='.rawurlencode($token),[
                        'touser'=>$recipient,'template_id'=>'3Huxw6Ptdx15igJrOWwJ2OhlRmqX15AKwDdI5cK-ZOE',
                        'miniprogram'=>['appid'=>env('wxoms.appid',''),'pagepath'=>'pages/home/index'],
                        'data'=>['keyword1'=>['value'=>(string)$saved['row']['id']],'keyword2'=>['value'=>'预离职申请'],
                            'keyword3'=>['value'=>'已撤销：'.mb_substr($saved['reason'],0,60)],'keyword4'=>['value'=>$saved['leader']['truename']],
                            'keyword5'=>['value'=>date('Y-m-d H:i:s')]],
                    ]);
                    if (!isset($result['errcode']) || (int)$result['errcode'] !== 0) {
                        if (in_array((int)($result['errcode'] ?? 0),[40001,40014,42001],true)) $this->cache()->delete($key);
                        Log::warning('oms leave cancellation notification failed: code='.(int)($result['errcode'] ?? -1));
                    }
                } catch (\Throwable $error) { Log::warning('oms leave cancellation recipient unavailable'); }
            }
        } catch (\Throwable $error) { Log::warning('oms leave cancellation notification unavailable'); }
    }
    private function postWechat($url,$data)
    {
        $ch = curl_init($url);
        curl_setopt_array($ch,[CURLOPT_POST=>true,CURLOPT_POSTFIELDS=>json_encode($data,JSON_UNESCAPED_UNICODE),CURLOPT_HTTPHEADER=>['Content-Type: application/json'],CURLOPT_RETURNTRANSFER=>true,CURLOPT_CONNECTTIMEOUT=>2,CURLOPT_TIMEOUT=>4,CURLOPT_SSL_VERIFYPEER=>true,CURLOPT_SSL_VERIFYHOST=>2]);
        $ca = $this->app->getRootPath().'config/cacert.pem'; if (is_file($ca)) curl_setopt($ch,CURLOPT_CAINFO,$ca);
        $body = curl_exec($ch); $status = curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch);
        $result = is_string($body) ? json_decode($body,true) : null;
        if ($status !== 200 || !is_array($result)) throw new \RuntimeException('Wechat unavailable');
        return $result;
    }
}
