<?php

namespace App\Jobs\WechatPlatform;

use App\Service\Util\Support\Trace\TraceContext;
use App\Service\WechatPlatform\GZHSendKFMessageService;
use App\Service\WechatPlatform\WechatPlatform;
use EasyWeChat\OfficialAccount\Application;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;


class GZHSendKFMessage implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * @var
     * <pre>
     * [
     *  'gzhId' => $gzhId,   // wechat_authorization_infos.id
     *  'messageId' => $item->id,  // wechat_kf_messages.id
     *  'traceInfo' => $traceContext->getTraceInfo()  // traceInfo
     * ]
     * </pre>
     */
    private $info;

    private $isTest;

    /**
     * @var TraceContext
     */
    private $traceContext;
    /**
     * Create a new job instance.
     */
    public function __construct($info)
    {
        $this->info = $info;
        $this->isTest = $info['isTest'] ?? false;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        $this->traceContext = TraceContext::newFromParent($this->info['traceInfo']);
        myLog('KFMessageSend')->info('公众号开始发送客服消息', [
            'info' => $this->info,
            'traceInfo' => $this->traceContext->getTraceInfo(),
        ]);

        $gzh = $this->getGZH();
        if(!$gzh) return;

        $message = $this->getMessage();
        if(!$message) return;


        $messageContent = collect(\json_decode($message->message_content, true));
        $messageStr = $messageContent->pluck('text')->join("\n");

        $officialAccount = $this->getOfficialAccount($gzh);
        if(false === $officialAccount) return;

        if($this->isTest) {
            $openid = $this->info['openid'] ?? '';
            if(!$openid) {
                myLog('KFMessageSend')->error('测试回传没有openid', [
                    'info' => $this->info
                ]);
            }
            GZHSendKFMessageService::sendText($officialAccount, $openid, $messageStr, $this->traceContext);
        } else {
            $next_openid = '';
            $loop = 1;
            while (true) {
                if($loop++ > 10000) {
                    break;
                }
                if(1 == $message->u_type) {
                    $info = $this->getUserOpenids($officialAccount, $next_openid);
                    foreach ($info['openid'] as $opid){
                        GZHSendKFMessageService::sendText($officialAccount, $opid, $messageStr, $this->traceContext);
                    }
                    $next_openid = $info['next_openid'];
                    if(!$next_openid) {
                        break;
                    }
                } elseif (2 == $message->u_type) {
                    // todo, 从人群包中获取.
                }
            }
        }
    }

    private function getMessage() {
        $message = DB::table('wechat_kf_messages')
            ->where('id', $this->info['messageId'])
            ->first();
        if(!$message) {
            myLog('KFMessageSend')->error('消息不存在', [
                'info' => $this->info,
                'traceInfo' => $this->traceContext->getTraceInfo(),
            ]);
            return false;
        }
        if(1 != $message->message_type) {
            myLog('KFMessageSend')->error('不支持的消息类型', [
                'info' => $this->info,
                'traceInfo' => $this->traceContext->getTraceInfo(),
            ]);
            return false;
        }
        return $message;
    }

    /**
     *
     * @param $officialAccount Application
     */
    private function getUserOpenids($officialAccount, $next_openid) {
        $result = $officialAccount->getClient()
            ->get('cgi-bin/user/get', [
                'query' => [
                    'next_openid' => $next_openid,
                ]
            ])->toArray();
        if(0 != ($result['errcode'] ?? 0)) {
            return false;
        }
        return $result;
    }

    /**
     * 获取公众号调用对象
     * @param $gzh
     * @return \EasyWeChat\OfficialAccount\Application
     * @throws \EasyWeChat\Kernel\Exceptions\BadResponseException
     * @throws \EasyWeChat\Kernel\Exceptions\HttpException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \Psr\SimpleCache\InvalidArgumentException
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    private function getOfficialAccount($gzh) {
        try{
            return WechatPlatform::buildApplication($gzh)
                ->getOfficialAccountWithRefreshToken($gzh->authorizer_appid, $gzh->authorizer_refresh_token);
        } catch (\Throwable $exception) {
            myLog('KFMessageSend')->error('获取公众号调用对象失败', [
                'exceptionMessage' => $exception->getMessage(),
                'traceInfo' => $this->traceContext->getTraceInfo()
            ]);
            return false;
        }
    }

    private function getGZH() {
        $gzh = DB::table('wechat_authorization_infos as a')
            ->join('wechat_open_platform_infos as o', 'a.component_appid', 'o.app_id')
            ->where([
                ['a.id', '=', $this->info['gzhId']],
                ['a.is_enabled', '=', 1],
                ['b.is_enabled', '=', 1]
            ])->select('a.authorizer_appid', 'a.authorizer_refresh_token', 'b.app_id', 'b.secret', 'b.token', 'b.aes_key')
            ->first();
        if(!$gzh) {
            myLog('KFMessageSend')->error('公众号不可用', [
                'traceInfo' => $this->traceContext->getTraceInfo(),
            ]);
        }
        return $gzh;
    }
}