<?php

namespace Extend\Wechat;

use App\Constants\HttpEnumCode;
use App\Exception\BusinessException;
use EasyWeChat\Kernel\Exceptions\BadResponseException;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
use EasyWeChat\MiniApp\Application;
use Hyperf\Utils\ApplicationContext;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\SimpleCache\CacheInterface;
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;

/**
 * 微信类
 */
class Wechat
{

    protected Application $app;

    protected array $config;// 系统配置

    /**
     * @param string|int $user_type
     */
    public function __construct(string|int $user_type)
    {
        if ($user_type == 1){
            $this->config = config("we_chat.applets.patient");
        }elseif ($user_type == 2){
            $this->config = config("we_chat.applets.doctor");
        }elseif ($user_type == 3){
            $this->config = config("we_chat.applets.pharmacist");
        }

        if (empty($this->config)){
            throw new BusinessException("系统配置错误", HttpEnumCode::SERVER_ERROR);
        }
    }

    /**
     * 获取wechat类
     * @param string $appId
     * @param string $appSecret
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function createApp(string $appId, string $appSecret): void
    {
        $config = array(
            "app_id" => $appId,
            "secret" => $appSecret,
            "http" => [
                'throw'  => false, // 状态码非 200、300 时是否抛出异常，默认为开启
                'timeout' => 5.0,
                // 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用，根据不同的模块配置不同的 uri

                'retry' => true, // 使用默认重试配置
                //  'retry' => [
                //      // 仅以下状态码重试
                //      'http_codes' => [429, 500]
                //       // 最大重试次数
                //      'max_retries' => 3,
                //      // 请求间隔 (毫秒)
                //      'delay' => 1000,
                //      // 如果设置，每次重试的等待时间都会增加这个系数
                //      // (例如. 首次:1000ms; 第二次: 3 * 1000ms; etc.)
                //      'multiplier' => 3
                //  ],
            ]
        );

        try {
            // 获取WeChat客户端
            $this->app = new Application($config);

            // 替换缓存
            $this->app->setCache(ApplicationContext::getContainer()->get(CacheInterface::class));

        } catch (InvalidArgumentException $e) {
            throw new BusinessException('实例化EasyWeChat类失败:' . $e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }

    }

    /**
     * 获取wechat客户端
     * @param string $appId
     * @param string $appSecret
     * @return AccessTokenAwareClient
     */
    public function createClient(string $appId, string $appSecret): AccessTokenAwareClient
    {
        try {
            // 获取WeChat客户端
            $this->createApp($appId, $appSecret);

            return $this->app->getClient();

        } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
            throw new BusinessException('实例化EasyWeChat类失败:' . $e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }
    }

    /**
     * 根据 jsCode 获取用户 session 信息
     * @param string $code code
     * @return array
     * @throws ClientExceptionInterface
     * @throws ContainerExceptionInterface
     * @throws DecodingExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     */
    public function codeToSession(string $code): array
    {
        try {
            $this->createApp($this->config['app_id'],$this->config['secret']);
            $utils = $this->app->getUtils();
            return $utils->codeToSession($code);
        } catch (\Exception $e) {
            throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }
    }

    /**
     * 解密微信会话信息
     * @param string $sessionKey 会话密钥
     * @param string $iv 加密算法的初始向量
     * @param string $encryptedData 用户信息的加密数据
     * @return array
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function decryptSession(string $sessionKey, string $iv, string $encryptedData): array
    {
        try {
            $this->createApp($this->config['app_id'],$this->config['secret']);

            $utils = $this->app->getUtils();
            return $utils->decryptSession($sessionKey, $iv, $encryptedData);
        } catch (\Exception $e) {
            throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }
    }

    /**
     * 获取 access_token
     * @return string
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function getAccessToken(): string
    {
        try {
            $this->createApp($this->config['app_id'],$this->config['secret']);

            $accessToken = $this->app->getAccessToken();
            return $accessToken->getToken();
        } catch (\Exception $e) {
            throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }
    }

    /**
     * 获取手机号
     * @param string $code
     * @return array
     * @throws ClientExceptionInterface
     * @throws DecodingExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface|BadResponseException
     */
    public function getPhone(string $code): array
    {
        try {
            $this->createApp($this->config['app_id'],$this->config['secret']);

            $client = $this->createClient($this->config['app_id'],$this->config['secret']);

            $options = [
                "code" => $code,
            ];

            $response = $client->postJson('wxa/business/getuserphonenumber', $options);

            if ($response->isFailed()) {
                // 出错了，处理异常
                $result = $response->toArray();
                if(empty($result)){
                    // 返回值为空
                    return [];
                }
                if (isset($result['errcode'])){
                    if ($result['errcode'] == "40029"){
                        // code过期
                        return [];
//                        throw new BusinessException( HttpEnumCode::getMessage(HttpEnumCode::GET_WX_ERROR),HttpEnumCode::GET_WX_ERROR);
                    }
                }
                return [];
            }

            return $response->toArray(false);
        } catch (\Exception $e) {
            throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }
    }

    /**
     * 获取分享二维码
     * @throws NotFoundExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ContainerExceptionInterface
     * @throws DecodingExceptionInterface
     * @throws ClientExceptionInterface
     * @throws TransportExceptionInterface
     * @throws ServerExceptionInterface
     */
    public function getUnlimitedQRCode(array $options): array|string
    {
        try {
            $this->createApp($this->config['app_id'], $this->config['secret']);

            $client = $this->createClient($this->config['app_id'], $this->config['secret']);

            $response = $client->postJson('wxa/getwxacodeunlimit', $options);

            if ($response->isFailed()) {
                // 出错了，处理异常
                $result = $response->toArray();
                if(empty($result)){
                    // 返回值为空
                    return [];
                }
                if (isset($result['errcode'])){
                    throw new BusinessException();
                }
                return [];
            }

            return $response->getContent(true);

        } catch (\Exception $e) {
            throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }
    }

    /**
     * 获取当前账号下订阅消息个人模版列表
     * @return array|string
     * @throws ClientExceptionInterface
     * @throws ContainerExceptionInterface
     * @throws DecodingExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     */
    public function getTemplate(): array|string
    {
        try {
            $this->createApp($this->config['app_id'], $this->config['secret']);

            $client = $this->createClient($this->config['app_id'], $this->config['secret']);

            $options = [];
            $response = $client->get('wxaapi/newtmpl/gettemplate', $options);
            if ($response->isFailed()) {
                // 出错了，处理异常
                $result = $response->toArray();
                if(empty($result)){
                    // 返回值为空
                    return [];
                }
                if (isset($result['errcode'])){
                    throw new BusinessException();
                }
                return [];
            }

            return $response->getContent(false);
        } catch (\Exception $e) {
            throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }
    }

    /**
     * 发送订阅消息
     * @throws NotFoundExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ContainerExceptionInterface
     * @throws DecodingExceptionInterface
     * @throws ClientExceptionInterface
     * @throws TransportExceptionInterface
     * @throws ServerExceptionInterface
     */
    public function sendSubscribeMessage(array $options): array|string
    {
        try {
            $this->createApp($this->config['app_id'], $this->config['secret']);

            $client = $this->createClient($this->config['app_id'], $this->config['secret']);

            $response = $client->postJson('cgi-bin/message/subscribe/send', $options);

            if ($response->isFailed()) {
                // 出错了，处理异常
                $result = $response->toArray();
                if(empty($result)){
                    // 返回值为空
                    throw new BusinessException("返回值为空");
                }
                if (isset($result['errcode'])){
                    if ($result['errcode'] == 43101){
                        // 用户拒绝接收消息
                        return $result;

                    }
                    throw new BusinessException($result['errmsg']);
                }
                throw new BusinessException();
            }

            return $response->toArray();
        } catch (\Exception $e) {
            throw new BusinessException($e->getMessage(), HttpEnumCode::SERVER_ERROR);
        }
    }
}