<?php

namespace Weasy\External\Api;

use Weasy\External\Utils\Error\HttpError;
use Weasy\External\Utils\HttpUtils;
use Weasy\External\Utils\Error\InternalError;
use Weasy\External\Utils\Error\NetWorkError;
use Weasy\External\Utils\Error\ParameterError;
use Weasy\External\Utils\Error\QyApiError;
use Weasy\External\Utils\Utils;

abstract class API
{
    public $rspJson = null;

    public $rspRawStr = null;

    const USER_CREATE = '/cgi-bin/user/create?access_token=ACCESS_TOKEN';
    const USER_GET = '/cgi-bin/user/get?access_token=ACCESS_TOKEN';
    const USER_UPDATE = '/cgi-bin/user/update?access_token=ACCESS_TOKEN';
    const USER_DELETE = '/cgi-bin/user/delete?access_token=ACCESS_TOKEN';
    const USER_BATCH_DELETE = '/cgi-bin/user/batchdelete?access_token=ACCESS_TOKEN';
    const USER_SIMPLE_LIST = '/cgi-bin/user/simplelist?access_token=ACCESS_TOKEN';
    const USER_LIST = '/cgi-bin/user/list?access_token=ACCESS_TOKEN';
    const USER_ID_TO_OPENID = '/cgi-bin/user/convert_to_openid?access_token=ACCESS_TOKEN';
    const OPENID_TO_USER_ID = '/cgi-bin/user/convert_to_userid?access_token=ACCESS_TOKEN';
    const USER_AUTH_SUCCESS = '/cgi-bin/user/authsucc?access_token=ACCESS_TOKEN';

    const MESSAGE_SEND = '/cgi-bin/message/send?access_token=ACCESS_TOKEN';

    const MEDIA_GET = '/cgi-bin/media/get?access_token=ACCESS_TOKEN';

    const EXTERNAL_CONTACT_SEND_WELCOME_MSG = '/cgi-bin/externalcontact/send_welcome_msg?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_GET_FOLLOW_USER_LIST = '/cgi-bin/externalcontact/get_follow_user_list?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_LIST = '/cgi-bin/externalcontact/list?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_GET = '/cgi-bin/externalcontact/get?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_BATCH = '/cgi-bin/externalcontact/batch/get_by_user?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_REMARK = '/cgi-bin/externalcontact/remark?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_GROUP_CHAT_LIST = '/cgi-bin/externalcontact/groupchat/list?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_GROUP_CHAT_GET = '/cgi-bin/externalcontact/groupchat/get?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_BEHAVIOR_DATA = '/cgi-bin/externalcontact/get_user_behavior_data?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_GROUP_CHAT_STATISTIC = '/cgi-bin/externalcontact/groupchat/statistic?access_token=ACCESS_TOKEN';
    const EXTERNAL_CONTACT_ADD_MESSAGE_TEMPLATE = '/cgi-bin/externalcontact/add_msg_template?access_token=ACCESS_TOKEN';

    /**
     * 获取访问令牌
     *
     * @return string
     */
    protected function GetAccessToken()
    {
    }

    /**
     * 刷新访问令牌
     *
     * @return string
     */
    protected function RefreshAccessToken()
    {
    }

    /**
     * 获取套件访问令牌
     *
     * @return string
     */
    protected function GetSuiteAccessToken()
    {
    }

    /**
     * 刷新套件访问令牌
     *
     * @return string
     */
    protected function RefreshSuiteAccessToken()
    {
    }

    /**
     * 获取提供商访问令牌
     *
     * @return string
     */
    protected function GetProviderAccessToken()
    {
    }

    /**
     * 刷新提供商访问令牌
     *
     * @return string
     */
    protected function RefreshProviderAccessToken()
    {
    }


    /**
     * Http呼叫
     *
     * @param $url
     * @param $method
     * @param $args
     *
     * @throws HttpError
     * @throws InternalError
     * @throws NetWorkError
     * @throws ParameterError
     * @throws QyApiError
     */
    protected function _HttpCall($url, $method, $args)
    {
        if ('POST' == $method) {
            $url = HttpUtils::MakeUrl($url);
            $this->_HttpPostParseToJson($url, $args);
            $this->_CheckErrCode();
        } else if ('GET' == $method) {
            if (count($args) > 0) {
                foreach ($args as $key => $value) {
                    if ($value == null) continue;
                    if (strpos($url, '?')) {
                        $url .= ('&' . $key . '=' . $value);
                    } else {
                        $url .= ('?' . $key . '=' . $value);
                    }
                }
            }
            $url = HttpUtils::MakeUrl($url);
            $this->_HttpGetParseToJson($url);
            $this->_CheckErrCode();
        } else {
            throw new QyApiError('wrong method');
        }
    }

    /**
     * Http获取解析给Json
     *
     * @param      $url
     * @param bool $refreshTokenWhenExpired
     *
     * @return bool|string|null
     * @throws QyApiError
     * @throws HttpError
     * @throws InternalError
     * @throws NetWorkError
     */
    protected function _HttpGetParseToJson($url, $refreshTokenWhenExpired = true)
    {
        $retryCnt = 0;
        $this->rspJson = null;
        $this->rspRawStr = null;

        while ($retryCnt < 2) {
            $tokenType = null;
            $realUrl = $url;

            if (strpos($url, "SUITE_ACCESS_TOKEN")) {
                $token = $this->GetSuiteAccessToken();
                $realUrl = str_replace("SUITE_ACCESS_TOKEN", $token, $url);
                $tokenType = "SUITE_ACCESS_TOKEN";
            } else if (strpos($url, "PROVIDER_ACCESS_TOKEN")) {
                $token = $this->GetProviderAccessToken();
                $realUrl = str_replace("PROVIDER_ACCESS_TOKEN", $token, $url);
                $tokenType = "PROVIDER_ACCESS_TOKEN";
            } else if (strpos($url, "ACCESS_TOKEN")) {
                $token = $this->GetAccessToken();
                $realUrl = str_replace("ACCESS_TOKEN", $token, $url);
                $tokenType = "ACCESS_TOKEN";
            } else {
                $tokenType = "NO_TOKEN";
            }

            $this->rspRawStr = HttpUtils::httpGet($realUrl);

            if (!Utils::notEmptyStr($this->rspRawStr)) throw new QyApiError("empty response");
            //
            $this->rspJson = json_decode($this->rspRawStr, true/*to array*/);
            if (strpos($this->rspRawStr, "errcode") !== false) {
                $errCode = Utils::arrayGet($this->rspJson, "errcode");
                if ($errCode == 40014 || $errCode == 42001 || $errCode == 42007 || $errCode == 42009) { // token expired
                    if ("NO_TOKEN" != $tokenType && true == $refreshTokenWhenExpired) {
                        if ("ACCESS_TOKEN" == $tokenType) {
                            $this->RefreshAccessToken();
                        } else if ("SUITE_ACCESS_TOKEN" == $tokenType) {
                            $this->RefreshSuiteAccessToken();
                        } else if ("PROVIDER_ACCESS_TOKEN" == $tokenType) {
                            $this->RefreshProviderAccessToken();
                        }
                        $retryCnt += 1;
                        continue;
                    }
                }
            }
            return $this->rspRawStr;
        }
        return null;
    }

    /**
     * Http发布解析到Json
     *
     * @param      $url
     * @param      $args
     * @param bool $refreshTokenWhenExpired
     * @param bool $isPostFile
     *
     * @return mixed
     * @throws HttpError
     * @throws InternalError
     * @throws NetWorkError
     * @throws QyApiError
     */
    protected function _HttpPostParseToJson($url, $args, $refreshTokenWhenExpired = true, $isPostFile = false)
    {
        $postData = $args;
        if (!$isPostFile) {
            if (!is_string($args)) {
                $postData = HttpUtils::Array2Json($args);
            }
        }
        $this->rspJson = null;
        $this->rspRawStr = null;

        $retryCnt = 0;
        while ($retryCnt < 2) {
            $tokenType = null;
            $realUrl = $url;

            if (strpos($url, "SUITE_ACCESS_TOKEN")) {
                $token = $this->GetSuiteAccessToken();
                $realUrl = str_replace("SUITE_ACCESS_TOKEN", $token, $url);
                $tokenType = "SUITE_ACCESS_TOKEN";
            } else if (strpos($url, "PROVIDER_ACCESS_TOKEN")) {
                $token = $this->GetProviderAccessToken();
                $realUrl = str_replace("PROVIDER_ACCESS_TOKEN", $token, $url);
                $tokenType = "PROVIDER_ACCESS_TOKEN";
            } else if (strpos($url, "ACCESS_TOKEN")) {
                $token = $this->GetAccessToken();
                $realUrl = str_replace("ACCESS_TOKEN", $token, $url);
                $tokenType = "ACCESS_TOKEN";
            } else {
                $tokenType = "NO_TOKEN";
            }


            $this->rspRawStr = HttpUtils::httpPost($realUrl, $postData);

            if (!Utils::notEmptyStr($this->rspRawStr)) throw new QyApiError("empty response");

            $json = json_decode($this->rspRawStr, true/*to array*/);
            $this->rspJson = $json;

            $errCode = Utils::arrayGet($this->rspJson, "errcode");
            if ($errCode == 40014 || $errCode == 42001 || $errCode == 42007 || $errCode == 42009) { // token expired
                if ("NO_TOKEN" != $tokenType && true == $refreshTokenWhenExpired) {
                    if ("ACCESS_TOKEN" == $tokenType) {
                        $this->RefreshAccessToken();
                    } else if ("SUITE_ACCESS_TOKEN" == $tokenType) {
                        $this->RefreshSuiteAccessToken();
                    } else if ("PROVIDER_ACCESS_TOKEN" == $tokenType) {
                        $this->RefreshProviderAccessToken();
                    }
                    $retryCnt += 1;
                    continue;
                }
            }

            return $json;
        }
        return null;
    }


    /**
     * 检查错误代码
     *
     * @throws ParameterError
     * @throws QyApiError
     */
    protected function _CheckErrCode()
    {
        $rsp = $this->rspJson;
        $raw = $this->rspRawStr;
        if (is_null($rsp))
            return;

        if (!is_array($rsp))
            throw new ParameterError("invalid type " . gettype($rsp));
        if (!array_key_exists("errcode", $rsp)) {
            return;
        }
        $errCode = $rsp["errcode"];
        if (!is_int($errCode))
            throw new QyApiError("invalid errcode type " . gettype($errCode) . ":" . $raw);
        if ($errCode != 0)
            throw new QyApiError("response error:" . $raw);
    }

}