<?php

use Aliyun\Api\Msg\Request\V20170525\QueryTokenForMnsQueueRequest;
use Aliyun\Core\Config;
use AliyunMNS\Client;
use Aliyun\Core\Profile\DefaultProfile;
use Aliyun\Core\DefaultAcsClient;
use Aliyun\Core\Exception\ClientException;
use Aliyun\Core\Exception\ServerException;

// 加载区域结点配置
Config::load();

/**
 * 接收云通信消息的临时token
 *
 * @property array tokenMap
 * @property int bufferTime 过期时间小于2分钟则重新获取，防止服务器时间误差
 * @property string mnsAccountEndpoint
 * @property \Aliyun\Core\DefaultAcsClient acsClient
 */
class TokenGetterForAlicom
{
    /**
     * TokenGetterForAlicom constructor.
     *
     * @param string $accountId AccountId
     * @param string $accessKeyId AccessKeyId
     * @param string $accessKeySecret AccessKeySecret
     */
    public function __construct($accountId, $accessKeyId, $accessKeySecret)
    {
        $endpointName = "cn-hangzhou";
        $regionId = "cn-hangzhou";
        $productName = "Dybaseapi";
        $domain = "dybaseapi.aliyuncs.com";

        $this->tokenMap = [];
        $this->bufferTime = 2 * 60;
        DefaultProfile::addEndpoint($endpointName, $regionId, $productName, $domain);
        $profile = DefaultProfile::getProfile($regionId, $accessKeyId, $accessKeySecret);
        $this->acsClient = new DefaultAcsClient($profile);
        $this->mnsAccountEndpoint = $this->getAccountEndpoint($accountId, $regionId);
    }


    /**
     * 配置获取AccountEndPoint
     *
     * @param string $accountId AccountId
     * @param string $region Region
     * @param bool $secure 是否启用https
     * @param bool $internal
     * @param bool $vpc
     * @return string <ul>
     * <li>http(s)://{AccountId}.mns.cn-beijing.aliyuncs.com</li>
     * <li>http(s)://{AccountId}.mns.cn-beijing-internal.aliyuncs.com</li>
     * <li>http(s)://{AccountId}.mns.cn-beijing-internal-vpc.aliyuncs.com</li>
     * </ul>
     */
    private function getAccountEndpoint($accountId, $region, $secure=false, $internal=false, $vpc=false)
    {
        $protocol = $secure ? 'https' : 'http';
        $realRegion = $region;
        if ($internal) {
            $realRegion .= '-internal';
        }

        if ($vpc) {
            $realRegion .= '-vpc';
        }

        return "$protocol://$accountId.mns.$realRegion.aliyuncs.com";
    }


    /**
     * 远程取Token
     *
     * @param string $messageType 消息订阅类型 SmsReport | SmsUp
     * @return TokenForAlicom|bool
     */
    public function getTokenFromRemote($messageType, $queueName)
    {
        $request = new QueryTokenForMnsQueueRequest();
        $request->setMessageType($messageType);
        $request->setQueueName($queueName);

        try {
            $response = $this->acsClient->getAcsResponse($request);
            // print_r($response);
            $tokenForAlicom = new TokenForAlicom();
            $tokenForAlicom->setMessageType($messageType);
            $tokenForAlicom->setToken($response->MessageTokenDTO->SecurityToken);
            $tokenForAlicom->setTempAccessKey($response->MessageTokenDTO->AccessKeyId);
            $tokenForAlicom->setTempSecret($response->MessageTokenDTO->AccessKeySecret);
            $tokenForAlicom->setExpireTime($response->MessageTokenDTO->ExpireTime);
            // print_r($tokenForAlicom);
            return $tokenForAlicom;
        }
        catch (ServerException $e) {
            print_r($e->getErrorCode());
            print_r($e->getErrorMessage());
        }
        catch (ClientException $e) {
            print_r($e->getErrorCode());
            print_r($e->getErrorMessage());
        }
        return false;
    }

    /**
     * 先从tokenMap中取，如果不存在则远程取Token并存入tokenMap
     *
     * @param string $messageType 消息订阅类型 SmsReport | SmsUp
     * @param string $queueName 在云通信页面开通相应业务消息后，就能在页面上获得对应的queueName<br/>(e.g. Alicom-Queue-xxxxxx-SmsReport)
     * @return TokenForAlicom|bool
     */
    public function getTokenByMessageType($messageType, $queueName)
    {
        $tokenForAlicom = null;
        if(isset($this->tokenMap[$messageType])) {
            $tokenForAlicom = $this->tokenMap[$messageType];
        }

        if(null == $tokenForAlicom || strtotime($tokenForAlicom->getExpireTime()) - time() > $this->bufferTime)
        {
            $tokenForAlicom =$this->getTokenFromRemote($messageType, $queueName);

            $client = new Client(
                $this->mnsAccountEndpoint,
                $tokenForAlicom->getTempAccessKey(),
                $tokenForAlicom->getTempSecret(),
                $tokenForAlicom->getToken()
            );

            $tokenForAlicom->setClient($client);
            $tokenForAlicom->setQueue($queueName);

            $this->tokenMap[$messageType] = $tokenForAlicom;
        }

        return $tokenForAlicom;
    }
}