<?php
namespace app\mobile\service;

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

class TemplateFailureMonitor
{
    public static function clean($message,$limit=2000) {
        $message=preg_replace('/(access_token|appsecret|secret|password|authorization)([\s"\x27:=]+)[^&\s"\x27,}]+/i','$1$2[redacted]',(string)$message);
        return mb_substr(strip_tags($message),0,$limit,'UTF-8');
    }
    public static function title($key) {
        return ['work_order_pending'=>'工单待处理通知','workflow_pending'=>'OA系统流程审批通知','workflow_approved'=>'OA系统流程审批通过通知',
            'workflow_rejected'=>'OA流程审批拒绝通知','exam_arranged'=>'培训课程考试报名成功提醒','feedback_result'=>'意见反馈处理通知'][$key] ?? $key;
    }
    public function record($context,\Throwable $error) {
        try {
            $now=time();$message=self::clean($error->getMessage());$stage=$context['stage'];
            $hash=empty($context['recipient'])?'':hash('sha256',$context['recipient']);$label='';
            if (!empty($context['recipient'])) {
                $account=Db::name('auth')->where('gzhOpenid',$context['recipient'])->field('username,name')->find();
                $label=$account?trim($account['username'].' '.($account['name'] ?? '')):'绑定接收人（已脱敏）';
            }
            $key=hash('sha256',implode('|',[$context['template'],$stage,$error->getCode(),$hash,$context['http_status'] ?? 0,$context['curl_errno'] ?? 0,
                $stage==='payload'?$message:'']));
            $definition=app()->config->get('wxoms_templates.'.$context['template'],[]);
            $data=['open_key'=>$key,'template_key'=>$context['template'],'template_id'=>$definition['id'] ?? '', 'stage'=>$stage,'error_code'=>(int)$error->getCode(),
                'error_message'=>$message,'http_status'=>$context['http_status'] ?? 0,'curl_errno'=>$context['curl_errno'] ?? 0,
                'business_no'=>mb_substr((string)($context['business_no'] ?? ''),0,64),'business_page'=>mb_substr($context['page'] ?? '',0,255),
                'recipient_label'=>mb_substr($label,0,255),'recipient_hash'=>$hash,'first_at'=>$now,'last_at'=>$now];
            $prefix=app()->config->get('database.connections.'.app()->config->get('database.default').'.prefix');
            if (!preg_match('/^[a-zA-Z0-9_]*$/D',$prefix)) throw new \RuntimeException('Invalid database prefix');
            $columns=array_keys($data);
            Db::execute('INSERT INTO `'.$prefix.'template_failure` (`'.implode('`,`',$columns).'`) VALUES ('.implode(',',array_fill(0,count($data),'?')).') ON DUPLICATE KEY UPDATE occurrences=occurrences+1,last_at=VALUES(last_at),error_message=VALUES(error_message),business_no=VALUES(business_no),business_page=VALUES(business_page),recipient_label=VALUES(recipient_label)',array_values($data));
        } catch (\Throwable $failure) { Log::warning('wxoms failure persistence unavailable: '.self::clean($failure->getMessage())); }
    }
    public function notifyPending(WechatTemplateService $service,$limit=1,$cooldown=600) {
        $sent=0;
        try {
            $ids=Db::name('template_alert_subscriber')->alias('s')->join('admin_user u','u.id=s.admin_id')->where(['s.enabled'=>1,'u.status'=>1])->where('u.rightid','>=',99)->column('s.admin_id');
            if (!$ids) return 0;
            $recipients=$service->admins($ids);if (!$recipients) return 0;
            $now=time();$rows=Db::name('template_failure')->where('status',0)->where('last_alert_attempt_at','<=',$now-$cooldown)
                ->whereRaw('alerted_occurrences < occurrences')->order('last_at desc,id desc')->limit($limit)->select()->toArray();
            foreach ($rows as $row) {
                $claimed=Db::name('template_failure')->where(['id'=>$row['id'],'status'=>0,'last_alert_attempt_at'=>$row['last_alert_attempt_at']])->update(['last_alert_attempt_at'=>$now]);
                if (!$claimed) continue;
                $summary=(int)$row['error_code']===40164?'IP不在服务号白名单':preg_replace('/^(Wechat rejected|Token unavailable):\s*/','',$row['error_message']);
                $ok=$service->sendAlert($recipients,['notice'=>$row['error_code'].' '.$summary,'number'=>(string)$row['id'],'time'=>WechatTemplateService::time($row['last_at']),'name'=>self::title($row['template_key'])]);
                $changes=$ok?['last_alert_at'=>$now,'alerted_occurrences'=>$row['occurrences'],'alert_error'=>'']:['alert_error'=>self::clean($service->lastFailure(),1000)];
                Db::name('template_failure')->where('id',$row['id'])->update($changes);
                if ($ok) $sent++;
            }
        } catch (\Throwable $error) { Log::warning('wxoms fault alert unavailable: '.self::clean($error->getMessage())); }
        return $sent;
    }
}
