<?php


namespace App\Modules\Push\Services;


use App\Cache\Lock\LockCache;
use App\Libs\Push\VPush\VPush;
use Exception;
use App\Cache\Push\PushCache;
use App\Consts\PushConst;
use App\Libs\Push\OPPOPush\OPPOPushCommon;
use App\Libs\Push\XMPush\MiPushCommon;
use App\Libs\Push\HuaWei\HwPushCommon;
use App\Modules\Push\Models\QappPushApp;
use App\Modules\Push\Models\QappPushTask;
use App\Modules\Push\Models\QappPushTaskLogs;
use App\Modules\Push\Models\QappPushTaskUsers;
use App\Modules\Push\Models\QappPushUser;
use App\Modules\User\Models\QappPackage;
use GuzzleHttp\Exception\GuzzleException;

class PushService
{
    /**
     * 设置用户reg_id
     * @param        $uid
     * @param string $regId
     * @param string $provider
     * @param string $package
     * @return bool
     */
    public static function setUserRegId($uid, string $regId, string $provider, string $package): bool
    {
        // push服务商（转小写）
        $provider = strtolower($provider);
        if (empty($uid) || empty($regId) || empty($package) || empty($provider)) {
            return false;
        }

        //myLog('push')->info('setUserRegId', compact('uid', 'regId', 'provider', 'package'));

        // 获取缓存
        $userCacheRegId = PushCache::getUserPushRegId($uid);
        if ($regId === $userCacheRegId) {
            return true;
        }

        // 加锁
        $token = md5($provider . ':' . $regId);
        $lock  = LockCache::getLock($token, 3);
        if (!$lock) {
            return false;
        }

        //myLog('push')->info('setUserRegId', compact('uid', 'regId', 'provider', 'token', 'lock'));

        // 获取包信息
        $packageInfo = QappPackage::getPackageByPackage($package);
        $packageId   = getProp($packageInfo, 'id');
        $channelId   = getProp($packageInfo, 'channel_id');
        $company     = getProp($packageInfo, 'company');
        //myLog('push')->info('setUserRegId', compact('packageId', 'channelId', 'company'));
        if (empty($packageId)) {
            return false;
        }

        // 获取app信息
        $pushApp   = QappPushApp::getPushAppByPackageIdAndProvider($packageId, $provider);
        $pushAppId = getProp($pushApp, 'app_id');
        //myLog('push')->info('setUserRegId', compact('pushAppId'));
        if (!$pushAppId) {
            return false;
        }

        // 设置
        $pushUser      = QappPushUser::getPushUserByUid($uid);
        $pushUserRegId = (string)getProp($pushUser, 'reg_id');
//        myLog('push')->info('setUserRegId', compact('pushUserRegId','uid','regId'));
        if ($pushUserRegId === $regId) {
            PushCache::setUserPushRegId($uid, $regId);
            return true;
        }

        // 初始化用户Push信息
        if (!$pushUser) {
            // 初始化push_users
            QappPushUser::initPushUser($uid, $regId, $pushAppId, $provider, $channelId);

            # 推送相关变量
            $topic        = PushConst::TOPIC_ALL;
            $appId        = getProp($pushApp, 'app_id');
            $appSecret    = getProp($pushApp, 'app_secret');
            $appKey       = getProp($pushApp, 'app_key');
            $masterSecret = getProp($pushApp, 'master_secret');

            // 针对华为用户需要主动订阅topic，方便后续全量推送
            if ($provider === PushConst::PROVIDER_HW) {
                $client = new HwPushCommon($pushAppId, $appSecret);
                $client->subscribeTopic($topic, [$regId]);
            }

            // 针对OPPO用户需要主动订阅topic，方便后续全量推送
            if ($provider === PushConst::PROVIDER_OPPO) {
                $client = new OPPOPushCommon($appKey, $masterSecret);
                $client->subscribeTags($regId, $topic);
            }

            // 针对VIVO用户需要主动订阅topic，方便后续全量推送
            if ($provider === PushConst::PROVIDER_OPPO) {
                $client = new VPush($appId, $appKey, $appSecret);
                $client->addTagForUsers($topic, [$regId]);
            }
        }

        // 更新用户数据库中reg_id
        $result = QappPushUser::setUserRegId($uid, $regId);
        if ($result) {
            // 更新缓存
            PushCache::setUserPushRegId($uid, $regId);
//            myLog('push')->info('updateUserRegId', compact('uid','regId'));
        }

        // 释放锁
        //        LockCache::releaseLock($token);

        return $result;
    }

    /**
     * 推送消息
     * @param $taskId
     * @return bool
     * @throws GuzzleException
     */
    public static function pushMessageByTaskId($taskId): bool
    {
        if (empty($taskId)) {
            return false;
        }

        // 获取任务数据，判断任务状态及发送时间
        $pushTask = QappPushTask::getPushTaskById($taskId);
        if (
            (int)getProp($pushTask, 'status') !== PushConst::STATUS_TODO ||
            (int)getProp($pushTask, 'select_user_status') !== PushConst::SELECT_USER_OK ||
            getProp($pushTask, 'push_time') > date('Y-m-d H:i:s')
        ) {
            return false;
        }

        // 获取全部子任务，判断子任务是否有效
        $subTasks = QappPushTaskLogs::getPushTaskLogsByTaskId($taskId);
        if (!$subTasks) {
            // 更新主任务失败状态及原因
            QappPushTask::updateMainTaskStatus($taskId, PushConst::STATUS_FAIL, '无有效子任务');
            return false;
        }

        // 获取推送应用信息
        $pushAppIds = array_column($subTasks, 'app_id');
        $pushApps   = QappPushApp::getPushAppByAppIds($pushAppIds);
        if (!$pushApps) {
            // 更新主任务失败状态及原因
            QappPushTask::updateMainTaskStatus($taskId, PushConst::STATUS_FAIL, '无有效推送APP');
            return false;
        }

        // 更新主任务状态为开始状态
        QappPushTask::updateMainTaskStatus($taskId, PushConst::STATUS_DOING, '无有效推送APP');

        // 全量发送走标签，否则走批量
        $pushFilter = getProp($pushTask, 'push_filter');
        if (in_array($pushFilter, [PushConst::FILTER_ALL_USERS, PushConst::FILTER_ALL_SUB_USERS])) {
            $result = self::pushMessageToAll($pushTask, $subTasks, $pushApps);
        } else {
            $result = self::pushMessageToUsers($pushTask, $subTasks, $pushApps);
        }

        // 更新主任务最终状态（成功/失败）
        $status = $result ? PushConst::STATUS_SUCCESS : PushConst::STATUS_FAIL;
        QappPushTask::updateMainTaskStatus($taskId, $status, $result);

        return $result;
    }

    /**
     * 全部发送
     * @param $pushTask
     * @param $subTasks
     * @param $pushApps
     * @return bool
     * @throws GuzzleException
     */
    private static function pushMessageToAll($pushTask, $subTasks, $pushApps): bool
    {
        if (empty($pushTask) || empty($subTasks) || empty($pushApps)) {
            return false;
        }

        // 推送结果、推送内容
        $pushResult = true;
        $title      = getProp($pushTask, 'title');
        $content    = getProp($pushTask, 'content');
        $url        = getProp($pushTask, 'url');
        myLog('push')->info('1', compact('title', 'content'));

        // 循环群发
        foreach ($subTasks as $subTask) {
            $appId     = getProp($subTask, 'app_id');
            $subTaskId = getProp($subTask, 'id');
            $pushApp   = collect($pushApps)->firstWhere('app_id', $appId);
            if (empty($pushApp)) {
                continue;
            }

            // push app相关
            $provider     = getProp($pushApp, 'provider');
            $package      = getProp($pushApp, 'package');
            $appId        = getProp($pushApp, 'app_id');
            $appSecret    = getProp($pushApp, 'app_secret');
            $appKey       = getProp($pushApp, 'app_key');
            $masterSecret = getProp($pushApp, 'master_secret');
            $topic        = PushConst::TOPIC_ALL;

            $content = self::contentFormat($content, $provider);
            // 更新开始状态
            QappPushTaskLogs::updateSubTaskStatus($subTaskId, PushConst::STATUS_DOING);

            $result = [];
            try {
                // 针对渠道做不同处理
                switch ($provider) {
                    // 华为
                    case PushConst::PROVIDER_HW:
                        // 开发状态还是生产状态
                        $target = env('APP_ENV') === 'production' ? 2 : 1;

                        // 设置相关配置
                        $client = new HwPushCommon($appId, $appSecret);
                        $client->setFastAppTarget($target);
                        $client->setTopic($topic);
                        $client->setBigTag('Task_' . getProp($pushTask, 'id'));

                        // 推送
                        $result = $client->sendPushMessage($title, $content, $url);
                        break;
                    // 小米
                    case PushConst::PROVIDER_MI:
                        $client = new MiPushCommon($package, $appSecret);
                        $result = $client->sendMessageToAll($title, $content, $url);
                        break;
                    // OPPO
                    case PushConst::PROVIDER_OPPO:
                        $client    = new OPPOPushCommon($appKey, $masterSecret);
                        $messageId = $client->getMessageId($title, $content, $url);
                        $result    = $client->broadCastAll($messageId, $topic);
                        break;
                    // VIVO
                    case PushConst::PROVIDER_VIVO:
                        $client = new VPush($appId, $appKey, $appSecret);
                        $client->setPushData($title, $content, $url);
                        // $result = $client->sendAll();
                        $result = $client->tagPush($topic);
                        break;
                }
            } catch (Exception $e) {
                myLog('push')->info('pushMessageToAll', [$provider, $result, $e->getMessage()]);

                // 最终结果
                $pushResult = 0 && $pushResult;

                // 更新子任务失败状态
                QappPushTaskLogs::updateSubTaskStatus($subTaskId, PushConst::STATUS_FAIL, $result);
                continue;
            }

            // 更新成功状态
            QappPushTaskLogs::updateSubTaskStatus($subTaskId, PushConst::STATUS_SUCCESS, $result);
        }

        return $pushResult;
    }

    /**
     * @param $pushTask
     * @param $subTasks
     * @param $pushApps
     * @return bool
     * @throws GuzzleException
     */
    private static function pushMessageToUsers($pushTask, $subTasks, $pushApps)
    {
        // 获取需要推送的人
        $taskId    = getProp($pushTask, 'id');
        $taskUsers = QappPushTaskUsers::getTaskUsers($taskId);
        if (!$taskUsers) {
            // 更新主任务失败状态及原因
            QappPushTask::updateMainTaskStatus($taskId, PushConst::STATUS_FAIL, '未设置推送用户');
            return false;
        }

        // 推送结果、推送内容
        $pushResult = true;
        $title      = getProp($pushTask, 'title');
        $content    = getProp($pushTask, 'content');
        $url        = getProp($pushTask, 'url');

        // 推送
        foreach ($subTasks as $subTask) {
            $subTaskId = getProp($subTask, 'id');
            $appId     = getProp($subTask, 'app_id');
            $pushApp   = collect($pushApps)->firstWhere('app_id', $appId);
            $uids      = collect($taskUsers)->where('app_id', $appId)->pluck('uid');
            $users     = QappPushUser::getPushUserByUids($uids);
            $regIds    = array_column($users, 'reg_id');

            // push app相关
            $provider     = getProp($pushApp, 'provider');
            $package      = getProp($pushApp, 'package');
            $appId        = getProp($pushApp, 'app_id');
            $appSecret    = getProp($pushApp, 'app_secret');
            $appKey       = getProp($pushApp, 'app_key');
            $masterSecret = getProp($pushApp, 'master_secret');

            $content = self::contentFormat($content, $provider);
            // 更新开始状态
            QappPushTaskLogs::updateSubTaskStatus($subTaskId, PushConst::STATUS_DOING);

            // 循环批量
            $result   = [];
            $regIdArr = array_chunk($regIds, 1000);
            try {
                switch ($provider) {
                        // 华为
                    case PushConst::PROVIDER_HW:
                        // 初始化huawei推送
                        $client = new HwPushCommon($appId, $appSecret);

                        // 循环推送
                        $i = 0;
                        foreach ($regIdArr as $regIdList) {
                            $client->setToken($regIdList);
                            $result = $client->sendPushMessage($title, $content, $url);

                            // 一般的应用默认消息流控是针对单个应用3000QPS（每秒不能超过3000个token数）
                            $i++;
                            if ($i % 3 === 0) {
                                sleep(1);
                            }
                        }
                        break;
                        // 小米
                    case PushConst::PROVIDER_MI:
                        // 初始化小米推送
                        $client = new MiPushCommon($package, $appSecret);

                        // 循环推送
                        $i = 0;
                        foreach ($regIdArr as $regIdList) {
                            $client->setRegArr($regIdList);
                            $result = $client->sendMessage($title, $content, $url);

                            // 一般的应用默认消息流控是针对单个应用3000QPS（每秒不能超过3000个token数）
                            $i++;
                            if ($i % 3 === 0) {
                                sleep(1);
                            }
                        }
                        break;
                        // OPPO
                    case PushConst::PROVIDER_OPPO:
                        // 初始化oppo推送
                        $client    = new OPPOPushCommon($appKey, $masterSecret);
                        $messageId = $client->getMessageId($title, $content, $url);

                        // 循环推送
                        $i = 0;
                        foreach ($regIdArr as $regIdList) {
                            $client->setRegArr($regIdList);
                            $result = $client->broadCastRegIds($messageId);
                            // 一般的应用默认消息流控是针对单个应用3000QPS（每秒不能超过3000个token数）
                            $i++;
                            if ($i % 3 === 0) {
                                sleep(1);
                            }
                        }
                        break;
                        // VIVO
                    case PushConst::PROVIDER_VIVO:
                        // 初始化oppo推送
                        $client = new VPush($appId, $appKey, $appSecret);

                        // 循环推送
                        $i = 0;
                        foreach ($regIdArr as $regIdList) {
                            // 设置相关推送数据，保存消息内容
                            $client->setPushData($title, $content, $url, $regIdList);
                            $client->saveListPayload();
                            $result = $client->sendMessage();

                            // 一般的应用默认消息流控是针对单个应用3000QPS（每秒不能超过3000个token数）
                            $i++;
                            if ($i % 3 === 0) {
                                sleep(1);
                            }
                        }
                }
            } catch (Exception $e) {
                // 最终结果
                $pushResult = 0 && $pushResult;

                // 更新子任务失败状态
                QappPushTaskLogs::updateSubTaskStatus($subTaskId, PushConst::STATUS_FAIL, $result);
                continue;
            }

            // 更新成功状态
            QappPushTaskLogs::updateSubTaskStatus($subTaskId, PushConst::STATUS_SUCCESS, $result);
        }

        return $pushResult;
    }

    private static function contentFormat(string $content, string $provider): string
    {
        return str_replace('{device}', PushMessageService::providers[$provider], $content);
    }
}
