<?php

declare(strict_types=1);

namespace App\Amqp\Consumer;

use App\Amqp\Producer\SendSmsMessageProducer;
use App\Constants\HttpEnumCode;
use App\Exception\BusinessException;
use App\Model\LogMessagePush;
use App\Model\SubTemplate;
use App\Model\User;
use App\Model\UserDoctor;
use App\Model\UserPatient;
use App\Services\UserService;
use App\Utils\Log;
use Extend\Wechat\Wechat;
use Hyperf\Amqp\Producer;
use Hyperf\Amqp\Result;
use Hyperf\Amqp\Annotation\Consumer;
use Hyperf\Amqp\Message\ConsumerMessage;
use Hyperf\DbConnection\Db;
use PhpAmqpLib\Message\AMQPMessage;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

/**
 * 订阅消息推送
 * 每条消息都需携带发送短信参数，用户可能拒绝接受订阅消息
 */
#[Consumer(exchange: 'amqp.direct', routingKey: 'SendSubMessage', queue: 'send.sub.message.queue', nums: 1)]
class SendSubMessageConsumer extends ConsumerMessage
{
    /**
     * @param $data
     * @param AMQPMessage $message
     * @return string
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ClientExceptionInterface
     * @throws DecodingExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     */
    public function consumeMessage($data, AMQPMessage $message): string
    {
        Log::getInstance()->info("开始执行 订阅消息推送 队列：" . json_encode($data, JSON_UNESCAPED_UNICODE));

        // 获取被推送用户信息
        $params = array();
        $params['user_id'] = $data['sub_data']['push_user_id'];
        $user = User::getOne($params);
        if (empty($user)){
            Log::getInstance()->error("订阅消息推送执行失败：未查询到被推送用户信息");
            return Result::ACK;
        }

        try {
            // 验证发送参数
            if (empty($data['sub_data']['params']['data'])){
                Log::getInstance()->error("订阅消息推送执行失败：无推送数据");
                $this->saveErrorPushLog($user['user_type'],$data['sub_data'],"无推送数据");

                return Result::DROP;
            }

            // 处理发送参数
            $send_data = array();
            foreach ($data['sub_data']['params']['data'] as $key => $item){
                $send_data[$key] = [
                    "value" => $item
                ];
            }

            if (empty($send_data)){
                Log::getInstance()->error("订阅消息推送执行失败：发送参数处理失败");
                $this->saveErrorPushLog($user['user_type'],$data['sub_data'],"发送参数处理失败");

                return Result::DROP;
            }

            // 获取open_id
            $UserService = new UserService();
            $open_id = $UserService->getOpenIdWithUserId($user['user_id'],$user['user_type']);
            if (empty($open_id)){
                Log::getInstance()->error("订阅消息推送执行失败：未获取到被推送用户open_id");
                $this->saveErrorPushLog($user['user_type'],$data['sub_data'],"未获取到被推送用户open_id");

                // 执行发送短信步骤
                $this->addSendSmsMessageQueue($data['sms_data']);

                return Result::ACK;
            }

            // 处理发送环境
//            $miniprogram_state = "developer";
            $miniprogram_state = "trial";
            if (env("APP_ENV") == "prod"){
                $miniprogram_state = "formal";
            }

            $options = [
                "template_id" => $data['sub_data']['wx_template_id'],
                "page" => $data['sub_data']['params']['page'] ?: "",
                "touser" => $open_id,
                "data" => $send_data,
                "miniprogram_state" => $miniprogram_state,
                "lang" => "zh_CN",
            ];

            // 发起推送
            $Wechat = new Wechat($user['user_type']);
            $result = $Wechat->sendSubscribeMessage($options);
            if (empty($result)){
                Log::getInstance()->error("订阅消息推送执行失败：推送失败");
                $this->saveErrorPushLog($user['user_type'],$data['sub_data'],"推送失败");
            }

            if (!isset($result['errcode'])){
                Log::getInstance()->error("订阅消息推送执行失败：推送失败,返回值错误" . json_encode($result,JSON_UNESCAPED_UNICODE));
                $this->saveErrorPushLog($user['user_type'],$data['sub_data'],"推送失败,返回值错误");
            }

            if ($result['errcode'] == 43101){
                Log::getInstance()->error("订阅消息推送执行失败：用户拒绝接收消息");
                $this->saveErrorPushLog($user['user_type'],$data['sub_data'],"用户拒绝接收消息");

                // 执行发送短信步骤
                $this->addSendSmsMessageQueue($data['sms_data']);

                return Result::ACK;
            }

            Log::getInstance()->info("订阅消息推送执行成功");
            $this->saveSuccessPushLog($user['user_type'],$data['sub_data']);

        } catch (\Exception $e) {
            Log::getInstance()->error("订阅消息推送执行失败：" . $e->getMessage());
            $this->saveErrorPushLog($user['user_type'],$data['sub_data'],$e->getMessage());
        }

        return Result::ACK;
    }

    /**
     * 记录成功日志
     * @param string|int $user_type 用户类型
     * @param array $sub_data 需发送的订阅消息数据
     * @return void
     */
    private function saveSuccessPushLog(string|int $user_type,array $sub_data): void
    {
        $data = array();
        $data['to_user_id'] = $sub_data['push_user_id'];
        $data['user_type'] = $user_type;
        $data['type'] = 2;
        $data['status'] = 1;
        $data['content'] = json_encode($sub_data['params'],JSON_UNESCAPED_UNICODE);
        $log_message_push = LogMessagePush::addLogMessagePush($data);
        if (empty($log_message_push)){
            Log::getInstance()->error("订阅消息推送成功，增加推送日志失败：" . json_encode($data,JSON_UNESCAPED_UNICODE));
        }
    }

    /**
     * 记录成功日志
     * @param string|int $user_type 用户类型
     * @param array $sub_data 需发送的订阅消息数据
     * @param string $fail_reason 失败原因
     * @return void
     */
    private function saveErrorPushLog(string|int $user_type,array $sub_data,string $fail_reason): void
    {
        $data = array();
        $data['to_user_id'] = $sub_data['push_user_id'];
        $data['user_type'] = $user_type;
        $data['type'] = 2;
        $data['status'] = 2;
        $data['fail_reason'] = $fail_reason ?: "";
        $data['content'] = json_encode($sub_data['params'],JSON_UNESCAPED_UNICODE);
        $log_message_push = LogMessagePush::addLogMessagePush($data);
        if (empty($log_message_push)){
            Log::getInstance()->error("订阅消息推送成功，增加推送日志失败：" . json_encode($data,JSON_UNESCAPED_UNICODE));
        }
    }

    /**
     * 添加短信队列
     * 接收所有异常
     * 只返回true，失败情况不处理
     * @param array $sms_data 短信发送参数
     * @return bool
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    private function addSendSmsMessageQueue(array $sms_data): bool
    {
        try {
            if (empty($sms_data)){
                // 无需发送短信
                Log::getInstance()->info("无数据需添加短信推送队列");
                return true;
            }

            Log::getInstance()->info("添加短信推送队列");

            // 增加至发送短信队列
            $message = new SendSmsMessageProducer($sms_data);
            $producer = $this->container->get(Producer::class);
            $res = $producer->produce($message);
            if (!$res) {
                Log::getInstance()->error("添加短信推送队列失败" . json_encode($sms_data,JSON_UNESCAPED_UNICODE));
            }

        } catch (\Exception $e) {
            Log::getInstance()->error("添加短信推送队列失败" . $e->getMessage());
        }

        Log::getInstance()->info("添加短信推送队列成功");

        return true;
    }

    /**
     * 获取数据库订阅消息模版
     * 接收所有异常
     * 异常记录log，不进行处理
     * @param string|int $user_type 用户类型
     * @param string $template_title 模版名称
     * @return array
     * @throws ClientExceptionInterface
     * @throws ContainerExceptionInterface
     * @throws DecodingExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     */
    public function getSubTemplate(string|int $user_type, string $template_title): array
    {
        try {
            Log::getInstance()->info("获取数据库消息订阅模版");

            // 获取订阅消息模版数据
            $params = array();
            $params['client_type'] = $user_type; // 客户端类型（1:患者端 2:医师端 3:药师端）
            $params['template_title'] = $template_title;
            $sub_template = SubTemplate::getOne($params);
            if (empty($sub_template)){
                // 下载消息模版数据
                Log::getInstance()->info("消息模版：" . $template_title . " 在数据库中为空");
                $this->downSubTemplate($user_type);

                // 重新获取订阅消息模版
                $params = array();
                $params['client_type'] = $user_type; // 客户端类型（1:患者端 2:医师端 3:药师端）
                $params['template_title'] = $template_title;
                $sub_template = SubTemplate::getOne($params);
            }
        } catch (\Exception $e) {
            Log::getInstance()->error("获取数据库消息订阅模版失败：" . $e->getMessage());
        }

        if (empty($sub_template)){
            return [];
        }else{
            return $sub_template->toArray();
        }
    }

    /**
     * 下载消息订阅模版
     * 接收所有异常
     * 只返回true，失败情况不处理
     * @param string|int $user_type
     * @return bool
     * @throws ClientExceptionInterface
     * @throws ContainerExceptionInterface
     * @throws DecodingExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     */
    private function downSubTemplate(string|int $user_type): bool
    {
        try {
            Log::getInstance()->info("下载微信消息订阅模版");

            $weChat = new Wechat($user_type);
            $result = $weChat->getTemplate();
            if (empty($result)){
                return true;
            }

            $template = json_decode($result, true);

            foreach ($template['data'] as $item) {
                $params = array();
                $params['wx_template_id'] = $item['priTmplId'];
                $sub_template = SubTemplate::getOne($params);
                if (empty($sub_template)) {
                    // 新增模版
                    $data = array();
                    $data['client_type'] = 1;
                    $data['wx_template_id'] = $item['priTmplId'];
                    $data['template_title'] = $item['title'];
                    $data['template_type'] = $item['type'];
                    $data['template_content'] = $item['content'];
                    $sub_template = SubTemplate::addSubTemplate($data);
                    if (empty($sub_template)) {
                        // 此处添加失败不做处理，记录log，
                        Log::getInstance()->error("下载微信消息订阅模版失败：" . json_encode($data,JSON_UNESCAPED_UNICODE));
                    }
                }
            }

        } catch (\Exception $e) {
            Log::getInstance()->error("下载微信消息订阅模版失败：" . $e->getMessage());
        }

        Log::getInstance()->info("下载微信消息订阅模版成功");
        return true;
    }
}
