<?php

namespace app\event;
/**
 * 用于检测业务代码死循环或者长时间阻塞等问题
 * 如果发现业务卡死，可以将下面declare打开（去掉//注释），并执行php start.php reload
 * 然后观察一段时间workerman.log看是否有process_timeout异常
 */
//declare(ticks=1);

use app\entity\gateway\ws\OutMessage;
use app\entity\gateway\ws\PutMessage;
use app\entity\SysRole;
use app\entity\SysUser;
use app\enum\gateway\message\MessageType;
use app\enum\gateway\message\SystemEnum;
use app\service\admin\LoginService;
use app\service\GatewayClientService;
use think\exception\ValidateException;
use think\facade\Event;

class SysGatewayEvent
{
    public static function onMessage(string $client_id, string $message): void
    {
        // 1. 客户端应该传递标准的json数据
        $message_data = json_decode($message, true);
        if (!$message_data) {
            return;
        }
        // 2. 封装进来的消息体
        $put = new PutMessage(
            $client_id,
            isset($message_data['type']) ? (string)$message_data['type'] : '',
            isset($message_data['event']) ? (string)$message_data['event'] : '',
            isset($message_data['data']) ? (array)$message_data['data'] : []
        );
        /*
         *  客户端登录
         */
        if ($put->type == MessageType::System && $put->event == SystemEnum::Login) {
            self::authEvent($put);
            return;
        }
        $isLogin = $put->login($_SESSION['user_id'] ?? 0,
            $_SESSION['user_type'] ?? '',
            $_SESSION['client'] ?? []
        );
        if (!$isLogin) {
            echo "用户未登录\r\n";
            // 未登录
            return;
        }


        // 3. 调度: 根据类型执行不同的业务
        switch ($put->type) {
            // 客户端回应服务端的心跳
            case MessageType::Pong:
                break;
            // 系统类型组操作
            case MessageType::System:
                switch ($put->event) {
                    case SystemEnum::Lock_Client:
                        /**
                         * 触发后续其他事件
                         */
                        Event::trigger("gateway.lockClient", [$put]);
                    break;
                }
                return;
        }
    }

    public static function onClose(string $client_id)
    {
        echo "客户端:{$client_id},关闭了\r\n";
        // 客户端离线的相关逻辑操作
        if (isset($_SESSION['group_list']) && is_array($_SESSION['group_list'])) {
            foreach ($_SESSION['group_list'] as $group) {
                /*
                 * 客户端退出了某个房间
                 */
                GatewayClientService::sendToGroup($group, new OutMessage(
                    SystemEnum::Client_quit_room
                ));
            }
        }
        /**
         * 触发后续其他事件
         */
        Event::trigger("gateway.clientClose", [$client_id]);
    }

    private static function authEvent(PutMessage $put): void
    {
        $token = $put->get('token', '');
        $clientVersion = $put->get('clientVersion', '');
        $clientName = $put->get('clientName', '');
        $clientId = $put->get('clientId', '');

        try {
            $token = str_replace('Bearer ', '', $token);
            $loginSrv = new LoginService();
            $auth = $loginSrv->checkUserAccessToken($token);
        } catch (ValidateException $e) {
            // 校验失败
            var_dump('E1.登录凭证校验失败: '.$e->getMessage());
            return;
        }
        /**
         * 加入同角色房间
         */
        $sysUser = SysUser::find($auth->userId);
        if (empty($sysUser)) {
            return;
        }

        // 登录
        $put->login($auth->userId, $auth->userType->value, [
            'id'      => $clientId,
            'name'    => $clientName,
            'version' => $clientVersion
        ]);
        /**
         * 记录登录凭证信息到$SESSION中
         */
        $_SESSION['user_id'] = $put->userId;
        $_SESSION['user_type'] = $put->userType;
        $_SESSION['client'] = [
            'id' => $put->getClientId(),
            'name' => $put->getClientName(),
            'version' => $put->getClientVersion()
        ];
        /**
         * 推送登录成功的消息到客户端
         */
        GatewayClientService::sendToClient($put->wsClientId, new OutMessage(
            SystemEnum::Login_SUCCESS,
            ['userId' => $auth->userId]
        ));
        /**
         *
         */
        GatewayClientService::bindUid($put->wsClientId, $auth->userId);
        /**
         * 加入已登录的用户公共大厅
         */
        GatewayClientService::joinGroup($put->wsClientId, 'PUBLIC_ROOM');
        /**
         * 加入同角色的房间
         */
        $sysUser->roles->each(function (SysRole $role) use ($put) {
            GatewayClientService::joinGroup($put->wsClientId, "ROLE_{$role['id']}");
        });
        /**
         * 触发后续其他事件
         */
        Event::trigger("gateway.clientLogin", [$put, $sysUser]);
    }
}
