<?php
declare (strict_types = 1);

namespace app\command;

use app\apicom\model\StockPosition;
use swoole\server\WebSocketServer;
use think\console\Input;
use think\console\Output;
use think\facade\Db;
use util\NoticeRedis;
use util\QuotationRedis;
use util\RedisUtil;
use util\WsServerRedis;
use util\SysWsRedis;
/**
 * 模拟股票交易服务器
 */
class WebSocket extends WebSocketServer
{

    protected $host = '0.0.0.0';
    protected $port = 9501;

    protected function configure()
    {
        parent::configure();

        $this->setName('WebSocket')
            ->setDescription('信息服务器(WebSocket)');
    }

    protected function execute(Input $input, Output $output)
    {
        parent::execute($input, $output);
    }

    // Server On Start
    public function onStart(\Swoole\Websocket\Server $server)
    {
    }

    // Server On WorkerStart
    public function onWorkerStart(\Swoole\Websocket\Server $server, int $workerID)
    {
        // 实时公告
        $this->notice();
        // $this->monthly_position_notice();
        //error_reporting(E_ALL & ~E_NOTICE);
        // 清空所有客户端关系缓存数据
        RedisUtil::redis()->del(RedisUtil::redis()->keys('ws_client_*'));
    }

    // Server On Client Open
    public function onOpen(\Swoole\Websocket\Server $server, \Swoole\Http\Request $request)
    {
        
    }

    // Server On Client Message
    public function onMessage(\Swoole\Websocket\Server $server, \Swoole\Websocket\Frame $frame)
    {
        // 客户端ID
        $clientID = $frame->fd;
        // 解析客户端数据
        $recMsg = $this->parseClientData($frame->data, $clientID);
        $token  = $recMsg['Token'] ?? '';
        switch ($recMsg['Key']) {
            case 'Heartbeat':
                // 心跳
                $this->sendToClient($clientID, 'Heartbeat', 1);
                break;
            case 'NoticeRead':
                // 设置用户已读公告
                $this->setNoticeRead($clientID, $recMsg);
                break;
            case 'MarketData':
                //股票行情
                $this->getMarketData($clientID,$recMsg['Data']);
                //$this->sendToClient($clientID,'MarketData', 1, '实时行情获取成功',$recMsg);
                break;
            case 'MoneyRecord':
                // 实时充值/体现信息
                $this->getMoneyRecord($clientID);
                break;
            default:
                $this->sendToClient($clientID, 'Error', 0, '无效的参数');
        }
    }

    // Server On Client Close
    public function onClose(\Swoole\WebSocket\Server $server, int $fd)
    {
    }

    /**
     * 解析客户端数据，只接受json格式的数据
     *
     * @param string $data 客户端数据
     * @param int    $clientID 客户端ID
     *
     * @return array|mixed
     */
    protected function parseClientData($data, $clientID)
    {
        // 解析数据
        try {
            $recMsg = json_decode($data, true);
        } catch (\Exception $e) {
            $recMsg['Key'] = 'Error';
        }
        // 用户请求合法性判断
        $avlKeys   = ['Heartbeat', 'NoticeRead', 'MarketData'];
        if (isset($recMsg['Key']) && in_array($recMsg['Key'], $avlKeys)) {
            // 缓存token与$clientID之间的关系
            $token = $recMsg['Token'] ?? '';
            if ($token) {
                $userData = $token ? RedisUtil::getToken($token) : [];
                $userID   = $userData['uid'] ?? 0;
                // 缓存 $userID 与 $clientID 之间的关系
                if ($userID) WsServerRedis::cacheWsClient($userID, $clientID);
            }
        }
        // 管理请求合法性判断
        $adminKeys = ['MoneyRecord'];
        if (isset($recMsg['Key']) && in_array($recMsg['Key'], $adminKeys)) {
            // 缓存token与$clientID之间的关系
            $token = $recMsg['Token'] ?? '';
            if ($token) {
                $adminData = $token ? RedisUtil::getAdminToken($token) : [];
                $role      = $adminData['role'] ?? '';
                if ($role !== "1") $recMsg['Key'] = 'Error';
                // 缓存 $userID 与 $clientID 之间的关系
                SysWsRedis::cacheTokenClient($token, $clientID);
            }
        }
        return $recMsg;
    }
    // 查看充值/提现申请
    public function getMoneyRecord($clientID)
    {
        $rech_count = Db::name('money_recharge')->where('status',0)->count();
        $cash_count = Db::name('money_withdraw')->where('status',0)->count();
        
        $this->sendToClient($clientID, 'BackMoneyRecord', 1, '请求成功',['recharge'=>$rech_count,'withdraw'=>$cash_count]);
    }
    // 请求个股股票
    public function getMarketData($clientID,$stockdata)
    {
        if (isset($stockdata['stock']) && $stockdata['stock']) {
            $code = $stockdata['stock'];
            $data['stock'] = RedisUtil::getQuotationData($code,toMarket($code));
        }
        if (isset($stockdata['posit']) && $stockdata['posit']) {
            $subid = $stockdata['posit'];
            $data['posit'] = StockPosition::getPositionFind($subid);
        }
        if($data) $this->sendToClient($clientID,'MarketData', 1, '实时行情获取成功',$data);
    }
    // 实时公告
    public function notice()
    {
        try {
            swoole_timer_tick(1000, function () {
                // 客户端列表
                $clientList = $this->server->connection_list(0);
                if (is_array($clientList) && count($clientList)) {
                    foreach ($clientList as $fd) {
                        $userID      = WsServerRedis::getWsUserID($fd);
                        $notPushList = NoticeRedis::getNotPushList($userID);
                        if (count($notPushList)) {
                            $this->sendToClient($fd, 'Notice', 1, '', $notPushList);
                        }
                    }
                }
            });
        } catch (\Exception $e) {
            $this->writeTrace($e);
        }
    }

    // 设置公告已读
    public function setNoticeRead($clientID, $recMsg)
    {
        $noticeID = $recMsg['Data']['notice_id'] ?? 0;
        $userID   = WsServerRedis::getWsUserID($clientID);
        NoticeRedis::setNoticeRead($userID, $noticeID);
    }

    // 设置月管理费公告已读
    public function setMonthlyNoticeRead($clientID, $recMsg)
    {
        $noticeID = $recMsg['Data']['position_id'] ?? 0;
        $userID   = WsServerRedis::getWsUserID($clientID);
        MonthlyNoticeRedis::setNoticeRead($userID, $noticeID);
    }

    /**
     * 向指定用户的发送消息
     * -- 多端发送
     *
     * @param int    $userID 用户ID
     * @param string $key 消息KEY
     * @param int    $code 状态 1 成功消息， 0 错误消息
     * @param string $msg 提示信息
     * @param array  $data 消息数据
     */
    protected function sendToUser($userID, $key, $code, $msg = '', $data = [])
    {
        go(function () use ($userID, $key, $code, $msg, $data) {
            // $userID 必须大于0
            if ($userID <= 0) return false;

            // 发送的数据
            $data = ['Key' => $key, 'code' => $code, 'msg' => $msg, 'data' => $data,];

            // 向用户的所有客户端发送消息
            $clientIDList = WsServerRedis::getWsClientIDList($userID);
            if (is_array($clientIDList) && count($clientIDList)) {
                foreach ($clientIDList as $fd) {
                    try {
                        if ($this->server->connection_info($fd)) {
                            $this->server->push($fd, json_encode($data));
                        }
                    } catch (\Exception $e) {
                    } catch (\Throwable $e) {
                    }
                }
            }
        });
    }

    /**
     * 向指定的客户端发送消息
     * -- 单端发送
     *
     * @param int    $fd websocket 客户端ID
     * @param string $key
     * @param int    $code
     * @param string $msg
     * @param array  $data
     */
    protected function sendToClient($fd, $key, $code, $msg = '', $data = [])
    {
        $data = [
            'Key'  => $key,
            'code' => $code,
            'msg'  => $msg,
            'data' => $data,
        ];

        if ($this->server->connection_info($fd)) {
            $this->server->push($fd, json_encode($data));
        }
    }
    
}
