<?php

namespace app\adminapi\controller\v1\merchant;

use app\adminapi\controller\AuthController;
use app\lib\wx_encode\WXBizMsgCrypt;
use app\models\merchant\MerchantCodeAudit;
use app\models\merchant\MerchantMiniprogram;
use app\models\merchant\Merchant;
use app\Request;
use crmeb\basic\BaseModel;
use crmeb\services\CacheService;
use crmeb\services\UtilService;
use Psr\SimpleCache\InvalidArgumentException;
use SimpleXMLElement;
use think\db\exception\DbException;
use think\Exception;
use think\facade\Log;

class Open extends AuthController
{

    protected $encodingAesKey = '';    //第三方平台解密key
    protected $appId = '';             //第三方平台appid
    protected $appSecret = '';         //第三方平台appSecret
    protected $token = '';             //第三方平台消息验证token


    public function __construct(\think\facade\App $app, $escape = false)
    {
        if (!$escape) {
            parent::__construct($app);
        } else {
            $this->app = $app;
            $this->request = app('request');
            $this->initialize($escape);
        }
    }

    public function initialize($escape = false)
    {
        if (!($this->request->action() == 'wxTicketCallback') && !($this->request->action() == 'wxCallback') && !$escape) {
            parent::initialize();
        }
        $this->encodingAesKey = config('third.encodingAesKey');
        $this->appId = config('third.appId');
        $this->appSecret = config('third.appSecret');
        $this->token = config('third.token');
    }

    /**
     * 票据获取接口
     * @param Request $request
     * @throws InvalidArgumentException
     */
    public function wxTicketCallback(Request $request)
    {
        $msg = '';
        $timeStamp = $request->param('timestamp', '');
        $nonce = $request->param('nonce', '');
        $msg_sign = $request->param('msg_signature', '');
        $encryptMsg = file_get_contents('php://input');
        $pc = new WXBizMsgCrypt($this->token, $this->encodingAesKey, $this->appId);
        $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $encryptMsg, $msg);
        if ($errCode == 0) {
            $data = $this->_xmlToArr($msg);
            switch ($data['InfoType']) {
                case'component_verify_ticket':
                    //接收验证票据
                    CacheService::redisHandler()->set('component_verify_ticket', $data['ComponentVerifyTicket']);
                    break;
                case 'unauthorized';
                    //todo 发送授权取消通知
                    break;
                case 'updateauthorized':
                    //todo 发送授权更新通知
                    break;
                case 'authorized':
                    //todo 发送授权成功通知
                    break;
                default:
                    break;
            }
            echo 'success';
        } else {
            echo '解密失败' . $errCode;
        }
    }


    /**
     * 消息与事件接受接口
     * @param $appid
     * @param Request $request
     */
    public function wxCallback($appid, Request $request)
    {
        $msg = '';
        $timeStamp = $request->param('timestamp', '');
        $nonce = $request->param('nonce', '');
        $msg_sign = $request->param('msg_signature', '');
        $encryptMsg = file_get_contents('php://input');
        $pc = new WXBizMsgCrypt($this->token, $this->encodingAesKey, $this->appId);
        Log::error('77777777777');
        Log::error($msg_sign);
        Log::error($timeStamp);
        Log::error($nonce);
        Log::error($encryptMsg);
        Log::error($msg);
        $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $encryptMsg, $msg);
        Log::error($errCode);
        Log::error($this->_xmlToArr($msg));
        @file_put_contents('call_back_log.txt', '[' . date('Y-m-d H:i:s') . ']' . json_encode($data) . PHP_EOL, FILE_APPEND);
        if ($errCode == 0) {
            $data = $this->_xmlToArr($msg);
            @file_put_contents('call_back_log.txt', '[' . date('Y-m-d H:i:s') . ']' . json_encode($data) . PHP_EOL, FILE_APPEND);
            $mer_id = MerchantMiniprogram::where(['appid' => $appid])->value('mer_id');
            BaseModel::beginTrans();
            try {
                switch ($data['Event']) {
                    case'weapp_audit_success':
                        $info = MerchantCodeAudit::where('mer_id', $mer_id)->where('status', 2)->order('commit_time', 'desc')->find();
                        $info->save(['success_time' => $data['SuccTime'], 'status' => 0]);
                        //todo 某小程序代码审核通过
                        break;
                    case 'weapp_audit_fail';
                        $info = MerchantCodeAudit::where('mer_id', $mer_id)->where('status', 2)->order('commit_time', 'desc')->find();
                        $info->save(['fail_time' => $data['FailTime'], 'status' => 1, 'reason' => $data['Reason'], 'screenshot' => $data['ScreenShot']]);
                        //todo 某小程序代码审核不通过
                        break;
                    case 'weapp_audit_delay':
                        $info = MerchantCodeAudit::where('mer_id', $mer_id)->where('status', 2)->order('commit_time', 'desc')->find();
                        $info->save(['delay_time' => $data['DelayTime'], 'status' => 4, 'reason' => $data['Reason']]);
                        //todo 某小程序代码核延后
                        break;
                    default:
                        break;
                }
                BaseModel::commitTrans();
                echo 'success';
            } catch (Exception $e) {
                BaseModel::rollbackTrans();
                echo $e->getMessage();
            } catch (DbException $e) {
                BaseModel::rollbackTrans();
                echo $e->getMessage();
            }
        } else {
            BaseModel::rollbackTrans();
            echo '解密失败' . $errCode;
        }
    }


    /**
     * @param $xml
     * @return mixed|SimpleXMLElement
     */
    protected function _xmlToArr($xml)
    {
        $res = @simplexml_load_string($xml, NULL, LIBXML_NOCDATA);
        $res = json_decode(json_encode($res), true);
        return $res;
    }

    /**
     * 获取令牌
     * @return mixed
     * @throws \Exception
     * @throws InvalidArgumentException
     */
    public function getComponentAccessToken()
    {
        try {
            if (CacheService::redisHandler()->get('component_access_token')) {
                return CacheService::redisHandler()->get('component_access_token');
            }
            $url = 'https://api.weixin.qq.com/cgi-bin/component/api_component_token';
            $data = [
                'component_appid' => $this->appId,
                'component_appsecret' => $this->appSecret,
                'component_verify_ticket' => CacheService::redisHandler()->get('component_verify_ticket'),
            ];
//            $data['component_verify_ticket'] = 'ticket@@@iWDHjOTCGw8wIGeopp1Nux6gbi7KYD-MtFroWBw6WkEReGbCQWBesNMd-c3LlaR07J1cx4AQF48LYDMmcmdqZQ';

            $res = json_decode(doRequest($url, $data, null, true, true), true);

            if (isset($res['component_access_token'])) {
                CacheService::redisHandler()->set('component_access_token', $res['component_access_token'], $res['expires_in']);
                return $res['component_access_token'];
            } else {
                throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误');
            }
        } catch (Exception $e) {
            throw exception($e->getMessage());
        }
    }

    /**
     * 获取预授权码
     * @return mixed
     * @throws \Exception
     * @throws InvalidArgumentException
     */
    protected function getPreAuthCode()
    {
        try {
            if (CacheService::redisHandler()->get('pre_auth_code')) {
                return CacheService::redisHandler()->get('pre_auth_code');
            }
            $component_access_token = $this->getComponentAccessToken();
            $url = 'https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=' . $component_access_token;
            $data = [
                'component_appid' => $this->appId,
            ];
            $res = json_decode(doRequest($url, $data, null, true, true), true);
            if (isset($res['pre_auth_code'])) {
                CacheService::redisHandler()->set('pre_auth_code', $res['pre_auth_code'], $res['expires_in']);
                return $res['pre_auth_code'];
            } else {
                throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误');
            }
        } catch (Exception $e) {
            throw exception($e->getMessage());
        }
    }

    /**
     * 生成授权地址
     * @param string $re_url 回调地址
     * @param int $type 授权地址类型：1桌面端，其他移动端
     * @return mixed
     * @throws InvalidArgumentException
     */
    public function createAuthUrl($type = 1)
    {
        try {
            $re_url = $this->request->get('re_url');
            if (!$re_url) {
                return app('json')->fail('请传入回调地址');
            }
            $url_base = $type == 1 ? 'https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=' : 'https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=3&no_scan=1&component_appid=';
            $pre_auth_code = $this->getPreAuthCode();
            $url = $url_base . $this->appId
                . '&pre_auth_code=' . $pre_auth_code
                . '&redirect_uri=' . $re_url;//todo 改成前端的接收页面
            if ($type != 1) {
                $url .= '#wechat_redirect';
            }
            return app('json')->success('ok', ['url' => $url]);
        } catch (Exception $e) {
            return app('json')->fail($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]);
        }
    }

    /**
     * 接收授权码
     * @param $mer_id
     * @param Request $request
     * @return mixed
     * @throws InvalidArgumentException
     */
    public function wxAuthRedirect(Request $request, $mer_id)
    {
        $auth_code = $request->post('authorization_code');
        $expires_in = $request->post('expires_in');
        if ($auth_code && $expires_in) {
            try {
                CacheService::redisHandler()->set($mer_id . '_authorization_code', $auth_code, $expires_in);
                //return $this->getAuthInfo(['mer_id' => $mer_id]);
                return app('json')->success('获取授权码成功');
            } catch (Exception $e) {
                return app('json')->fail($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]);
            }
        } else {
            return app('json')->fail('获取授权码失败');
        }
    }

    /**
     *使用授权码获取授权信息
     * @param array $param
     * @return mixed
     * @throws \Exception
     * @throws InvalidArgumentException
     */
    protected function getAuthInfo($param = ['mer_id' => ''])
    {
        BaseModel::beginTrans();
        try {
            $mer_id = $param['mer_id'] ?? '';
            if (!$mer_id) {
                BaseModel::rollbackTrans();
                throw exception('未找到商家');
            }
            $mer_info = Merchant::get($mer_id);
            if (!$mer_info) {
                BaseModel::rollbackTrans();
                throw exception('未找到商家');
            }
            $authorization_code = CacheService::redisHandler()->get($param['mer_id'] . '_authorization_code');
            if (!$authorization_code) {
                BaseModel::rollbackTrans();
                throw exception('请先获取授权码');
            }
            $component_access_token = $this->getComponentAccessToken();
            $url = 'https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=' . $component_access_token;
            $data = [
                'component_appid' => $this->appId,
                'authorization_code' => $authorization_code,
            ];
            $res = json_decode(doRequest($url, $data, null, true, true), true);
            if (isset($res['authorization_info'])) {
                //保存/更新内容到商户小程序表
                if (MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find()) {
                    MerchantMiniprogram::vaildWhere()->where('mer_id', $mer_id)->update([
                        'appid' => $res['authorization_info']['authorizer_appid'],
                        'authorizer_refresh_token' => $res['authorization_info']['authorizer_refresh_token'],
                        'func_info' => json_encode($res['authorization_info']),
                        'update' => time(),
                    ]);
                    BaseModel::commitTrans();
                } else {
                    $app_secret = self::createAppSecret();
                    MerchantMiniprogram::create([
                        'mer_id' => $mer_id,
                        'appid' => $res['authorization_info']['authorizer_appid'],
                        'authorizer_refresh_token' => $res['authorization_info']['authorizer_refresh_token'],
                        'func_info' => json_encode($res['authorization_info']),
                        'add_time' => time(),
                        'update' => time(),
                        'boofly_app_secret' => $app_secret,
                    ]);
                    BaseModel::commitTrans();
                }
                //保存authorizer_access_token到redis，过期时间为expires_in
                CacheService::redisHandler()->set($mer_id . '_authorizer_access_token', $res['authorization_info']['authorizer_access_token'], $res['authorization_info']['expires_in']);
                return array_merge($res['authorization_info'], ['boofly_app_secret' => $app_secret ?? '']);
            } else {
                BaseModel::rollbackTrans();
                throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误');
            }
        } catch (Exception $e) {
            BaseModel::rollbackTrans();
            throw exception($e->getMessage());
        } catch (DbException $e) {
            BaseModel::rollbackTrans();
            throw exception($e->getMessage());
        }
    }

    /**
     * 创建app_secret
     * @return string
     * @throws \Exception
     */
    private static function createAppSecret()
    {
        return md5(bin2hex(random_bytes(8)));
    }

    /**
     *获取/刷新商户接口调用令牌
     * @param array $param
     * @return mixed
     * @throws InvalidArgumentException
     * @throws \Exception
     */
    public function getAuthorizerAccessToken($param = ['mer_id' => '', 'get_token' => 1, 'return_array' => false])
    {
        BaseModel::beginTrans();
        try {
            $mer_id = $param['mer_id'] ?? '';
            $get_token = isset($param['get_token']) ? $param['get_token'] : 1;
            $return_array = isset($param['return_array']) ? $param['return_array'] : false;
            if (!$mer_id) {
                throw exception('未找到商家');
            }
            if (CacheService::redisHandler()->get($mer_id . '_authorizer_access_token') && $get_token == 1) {
                BaseModel::commitTrans();
                return CacheService::redisHandler()->get($mer_id . '_authorizer_access_token');
            }
            // 根据商户id从商户表获取authorizer_appid和authorizer_refresh_token
            $authorizer_info = MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find();
            if (!$authorizer_info) {
                throw exception('未找到商家');
            }
            $component_access_token = $this->getComponentAccessToken();
            $url = 'https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=' . $component_access_token;
            $data = [
                'component_appid' => $this->appId,
                'authorizer_appid' => $authorizer_info['appid'],
                'authorizer_refresh_token' => $authorizer_info['authorizer_refresh_token'],
            ];
            $res = json_decode(doRequest($url, $data, null, true, true), true);
            if (isset($res['authorizer_access_token'])) {
                MerchantMiniprogram::vaildWhere()->where('mer_id', $mer_id)->update([
                    'authorizer_refresh_token' => $res['authorizer_refresh_token'],
                    'update' => time(),
                ]);
                BaseModel::commitTrans();
                //保存新的authorizer_access_token到redis，过期时间为expires_in
                CacheService::redisHandler()->set($mer_id . '_authorizer_access_token', $res['authorizer_access_token'], $res['expires_in']);
                return $return_array ? ['access_token' => $res['authorizer_access_token'], 'expires_in' => $res['expires_in']] : $res['authorizer_access_token'];
            } else {
                BaseModel::rollbackTrans();
                throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误');
            }
        } catch (Exception $e) {
            BaseModel::rollbackTrans();
            throw exception($e->getMessage());
        } catch (DbException $e) {
            BaseModel::rollbackTrans();
            throw exception($e->getMessage());
        }
    }

    /**
     * 获取授权方的帐号基本信息
     * 包括头像、昵称、帐号类型、认证类型、微信号、原始ID和二维码图片URL
     * 具体参数详见https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/api_get_authorizer_info.html#%E5%85%AC%E4%BC%97%E5%8F%B7%E5%B8%90%E5%8F%B7%E4%BF%A1%E6%81%AF
     * @param array $param
     * @return mixed
     * @throws InvalidArgumentException
     * @throws \Exception
     */
    protected function getAuthorizerInfo($param = ['mer_id' => ''])
    {
        BaseModel::beginTrans();
        try {
            $mer_id = $param['mer_id'] ?? '';
            if (!$mer_id) {
                BaseModel::rollbackTrans();
                throw exception('未找到商家');
            }
            //根据商户id从商户表获取authorizer_appid和authorizer_refresh_token
            $authorizer_info = MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find();
            if (!$authorizer_info) {
                BaseModel::rollbackTrans();
                throw exception('未找到商家');
            }
            $component_access_token = $this->getComponentAccessToken();
            $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=' . $component_access_token;
            $data = [
                'component_appid' => $this->appId,
                'authorizer_appid' => $authorizer_info['appid'],
            ];
            $res = json_decode(doRequest($url, $data, null, true, true), true);
            if (isset($res['authorizer_info'])) {
                //更新商户信息到商户表
                MerchantMiniprogram::vaildWhere()->where('mer_id', $mer_id)->update([
                    'nick_name' => $res['authorizer_info']['nick_name'],
                    'head_img' => $res['authorizer_info']['head_img'],
                    'signature' => $res['authorizer_info']['signature'],
                    'principal_name' => $res['authorizer_info']['principal_name'],
                    'categories' => $res['authorizer_info']['nick_name'],
                    'business_info' => json_encode($res['authorizer_info']['business_info']),
                    'service_type_info' => $res['authorizer_info']['service_type_info']['id'],
                    'user_name' => $res['authorizer_info']['user_name'],
                    'qrcode_url' => $res['authorizer_info']['qrcode_url'],
                    'update' => time(),
                ]);
                BaseModel::commitTrans();
//                CacheService::redisHandler()->set($mer_id . '_authorizer_access_token', $res['authorizer_access_token'], $res['expires_in']);
                return $res;
            } else {
                BaseModel::rollbackTrans();
                throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误');
            }
        } catch (Exception $e) {
            BaseModel::rollbackTrans();
            throw exception($e->getMessage());
        } catch (DbException $e) {
            BaseModel::rollbackTrans();
            throw exception($e->getMessage());
        }
    }

    /**
     *获取授权方选项信息（可能不常用）
     * 如：地理位置上报，语音识别开关，多客服开关
     * @param array $param
     * @return mixed
     * @throws InvalidArgumentException
     * @throws \Exception
     */
    protected function getAuthorizerOption($param = ['mer_id' => '', 'option_name' => ''])
    {
        try {
            $mer_id = $param['mer_id'] ?? '';
            $option_name = $param['option_name'] ?? '';
            if (!$mer_id) {
                throw exception('未找到商家');
            }
            //根据商户id从商户表获取authorizer_appid和authorizer_refresh_token
            $authorizer_info = MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find();
            if (!$authorizer_info) {
                throw exception('未找到商家');
            }
            $component_access_token = $this->getComponentAccessToken();
            $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=' . $component_access_token;
            $data = [
                'component_appid' => $this->appId,
                'authorizer_appid' => $authorizer_info['appid'],
                'option_name' => $option_name,
            ];
            $res = json_decode(doRequest($url, $data, null, true, true), true);
            if (isset($res['option_value'])) {
                return $res['option_value'];
            } else {
                throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误');
            }
        } catch (Exception $e) {
            throw exception($e->getMessage());
        }
    }

    /**
     * 设置授权方选项信息
     * @param $mer_id
     * @param $option_name
     * @param $option_value
     * @return mixed
     * @throws InvalidArgumentException
     * @throws \Exception
     */
    public function setAuthorizerOption($mer_id, $option_name, $option_value)
    {
        try {
            if (!$mer_id) {
                return app('json')->fail('未找到商家');
            }
            //根据商户id从商户表获取authorizer_appid和authorizer_refresh_token
            $authorizer_info = MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find();
            if (!$authorizer_info) {
                throw exception('未找到商家');
            }
            $component_access_token = $this->getComponentAccessToken();
            $url = 'https://api.weixin.qq.com/cgi-bin/component/api_set_authorizer_option?component_access_token=' . $component_access_token;
            $data = [
                'component_appid' => $this->appId,
                'authorizer_appid' => $authorizer_info['appid'],
                'option_name' => $option_name,
                'option_value' => $option_value,
            ];
            $res = json_decode(doRequest($url, $data, null, true, true), true);
            if (isset($res['errcode'])) {
                if ($res['errcode'] == 0) {
                    return app('json')->success('设置成功');
                } else {
                    return app('json')->fail($res['errmsg']);
                }
            } else {
                return app('json')->fail($res['errmsg'] ?? $res['msg'] ?? '请求错误');
            }
        } catch (Exception $e) {
            return app('json')->fail($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]);
        }
    }

    /**
     * 拉取所有已授权的帐号信息
     * @param array $page
     * @return mixed
     * @throws InvalidArgumentException
     * @throws \Exception
     */
    protected function getAuthorizerList($page = ['offset' => 1, 'count' => 10])
    {
        $offset = $page['offset'] ?? 1;
        $count = $page['count'] ?? 10;
        BaseModel::beginTrans();
        try {
            $component_access_token = $this->getComponentAccessToken();
            $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=' . $component_access_token;
            $data = [
                'component_appid' => $this->appId,
                'offset' => $offset,
                'count' => $count,
            ];
            $res = json_decode(doRequest($url, $data, null, true, true), true);
            if (isset($res['total_count'])) {
                foreach ($res['list'] as $v) {
                    MerchantMiniprogram::vaildWhere()->where('appid', $v['authorizer_appid'])->update(['authorizer_refresh_token' => $v['refresh_token'], 'update' => time()]);
                }
                BaseModel::commitTrans();
                return $res;
            } else {
                BaseModel::rollbackTrans();
                throw exception($res['errmsg'] ?? '请求错误');
            }
        } catch (Exception $e) {
            BaseModel::rollbackTrans();
            throw exception($e->getMessage());
        } catch (DbException $e) {
            BaseModel::rollbackTrans();
            throw exception($e->getMessage());
        }
    }

    /**
     * 前端获取某些信息
     * @param Request $request
     * @param string $info_type 获取的信息类型
     * @return mixed
     */
    public function getInfoForFront(Request $request, $info_type = 'AuthorizerAccessToken')
    {
        try {
            $function = 'get' . $info_type;
            if (method_exists($this, $function)) {
                if (!$request->param()) {
                    $res = $this->$function();
                } else {
                    $res = $this->$function($request->param());
                }
                return app('json')->success('ok', ['res' => $res]);
            } else {
                return app('json')->fail('获取失败');
            }
        } catch (Exception $e) {
            return app('json')->fail($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]);
        }
    }
}