<?php

namespace liuniu;

use app\admin\model\Company;
use app\admin\model\WechatPlanRecord;
use app\common\model\LaveMonth;
use app\common\model\WechatContext;
use EasyWeChat\Factory;
use EasyWeChat\Kernel\Messages\Article;
use EasyWeChat\Kernel\Messages\Image;
use EasyWeChat\Kernel\Messages\News;
use EasyWeChat\Kernel\Messages\Text;
use EasyWeChat\Kernel\Messages\Video;
use think\Hook;
use think\Request;
use think\Response;

class WechatService
{
    private static $instance = null;
    private static $app = null;

    public static function options($cid)
    {
        $info = Company::where('id', $cid)->find();
        $config = [
            'app_id' => isset($info['wechat_appid']) ? trim($info['wechat_appid']) : '',
            'secret' => isset($info['wechat_appsecret']) ? trim($info['wechat_appsecret']) : '',
            'token' => isset($info['wechat_token']) ? trim($info['wechat_token']) : '',
            'guzzle' => [
                'timeout' => 10.0, // 超时时间（秒）
                'verify' => false
            ],
        ];
        if (isset($info['wechat_encode']) && (int)$info['wechat_encode'] > 0 && isset($info['wechat_encodingaeskey']) && !empty($info['wechat_encodingaeskey']))
            $config['aes_key'] = $info['wechat_encodingaeskey'];
        if (isset($info['pay_weixin_open']) && $info['pay_weixin_open'] == 1) {
            $config1 = [
                'mch_id' => trim($info['pay_weixin_mchid']),
                'key' => trim($info['pay_weixin_key']),
                'cert_path' => realpath('.' . $info['pay_weixin_client_certfile']),
                'key_path' => realpath('.' . $info['pay_weixin_client_keyfile']),
                'notify_url' => Request::instance()->domain() . "/api/wechat/notify/" . $cid
            ];
            $config = array_merge($config, $config1);
        }
        return $config;
    }

    public static function application($cache = false, $cid = 0)
    {
        (self::$instance[$cid] === null || $cache === true) && (self::$instance[$cid] = Factory::officialAccount(self::options($cid)));
        return self::$instance[$cid];
    }

    /**
     * 支付接口
     * @param false $cache
     * @param int $cid
     * @return \EasyWeChat\Payment\Application|mixed
     */
    public static function payment($cache = false, $cid = 0)
    {
        (self::$app[$cid] === null || $cache === true) && (self::$app[$cid] = Factory::payment(self::options($cid)));
        return self::$app[$cid];
    }
    public static function payment2($cache = false, $cid = 0) {
        // 使用 isset() 检查下标，避免直接访问未定义键
        if (!isset(self::$app[$cid])) {
            $options = self::options($cid);
            // 校验必要配置是否存在
//            if (empty($options['app_id']) || empty($options['mch_id'])) {
//                throw new \Exception("微信支付配置缺失: cid={$cid}");
//            }
            self::$app[$cid] = Factory::payment($options);
        }
        return self::$app[$cid];
    }

    public static function serve($cid = 0): Response
    {
        $wechat = self::application(true, $cid);
        $server = $wechat->server;
        self::hook($server);
        $response = $server->serve();
        return Response($response->getContent());
    }

    /**
     * 监听行为
     * @param Guard $server
     */
    private static function hook($server)
    {
        $server->push(function ($message) {
            switch ($message['MsgType']) {
                case 'event':
                    switch (strtolower($message['Event'])) {
                        case 'subscribe':
                            $response = WechatReply::reply('subscribe');
                            if (isset($message->EventKey)) {
                                if ($message->EventKey && ($qrInfo = QrcodeService::getQrcode($message->Ticket, 'ticket'))) {
                                    QrcodeService::scanQrcode($message->Ticket, 'ticket');
                                    if (strtolower($qrInfo['third_type']) == 'spread') {

                                    }
                                }
                            }
                            break;
                        case 'unsubscribe':
                            event('WechatEventUnsubscribeBefore', [$message]);
                            break;
                        case 'scan':
                            $response = WechatReply::reply('subscribe');
                            if ($message->EventKey && ($qrInfo = QrcodeService::getQrcode($message->Ticket, 'ticket'))) {
                                QrcodeService::scanQrcode($message->Ticket, 'ticket');
                                if (strtolower($qrInfo['third_type']) == 'spread') {

                                }
                            }
                            break;
                        case 'location':
                            $response = MessageRepositories::wechatEventLocation($message);
                            break;
                        case 'click':
                            $response = WechatReply::reply($message->EventKey);
                            break;
                        case 'view':
                            $response = MessageRepositories::wechatEventView($message);
                            break;
                    }
                    break;
                case 'text':
                    @file_put_contents("tt.txt", '1');
                    $response = self::textMessage('绑定推荐人失败');
                    @file_put_contents("tt.txt", '2', 8);
                    @file_put_contents("tt.txt", json_encode($response), 8);
                    break;
                    $response = WechatReply::reply($message->Content);
                    break;
                case 'image':
                    $response = MessageRepositories::wechatMessageImage($message);
                    break;
                case 'voice':
                    $response = MessageRepositories::wechatMessageVoice($message);
                    break;
                case 'video':
                    $response = MessageRepositories::wechatMessageVideo($message);
                    break;
                case 'location':
                    $response = MessageRepositories::wechatMessageLocation($message);
                    break;
                case 'link':
                    $response = MessageRepositories::wechatMessageLink($message);
                    break;
                // ... 其它消息
                default:
                    $response = MessageRepositories::wechatMessageOther($message);
                    break;
            }

            return $response ?? false;
        });
    }


    /**
     * 多客服消息转发
     * @param string $account
     * @return \EasyWeChat\Message\Transfer
     */
    public static function transfer($account = '')
    {
        $transfer = new \EasyWeChat\Message\Transfer();
        return empty($account) ? $transfer : $transfer->to($account);
    }


    /**
     * 上传永久素材接口
     * @return \EasyWeChat\Material\Material
     */
    public static function materialService($cid = 0)
    {
        return self::application(false, $cid)->material;
    }

    /**
     * 上传临时素材接口
     * @return \EasyWeChat\Material\Temporary
     */
    public static function materialTemporaryService($cid = 0)
    {
        return self::application(false, $cid)->media;
    }

    /**
     * 用户接口
     * @return \EasyWeChat\User\User
     */
    public static function userService($cid = 0)
    {
        return self::application(false, $cid)->user;
    }


    /**
     * 客服消息接口
     * @param null $to
     * @param null $message
     */
    public static function staffService($cid = 0)
    {
        return self::application(false, $cid)->staff;
    }

    /**
     * 微信公众号菜单接口
     * @return \EasyWeChat\Menu\Menu
     */
    public static function menuService($cid = 0)
    {
        return self::application(false, $cid)->menu;
    }

    /**
     * 微信二维码生成接口
     * @return \EasyWeChat\QRCode\QRCode
     */
    public static function qrcodeService($cid = 0)
    {
        return self::application(false, $cid)->qrcode;
    }

    /**
     * 微信永久二维码生成接口 小于10万个
     * @return \EasyWeChat\QRCode\QRCode
     */
    public static function qrcodeForeverService($sceneValue, $cid = 0)
    {
        return self::application(false, $cid)->qrcode->forever($sceneValue);
    }

    /**
     * 微信临时二维码生成接口 30天有效期
     * @return \EasyWeChat\QRCode\QRCode
     */
    public static function qrcodeTempService($sceneValue, $expireSeconds = 2592000, $cid = 0)
    {
        return self::application(false, $cid)->qrcode->temporary($sceneValue, $expireSeconds);

    }

    /**
     * 短链接生成接口
     * @return \EasyWeChat\Url\Url
     */
    public static function urlService($cid = 0)
    {
        return self::application(false, $cid)->url;
    }

    /**
     * 用户授权
     * @return \Overtrue\Socialite\Providers\WeChatProvider
     */
    public static function oauthService($cid = 0)
    {
        return self::application(false, $cid)->oauth;
    }

    /**
     * 模板消息接口
     * @return \EasyWeChat\Notice\Notice
     */
    public static function noticeService($cid = 0)
    {
        return self::application(false, $cid)->template_message;
    }

    public static function sendTemplate($openid, $templateId, array $data, $url = null, $cid = 0)
    {
        $notice = self::noticeService($cid);
        return $notice->send([
            'touser' => $openid,
            'template_id' => $templateId,
            'url' => $url,
            'data' => $data,
        ]);
    }


    public static function userTagService($cid = 0)
    {
        return self::application(false, $cid)->user_tag;
    }

    /**
     * 生成支付订单对象
     * @param $openid
     * @param $out_trade_no
     * @param $total_fee
     * @param $attach
     * @param $body
     * @param string $detail
     * @param string $trade_type
     * @param array $options
     * @return Order
     */
    public static function paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [], $cid = 0)
    {
        $total_fee = bcmul($total_fee, 100, 0);
        $order = array_merge(compact('out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type', 'openid'), $options);
        if ($order['detail'] == '') unset($order['detail']);
        @file_put_contents("quanju.txt", json_encode($order)."-微信支付传值\r\n", 8);
        $result = self::payment(false, $cid)->order->unify(
            $order
        );
        return $result;
    }


    /**
     * 使用商户订单号退款
     * @param $orderNo
     * @param $opt
     */

    public static function payOrderRefund($cid, $orderNo, array $opt)
    {
        if (!isset($opt['pay_price'])) exception('缺少pay_price');
        $totalFee = floatval(bcmul($opt['pay_price'], 100, 0));
        $refundFee = isset($opt['refund_price']) ? floatval(bcmul($opt['refund_price'], 100, 0)) : null;
        $refundReason = isset($opt['desc']) ? $opt['desc'] : '';
        $refundNo = isset($opt['refund_id']) ? $opt['refund_id'] : $orderNo;
        $opUserId = isset($opt['op_user_id']) ? $opt['op_user_id'] : null;
        $type = isset($opt['type']) ? $opt['type'] : 'out_trade_no';
        /*仅针对老资金流商户使用
        REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款（默认使用未结算资金退款）
        REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款*/
        $refundAccount = isset($opt['refund_account']) ? $opt['refund_account'] : 'REFUND_SOURCE_UNSETTLED_FUNDS';
        try {
            $res = self::payment('false', $cid)->byOutTradeNumber($orderNo, $refundNo, $totalFee, $refundFee, ['refund_desc' => $refundReason]);
            if ($res->return_code == 'FAIL') exception('退款失败:' . $res->return_msg);
            if (isset($res->err_code)) exception('退款失败:' . $res->err_code_des);
        } catch (\Exception $e) {
            exception($e->getMessage());
        }
        return true;
    }

    /**
     * 微信支付成功回调接口
     */
    public static function handleNotify($cid)
    {
        $response = self::payment(true, $cid)->handlePaidNotify(function ($notify, $successful) use ($cid) {
            if ($successful && isset($notify['out_trade_no'])) {
                if (isset($notify['attach']) && $notify['attach']) {
                    if (($count = strpos($notify['out_trade_no'], '_')) !== false) {
                        $notify['out_trade_no'] = substr($notify['out_trade_no'], $count + 1);
                    }
                    $params = [$cid, $notify['out_trade_no']];
                    Hook::exec("\\liuniu\\repositories\\PaymentRepositories", "wechat" . ucfirst($notify['attach']), $params);
                }
                $data = ['eventkey' => 'notify', 'command' => '', 'refreshtime' => time(), 'openid' => $notify['openid'], 'message' => json_encode($notify)];
                $wechatContext = WechatContext::create($data, true);
                return true;
            }
            @file_put_contents("quanju.txt", json_encode($notify)."-签约返回内容\r\n", 8);
//            @file_put_contents("quanju.txt", json_encode($successful)."-这是什么\r\n", 8);
            if (isset($notify['plan_id'])) {
                if (isset($notify['change_type']) && $notify['change_type']) {
                    @file_put_contents("quanju.txt", $notify['change_type']."-状态\r\n", 8);
                    if ($notify['change_type']=='ADD'){
                        $cs=WechatPlanRecord::where('contract_code',$notify['contract_code'])->Update(['contract_id' => $notify['contract_id'],'is_signing'=>0]);
                        if (($count = strpos($notify['contract_code'], '_')) !== false) {
                            $notify['contract_code'] = substr($notify['contract_code'], $count + 1);
                        }
                        $params = [$cid, $notify['contract_code']];
                        Hook::exec("\\liuniu\\repositories\\PaymentRepositories", "wechat" . ucfirst('MonthLave'), $params);
                        @file_put_contents("quanju.txt", json_encode($params)."-不知道行不行\r\n", 8);
                    }elseif ($notify['change_type']=='DELETE'){
                        @file_put_contents("quanju.txt", "-关闭签约\r\n", 8);
                        $cs=WechatPlanRecord::where('contract_code',$notify['contract_code'])->Update(['is_signing' => 1,'deletetime'=>time()]);
                    }
                    @file_put_contents("quanju.txt", $cs."-修改记录\r\n", 8);
                }
                $data = ['eventkey' => 'notify', 'command' => '', 'refreshtime' => time(), 'openid' => $notify['openid'], 'message' => json_encode($notify)];
                $wechatContext = WechatContext::create($data, true);
                return true;
            }

            if (isset($notify['nonce_str'])) {
                $cz = LaveMonth::Where(['contract_code' => $notify['nonce_str']])->find();
                @file_put_contents("quanju.txt", json_encode($cz)."-随机字符搜索\r\n", 8);
                if (!empty($cz)){
                    $params = [$cid, $notify['nonce_str']];
                    Hook::exec("\\liuniu\\repositories\\PaymentRepositories", "wechat" . ucfirst('MonthLavePay'), $params);
                }
                $data = ['eventkey' => 'notify', 'command' => '', 'refreshtime' => time(), 'openid' => $notify['openid'], 'message' => json_encode($notify)];
                $wechatContext = WechatContext::create($data, true);
                return true;
            }
        });
        $response->send();
    }

    /**
     * jsSdk
     * @return \EasyWeChat\Js\Js
     */
    public static function jsService($cid = 0)
    {
        return self::payment(false, $cid)->jssdk;
    }

    public static function WeixinJSBridge($cid, $prepayId)
    {
        $json = self::jsService($cid)->bridgeConfig($prepayId);
        return $json;
    }

    public static function jspay($cid, $prepayId)
    {
        $json = self::jsService($cid)->sdkConfig($prepayId);
        return $json;
    }

    public static function jsSdk($url = '', $cid = 0)
    {
        $apiList = ['editAddress', 'openAddress', 'updateTimelineShareData', 'updateAppMessageShareData', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'translateVoice', 'getNetworkType', 'openLocation', 'getLocation', 'hideOptionMenu', 'showOptionMenu', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', 'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView', 'addCard', 'chooseCard', 'openCard'];
        $jsService = self::jsService($cid);
        if ($url) $jsService->setUrl($url);
        try {
            return $jsService->buildConfig($apiList, false);
        } catch (\Exception $e) {
            // var_dump($e->getMessage());
            return '{}';
        }

    }


    /**
     * 回复文本消息
     * @param string $content 文本内容
     * @return Text
     */
    public static function textMessage($content)
    {

        return new Text($content);
    }

    /**
     * 回复图片消息
     * @param string $media_id 媒体资源 ID
     * @return Image
     */
    public static function imageMessage($media_id)
    {
        return new Image('media_id');
    }

    /**
     * 回复视频消息
     * @param string $media_id 媒体资源 ID
     * @param string $title 标题
     * @param string $description 描述
     * @param null $thumb_media_id 封面资源 ID
     * @return Video
     */
    public static function videoMessage($media_id, $title = '', $description = '...', $thumb_media_id = null)
    {
        return new Video(compact('media_id', 'title', 'description', 'thumb_media_id'));
    }

    /**
     * 回复声音消息
     * @param string $media_id 媒体资源 ID
     * @return Voice
     */
    public static function voiceMessage($media_id)
    {
        return new Voice(compact('media_id'));
    }

    /**
     * 回复图文消息
     * @param string|array $title 标题
     * @param string $description 描述
     * @param string $url URL
     * @param string $image 图片链接
     */
    public static function newsMessage($title, $description = '...', $url = '', $image = '')
    {
        if (is_array($title)) {
            if (isset($title[0]) && is_array($title[0])) {
                $newsList = [];
                foreach ($title as $news) {
                    $newsList[] = self::newsMessage($news);
                }
                return $newsList;
            } else {
                $data = $title;
            }
        } else {
            $data = compact('title', 'description', 'url', 'image');
        }
        return new News($data);
    }

    /**
     * 回复文章消息
     * @param string|array $title 标题
     * @param string $thumb_media_id 图文消息的封面图片素材id（必须是永久 media_ID）
     * @param string $source_url 图文消息的原文地址，即点击“阅读原文”后的URL
     * @param string $content 图文消息的具体内容，支持HTML标签，必须少于2万字符，小于1M，且此处会去除JS
     * @param string $author 作者
     * @param string $digest 图文消息的摘要，仅有单图文消息才有摘要，多图文此处为空
     * @param int $show_cover_pic 是否显示封面，0为false，即不显示，1为true，即显示
     * @param int $need_open_comment 是否打开评论，0不打开，1打开
     * @param int $only_fans_can_comment 是否粉丝才可评论，0所有人可评论，1粉丝才可评论
     * @return Article
     */
    public static function articleMessage($title, $thumb_media_id, $source_url, $content = '', $author = '', $digest = '', $show_cover_pic = 0, $need_open_comment = 0, $only_fans_can_comment = 1)
    {
        $data = is_array($title) ? $title : compact('title', 'thumb_media_id', 'source_url', 'content', 'author', 'digest', 'show_cover_pic', 'need_open_comment', 'only_fans_can_comment');
        return new Article($data);
    }


    /**
     * 作为客服消息发送
     * @param $to
     * @param $message
     * @return bool
     */
    public static function staffTo($to, $message)
    {
        $staff = self::staffService();
        $staff = is_callable($message) ? $staff->message($message()) : $staff->message($message);
        $res = $staff->to($to)->send();
        return $res;
    }

    /**
     * 获得用户信息
     * @param array|string $openid
     * @return \EasyWeChat\Support\Collection
     */
    public static function getUserInfo($cid, $openid)
    {
        $userService = self::userService($cid);
        $userInfo = is_array($openid) ? $userService->select($openid) : $userService->get($openid);
        return $userInfo;
    }
    /**
     * 生成支付签约订单对象
     * @param $openid
     * @param $out_trade_no
     * @param $total_fee
     * @param $attach
     * @param $body
     * @param string $detail
     * @param string $trade_type
     * @param array $options
     * @return Order
     */
    public static function paysignedOrder($openid, $out_trade_no, $total_fee, $attach, $body,$contract_code, $plan_id,$spbill_create_ip,$detail = '', $trade_type = 'JSAPI', $options = [], $cid = 0,$contract_display_account='')
    {
        $total_fee = bcmul($total_fee, 100, 0);
        $order = array_merge(compact('out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type', 'openid','contract_code','plan_id','spbill_create_ip','contract_display_account'), $options);
        if ($order['detail'] == '') unset($order['detail']);
        $order['contract_notify_url']=Request::instance()->domain() . "/api/wechat/notify/" . $cid;
        $result = self::payment(false, $cid)->order->unify(
            $order,true
        );
//        var_dump($result);die();
        @file_put_contents("quanju.txt", json_encode($result) . "-签约返回结果\r\n", 8);
        return $result;
    }
    /**
     * 签约申请扣款
     * @param $openid
     * @param $out_trade_no
     * @param $total_fee
     * @param $attach
     * @param $body
     * @param string $detail
     * @param string $trade_type
     * @param array $options
     * @return Order
     */
    public static function papPayApply($mch_id,$out_trade_no, $total_fee, $attach, $detail = '', $trade_type = 'PAP', $options = [], $cid = 0,$contract_id='',$body='月捐款',$nonce_str='')
    {
        $total_fee = bcmul($total_fee, 100, 0);
        $order = array_merge(compact('mch_id','out_trade_no', 'total_fee', 'attach', 'detail', 'trade_type','contract_id','body','nonce_str'), $options);
        if ($order['detail'] == '') unset($order['detail']);
        $order['notify_url']=Request::instance()->domain() . "/api/wechat/notify/" . $cid;
        $result = self::payment(false, $cid)->contract->apply(
            $order
        );
//        var_dump($result);die();
        return $result;
    }
    /**
     * 解除签约
     */
    public static function deleteSign($mch_id,$contract_code,$plan_id,$version='1.0',$options=[],$cid=0)
    {
        $contract_termination_remark = '月捐款解约';
        $order = array_merge(compact('mch_id','contract_code','plan_id','version','contract_termination_remark'), $options);
//        if ($order['detail'] == '') unset($order['detail']);
//        $order['notify_url']=Request::instance()->domain() . "/api/wechat/notify/" . $cid;
        $result = self::payment(false, $cid)->contract->delete(
            $order
        );
        return $result;
    }
    /**
     * 查询签约
     */
    public static function querySign($mch_id,$contract_code,$pan_id,$version='1.0',$options=[],$cid=0)
    {
        $appid='wx5681205d1ef4d9d3';
        $order = array_merge(compact('appid','mch_id','contract_code','pan_id','version'), $options);
//        if ($order['detail'] == '') unset($order['detail']);
//        $order['notify_url']=Request::instance()->domain() . "/api/wechat/notify/" . $cid;
        $result = self::payment(false, $cid)->contract->query(
            $order
        );
        @file_put_contents("quanju.txt", json_encode($result) . "-查询签约状态返回结果\r\n", 8);
        return $result;
    }
    /**
     * 查询签约订单
     */
    public static function querySignOrder($order_id,$cid=0)
    {
        @file_put_contents("quanju2.txt", $order_id . "-查询的订单id\r\n", 8);
        @file_put_contents("quanju2.txt", $cid . "-cid\r\n", 8);
        $result = self::payment2(false, $cid)->order->queryByOutTradeNumber(
            $order_id
        );
        @file_put_contents("quanju2.txt", json_encode($result) . "-查询签约状态返回结果\r\n", 8);
        if ($result['return_code'] == 'SUCCESS' && $result['result_code'] !='FAIL'){
            if (!empty($result['trade_state_desc'])){
                if ($result['trade_state_desc'] == '支付成功'){
                    $time=strtotime($result['time_end']);
                LaveMonth::where(['order_id' => $order_id])->update(['paid' => 1,'paytime'=>$time]);  //修改订单状态
                }
            }
        }
        return $result;
    }
    /**
     * 查询签约关系
     */
    public static function querycontract($mch_id,$contract_id,$version='1.0',$options=[],$cid=0)
    {
        $appid='wx5681205d1ef4d9d3';
        $order = array_merge(compact('appid','mch_id','contract_id','version'), $options);
//        if ($order['detail'] == '') unset($order['detail']);
//        $order['notify_url']=Request::instance()->domain() . "/api/wechat/notify/" . $cid;
        $result = self::payment(false, $cid)->contract->querycontract(
            $order
        );
//        @file_put_contents("quanju.txt", json_encode($result) . "-查询签约状态返回结果\r\n", 8);
        return $result;
    }
}