
import asyncio
import logging
import time

import requests
import json
import grpc.aio

from grpc_client import GrpcClient
from grpc_listener import GrpcListener
from models import ResultModel

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 定义全局变量用于状态跟踪
isopentab = 0  # 标记是否已执行open_tab
has_received_adaptable = False  # 标记是否已接收adaptable_activities指令
first_fetch_after_adaptable = False  # 标记adaptable_activities后的首次fetch_ui

# 认证配置
auth_config = {
    "headers": {
        "Content-Type": "application/x-www-form-urlencoded",
        "Sm4-Key": "dUVNkTJLlzMHPqBTZh085jT0N6S7930l6sgddJsYSC8=",
        "Cookie": "ENCRYPT_KEY=f48bf3-05fe-4978"
    },
    "params": {
        "username": "api_tester",
        "password": "$SM4$Nd/e40vIS6UD9VMaSOmYGlrBORfo7r5/1z9D5d4E4es=$4MS$",
        "tenantCode": "API_test",
        "scope": "all",
        "client_id": "a68ad587830d41aebf418a919006353e",
        "client_secret": "Ninetech@123",
        "grant_type": "password"
    }
}

# 定义需要创建gRPC连接的3个client_id
oclient = "a68ad587830d41aebf418a919006353e"
CLIENT_IDS = [
    oclient,
    oclient+"_Default",
    oclient+"_InPrivate"
]


async def handle_message(request_path, request_data):
    global isopentab
    global has_received_adaptable
    global first_fetch_after_adaptable

    if request_path == "list_mcp_servers":  # 特定指令1
        return ResultModel(code=200, data=[], message="")

    elif request_path == "adaptable_activities":  # 特定指令2
        # 接收该指令后，标记状态（后续首次fetch_ui需要返回空白页）
        has_received_adaptable = True
        first_fetch_after_adaptable = True
        return ResultModel(code=200, data=[{"id": "close_tab", "name": "close_tab", "displayName": "关闭标签页",
                                            "description": "默认关闭当前的标签页,is_all_close参数可控制是否标签页全部关闭,is_close_children参数控制是否关闭当前页的子标签页",
                                            "parametersSchema": {"description": "关闭页签", "properties": {
                                                "is_all_close": {"default": False, "description": "是否全部关闭",
                                                                 "title": "Is All Close", "type": "boolean"},
                                                "is_close_children": {"default": False, "description": "是否关闭子页签",
                                                                      "title": "Is Close Children", "type": "boolean"}},
                                                                 "title": "CloseTab", "type": "object", "required": []},
                                            "elementIdJsonPaths": [], "icon": "close_tab", "timeout": 180},
                                           {"id": "keyboard_entry", "name": "keyboard_entry", "displayName": "键盘输入",
                                            "description": "键盘输入", "parametersSchema": {"description": "键盘输入",
                                                                                            "properties": {"key_code": {
                                                                                                "description": "输入键盘码或组合快捷键，支持Playwright键盘码和组合快捷键，例如:[`F1`, `F2`, `F3`, `F4`, `F5`, `F6`, `F7`, `F8`, `F9`, `F10`, `F11`, `F12`, `Digit0`, `Digit1`, `Digit2`, `Digit3`, `Digit4`, `Digit5`, `Digit6`, `Digit7`, `Digit8`, `Digit9`, `KeyA`, `KeyB`, `KeyC`, `KeyD`, `KeyE`, `KeyF`, `KeyG`, `KeyH`, `KeyI`, `KeyJ`, `KeyK`, `KeyL`, `KeyM`, `KeyN`, `KeyO`, `KeyP`, `KeyQ`, `KeyR`, `KeyS`, `KeyT`, `KeyU`, `KeyV`, `KeyW`, `KeyX`, `KeyY`, `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,`Delete`, `Escape`, `ArrowDown`, `PageDown`, `PageUp`, `ArrowLeft`,`ArrowRight`, `ArrowUp`]，Shortcuts such as `Control+o`, `Control++`, `Control+Shift+T` , `Alt+ArrowRight` or `Alt+ArrowLeft`",
                                                                                                "enum": ["F1", "F2",
                                                                                                         "F3", "F4",
                                                                                                         "F5", "F6",
                                                                                                         "F7", "F8",
                                                                                                         "F9", "F10",
                                                                                                         "F11", "F12",
                                                                                                         "Digit0",
                                                                                                         "Digit1",
                                                                                                         "Digit2",
                                                                                                         "Digit3",
                                                                                                         "Digit4",
                                                                                                         "Digit5",
                                                                                                         "Digit6",
                                                                                                         "Digit7",
                                                                                                         "Digit8",
                                                                                                         "Digit9",
                                                                                                         "KeyA", "KeyB",
                                                                                                         "KeyC", "KeyD",
                                                                                                         "KeyE", "KeyF",
                                                                                                         "KeyG", "KeyH",
                                                                                                         "KeyI", "KeyJ",
                                                                                                         "KeyK", "KeyL",
                                                                                                         "KeyM", "KeyN",
                                                                                                         "KeyO", "KeyP",
                                                                                                         "KeyQ", "KeyR",
                                                                                                         "KeyS", "KeyT",
                                                                                                         "KeyU", "KeyV",
                                                                                                         "KeyW", "KeyX",
                                                                                                         "KeyY", "KeyZ",
                                                                                                         "Backquote",
                                                                                                         "Minus",
                                                                                                         "Equal",
                                                                                                         "Backslash",
                                                                                                         "Backspace",
                                                                                                         "Tab",
                                                                                                         "Delete",
                                                                                                         "Escape",
                                                                                                         "ArrowDown",
                                                                                                         "PageDown",
                                                                                                         "PageUp",
                                                                                                         "ArrowLeft",
                                                                                                         "ArrowRight",
                                                                                                         "ArrowUp",
                                                                                                         "Control+o",
                                                                                                         "Control++",
                                                                                                         "Control+Shift+T",
                                                                                                         "Alt+ArrowRight",
                                                                                                         "Alt+ArrowLeft"],
                                                                                                "title": "Key Code",
                                                                                                "type": "string"}},
                                                                                            "required": ["key_code"],
                                                                                            "title": "KeyboardEntry",
                                                                                            "type": "object"},
                                            "elementIdJsonPaths": [], "icon": "keyboard_entry", "timeout": 180},
                                           {"id": "navigate_page", "name": "navigate_page", "displayName": "跳转到网页",
                                            "description": "跳转到网页", "parametersSchema": {"$defs": {
                                               "NavigatePageType": {
                                                   "enum": ["go_to", "go_back", "go_forward", "refresh"],
                                                   "title": "NavigatePageType", "type": "string"}},
                                                                                              "description": "跳转到网页",
                                                                                              "properties": {
                                                                                                  "url": {"default": "",
                                                                                                          "description": "需要打开的url",
                                                                                                          "title": "Url",
                                                                                                          "type": "string"},
                                                                                                  "type": {
                                                                                                      "$ref": "#/$defs/NavigatePageType",
                                                                                                      "description": "需要操作的类型"}},
                                                                                              "required": ["type"],
                                                                                              "title": "NavigatePage",
                                                                                              "type": "object"},
                                            "elementIdJsonPaths": [], "icon": "navigate_page", "timeout": 180},
                                           {"id": "sleep_seconds", "name": "sleep_seconds", "displayName": "等待",
                                            "description": "此工具的用途是在目标无法达成时进行等待，最多等待2次",
                                            "parametersSchema": {"description": "等待", "properties": {
                                                "wait_seconds": {"default": 5,
                                                                 "description": "默认5秒，可修改为1-5秒时间",
                                                                 "title": "Wait Seconds", "type": "integer"}},
                                                                 "title": "SleepSeconds", "type": "object",
                                                                 "required": []}, "elementIdJsonPaths": [],
                                            "icon": "sleep_seconds", "timeout": 180},
                                           {"id": "switch_page", "name": "switch_page", "displayName": "切换页面",
                                            "description": "切换页面", "parametersSchema": {"description": "切换页面",
                                                                                            "properties": {"page_id": {
                                                                                                "description": "需要切换的页面id，如果id对应的页面不存在将不会切换成功",
                                                                                                "title": "Page Id",
                                                                                                "type": "string"}},
                                                                                            "required": ["page_id"],
                                                                                            "title": "SwitchPage",
                                                                                            "type": "object"},
                                            "elementIdJsonPaths": [], "icon": "switch_page", "timeout": 180},
                                           {"id": "click", "name": "click", "displayName": "点击元素",
                                            "description": "点击某个元素",
                                            "parametersSchema": {"description": "点击某个元素", "properties": {
                                                "element_id": {"description": "需要操作元素的id", "title": "Element Id",
                                                               "type": "string"}}, "required": ["element_id"],
                                                                 "title": "ClickElement", "type": "object"},
                                            "elementIdJsonPaths": ["$.element_id"], "icon": "click", "timeout": 180},
                                           {"id": "input_text", "name": "input_text", "displayName": "输入文本",
                                            "description": "输入文本", "parametersSchema": {"description": "输入内容",
                                                                                            "properties": {
                                                                                                "element_id": {
                                                                                                    "description": "需要操作元素的id",
                                                                                                    "title": "Element Id",
                                                                                                    "type": "string"},
                                                                                                "content": {
                                                                                                    "description": "需要输入的内容",
                                                                                                    "title": "Content",
                                                                                                    "type": "string"}},
                                                                                            "required": ["element_id",
                                                                                                         "content"],
                                                                                            "title": "InputText",
                                                                                            "type": "object"},
                                            "elementIdJsonPaths": ["$.element_id"], "icon": "input_text",
                                            "timeout": 180}, {"id": "screenshot_element", "name": "screenshot_element",
                                                              "displayName": "元素截图", "description": "截图某个元素",
                                                              "parametersSchema": {"description": "截图某个元素",
                                                                                   "properties": {"element_id": {
                                                                                       "description": "需要操作元素的id",
                                                                                       "title": "Element Id",
                                                                                       "type": "string"}},
                                                                                   "required": ["element_id"],
                                                                                   "title": "ScreenshotElement",
                                                                                   "type": "object"},
                                                              "elementIdJsonPaths": ["$.element_id"],
                                                              "icon": "screenshot_element", "timeout": 180},
                                           {"id": "select_option", "name": "select_option", "displayName": "选择选项",
                                            "description": "选择某个选项，仅适用于radio,checkbox,select,option标签及点击后有dropdown menu效果的标签。label，value，index三者至少填一个",
                                            "parametersSchema": {"description": "选择某个选项", "properties": {
                                                "element_id": {"description": "需要操作元素的id", "title": "Element Id",
                                                               "type": "string"},
                                                "label": {"anyOf": [{"type": "string"}, {"type": "None"}],
                                                          "default": None, "description": "需要选择的选项的label",
                                                          "title": "Label"},
                                                "value": {"anyOf": [{"type": "string"}, {"type": "None"}],
                                                          "default": None, "description": "需要选择的选项的value",
                                                          "title": "Value"},
                                                "index": {"anyOf": [{"type": "integer"}, {"type": "None"}],
                                                          "default": None, "description": "需要选择的选项的index索引",
                                                          "title": "Index"}}, "required": ["element_id"],
                                                                 "title": "SelectOption", "type": "object"},
                                            "elementIdJsonPaths": ["$.element_id"], "icon": "select_option",
                                            "timeout": 180},
                                           {"id": "slider_bar", "name": "slider_bar", "displayName": "拖动滑块",
                                            "description": "拖动滑块", "parametersSchema": {"description": "拖动滑块",
                                                                                            "properties": {
                                                                                                "element_id": {
                                                                                                    "description": "需要操作元素的id",
                                                                                                    "title": "Element Id",
                                                                                                    "type": "string"},
                                                                                                "value": {
                                                                                                    "description": "滑块设定值",
                                                                                                    "title": "Value",
                                                                                                    "type": "number"},
                                                                                                "min_value": {
                                                                                                    "description": "必须给出滑块可拖动最大值，根据页面元素获取",
                                                                                                    "title": "Min Value",
                                                                                                    "type": "number"},
                                                                                                "max_value": {
                                                                                                    "description": "必须给出滑块可拖动最小值，根据页面元素获取",
                                                                                                    "title": "Max Value",
                                                                                                    "type": "number"}},
                                                                                            "required": ["element_id",
                                                                                                         "value",
                                                                                                         "min_value",
                                                                                                         "max_value"],
                                                                                            "title": "SliderBar",
                                                                                            "type": "object"},
                                            "elementIdJsonPaths": ["$.element_id"], "icon": "slider_bar",
                                            "timeout": 180},
                                           {"id": "upload_file", "name": "upload_file", "displayName": "上传文件",
                                            "description": "上传文件", "parametersSchema": {"description": "上传文件",
                                                                                            "properties": {
                                                                                                "element_id": {
                                                                                                    "description": "需要操作元素的id",
                                                                                                    "title": "Element Id",
                                                                                                    "type": "string"},
                                                                                                "file_id": {
                                                                                                    "description": "文件ID，从文件空间中选取",
                                                                                                    "title": "File Id",
                                                                                                    "type": "string"}},
                                                                                            "required": ["element_id",
                                                                                                         "file_id"],
                                                                                            "title": "UploadFile",
                                                                                            "type": "object"},
                                            "elementIdJsonPaths": ["$.element_id"], "icon": "upload_file",
                                            "timeout": 180}],
                           message="")

    elif request_path == "get_current_tab":
        return ResultModel(code=200, data={
            "id": "test_tab",
            "url": "https://www.baidu.com/",
            "title": "百度一下，你就知道",
            "screenshot": []
        }, message="")

    elif request_path == "open_tab":
        # 执行open_tab后，标记状态（后续fetch_ui需要返回百度页面）
        isopentab = 1
        return ResultModel(code=200, data={
            "id": "test_tab",
            "url": "https://www.baidu.com/",
            "title": "百度一下，你就知道",
            "screenshot": []
        }, message="")

    elif request_path == "fetch_ui":
        # 核心逻辑：根据不同状态返回不同内容
        if first_fetch_after_adaptable:
            # 情况1：adaptable_activities后的首次fetch_ui，返回空白页
            first_fetch_after_adaptable = False  # 仅首次生效
            return ResultModel(
                code=200,
                data={
                    "url": "about:blank",
                    "content": "当前页面为空白页",
                    "elementIdSelectorMapping": {},
                    "screenshotsBase64": [],
                    "text": "",
                    "page_tabs": []
                },
                message=""
            )
        else:
            # 情况2：open_tab后的fetch_ui，返回百度页面
            isopentab = 0  # 重置状态（确保仅生效一次）
            time.sleep(5)  # 模拟加载延迟
            return ResultModel(
                code=200,
                data={
                    "url": "https://www.baidu.com/",
                    "content": "<a href=\"http://news.baidu.com\" id=\"pcWI\">新闻</a><a href=\"https://www.hao123.com?src=from_pc\" id=\"KkD4\">hao123</a><a href=\"http://map.baidu.com\" id=\"ZsH9\">地图</a><a href=\"http://tieba.baidu.com/\" id=\"Ggy7\">贴吧</a><a href=\"https://haokan.baidu.com/?sfrom=baidu-top\" id=\"AOeX\">视频</a><a href=\"http://image.baidu.com/\" id=\"Xd1C\">图片</a><a href=\"https://pan.baidu.com?from=1026962h\" id=\"8IWL\">网盘</a><a href=\"https://wenku.baidu.com/?fr=bdpcindex\" id=\"lCsO\">文库</a><a href=\"https://chat.baidu.com/search?isShowHello=1&pd=csaitab&setype=csaitab&extParamsJson=%7B%22enter_type%22%3A%22home_tab%22%7D\" id=\"b5rX\"><img class=\"s-top-tab-image\" id=\"5QSr\"></a><a href=\"http://www.baidu.com/more/\" name=\"tj_briicon\" id=\"lduS\">更多</a><a href=\"https://senode.baidu.com/seop-landing/knowledge_question?ref_sa=searchpromo_selftask_dati_shouyetab\" id=\"SnLp\"><div class=\"operate-image\" id=\"5eWj\"></div></a><span name=\"tj_settingicon\" id=\"LoOR\">设置</span><a href=\"https://passport.baidu.com/v2/?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2F&sms=5\" name=\"tj_login\" id=\"iGko\">登录</a><span class=\"soutu-btn\" id=\"XlOq\"></span><input name=\"wd\" value maxlength=\"255\" placeholder=\"王曼昱亮相美国大满贯海报\" id=\"Ie3z\"><input type=\"submit\" value=\"百度一下\" id=\"zqXv\"><a href=\"https://chat.baidu.com/search?extParams=%7B%22enter_type%22%3A%22ai_explore_home%22%7D&isShowHello=1\" id=\"E13Q\"><div class=\"new_search_guide_bub_container_left\" id=\"jv0S\"><svg></svg><svg></svg></div><div class=\"new_search_guide_bub_container_center\" id=\"j8J7\">AI搜索已支持「DeepSeek-R1」最新版</div><div class=\"new_search_guide_bub_container_right\" id=\"dHgS\">立即体验</div></a><div aria-label=\"百度热搜\" id=\"E7v6\"><i class=\"c-icon hot-title-icon\" id=\"y7ZQ\"></i><i class=\"c-icon arrow\" id=\"Ekyf\"></i></div><a class=\"hot-refresh c-font-normal c-color-gray2\" id=\"lNcM\"><i class=\"c-icon refresh-icon\" id=\"XZkU\"></i><span class=\"hot-refresh-text\" id=\"hF5v\">换一换</span></a><a href=\"{{_86d720161c56c8c50ca074b8addea74e08f618a7d03681af9f56b5bf60a6e109}}\" id=\"914C\"><i class=\"c-icon title-content-top-icon c-color-red c-gap-right-small\" id=\"6EZe\"></i><span class=\"title-content-title\" id=\"R5B4\">世界上最大的政党是如何炼成的</span></a><a href=\"{{_0609df375efd9ef496de58f77a745debd62d2390ee7b5c6861d3c38f98b4f2b6}}\" id=\"7t6N\"><span class=\"title-content-index c-index-single c-index-single-hot5 \" id=\"p5Zy\">5</span><span class=\"title-content-title\" id=\"FqiJ\">《新华字典》称小孩是累赘</span></a><a href=\"{{_0d7c4837c7faf638c147babd6e8a213b9a1cd92f94e8e24f5c502940e96c5833}}\" id=\"ayzg\"><span class=\"title-content-index c-index-single c-index-single-hot1 \" id=\"vJa0\">1</span><span class=\"title-content-title\" id=\"RQ4p\">高校引进高层次人才 第2名举报第1名</span></a><span class=\"title-content-mark ie-vertical c-text c-gap-left-small c-text-new\" id=\"z7Cd\">新</span><a href=\"{{_e6b1b4573d70f6f8f7fd7d217d73aff2aba5b9e3746e6a9783470b3330bd1917}}\" id=\"ff9J\"><span class=\"title-content-index c-index-single c-index-single-hot6 \" id=\"ovY0\">6</span><span class=\"title-content-title\" id=\"0rtw\">党的旗帜所指 即是网警铁翼所向</span></a><a href=\"{{_13ec6819a42ac28f147cb75ea1b8877a3a7296df40f4a456963f38d2adcba54d}}\" id=\"6TtQ\"><span class=\"title-content-index c-index-single c-index-single-hot2 \" id=\"EAry\">2</span><span class=\"title-content-title\" id=\"DVrk\">韦东奕晋升北大长聘副教授？校方回应</span></a><span class=\"title-content-mark ie-vertical c-text c-gap-left-small c-text-new\" id=\"Y2ua\">新</span><a href=\"{{_4d4c744b982ab1cd8436ea8806a2785a8c2d258bfdd57828e543080616d50704}}\" id=\"4240\"><span class=\"title-content-index c-index-single c-index-single-hot7 \" id=\"dXlE\">7</span><span class=\"title-content-title\" id=\"k2E1\">广州男子开车看手机撞人致3死3伤</span></a><span class=\"title-content-mark ie-vertical c-text c-gap-left-small c-text-new\" id=\"JA4p\">新</span><a href=\"{{_2d2cb344298e3c01a7eb639603d5d60037253006c48d5a997856bc86d5e01c9b}}\" id=\"ORRe\"><span class=\"title-content-index c-index-single c-index-single-hot3 \" id=\"j3h2\">3</span><span class=\"title-content-title\" id=\"Bmkd\">香港回归祖国28周年</span></a><a href=\"{{_c938881ae5de8a4a2a8395ee6482ca79a7d140eb1c5a7b0d0ddf48d57e9869b9}}\" id=\"hSRq\"><span class=\"title-content-index c-index-single c-index-single-hot8 \" id=\"aTwd\">8</span><span class=\"title-content-title\" id=\"9XGF\">男子8公斤黄金卖了612万赚312万</span></a><a href=\"{{_833e2addd1c5511c52653c92a46d9a996b7bfa3b7ba32168502057514e315095}}\" id=\"Qb7j\"><span class=\"title-content-index c-index-single c-index-single-hot4 \" id=\"iegt\">4</span><span class=\"title-content-title\" id=\"ubEb\">女子遇已故同学父亲摆摊：买下所有瓜</span></a><span class=\"title-content-mark ie-vertical c-text c-gap-left-small c-text-new\" id=\"PSS1\">新</span><a href=\"{{_8ae1eeb912dc25e1f8532a67bee6c7923fbf8fbf6b9ecc3cc0e13641c7d8e826}}\" id=\"qDqK\"><span class=\"title-content-index c-index-single c-index-single-hot9 \" id=\"ToGA\">9</span><span class=\"title-content-title\" id=\"3IYi\">客机高空急坠亲历者：给老公写了遗书</span></a><span class=\"title-content-mark ie-vertical c-text c-gap-left-small c-text-hot\" id=\"ZSGD\">热</span><a href=\"//home.baidu.com\" id=\"G6xr\">关于百度</a><a href=\"http://ir.baidu.com\" id=\"F2zX\">About Baidu</a><a href=\"//www.baidu.com/duty\" id=\"cN4M\">使用百度前必读</a><a href=\"https://help.baidu.com/question?prod_id=1\" id=\"nvcN\">帮助中心</a><a href=\"https://e.baidu.com/?refer=1271\" id=\"2xUA\">企业推广</a><span class=\"c-icon\" id=\"6r4L\"></span><span class=\"c-icon c-color-gray2\" id=\"Bdmj\"></span>",
                    "elementIdSelectorMapping": {},
                    "screenshotsBase64": [],
                    "text": "",
                    "page_tabs": [{"id": "B9EF2368E1361855B8603FE716C87B69", "title": "百度一下，你就知道",
                                   "url": "https://www.baidu.com/", "active": True, "children": []}]
                },
                message=""
            )
        # else:
        #     # 其他情况默认返回空白页（可根据需求调整）
        #     return ResultModel(
        #         code=200,
        #         data={
        #             "url": "about:blank",
        #             "content": "当前页面为空白页",
        #             "elementIdSelectorMapping": {},
        #             "screenshotsBase64": [],
        #             "text": "",
        #             "page_tabs": []
        #         },
        #         message=""
        #     )

    # 默认响应
    return ResultModel(code=200, data={"message": "请求处理成功"})


# 心跳检测任务
async def send_heartbeat(auth_token):
    """每60秒发送一次心跳请求，首次为login状态，后续为keepalive"""
    heartbeat_url = "http://bitagent.sit.ninetechone.com/api/v1/common/heartbeat"
    headers = {
        "Authorization": auth_token,
        "Content-Type": "application/json"
    }
    base_data = {"client_id": "a68ad587830d41aebf418a919006353e"}
    is_first = True  # 标记是否为首次发送

    while True:
        try:
            # 首次发送login状态，后续发送keepalive
            data = {**base_data, "user_state": "login"} if is_first else {**base_data, "user_state": "keepalive"}
            if is_first:
                is_first = False

            # 发送心跳请求
            response = requests.post(
                url=heartbeat_url,
                headers=headers,
                json=data
            )
            response.raise_for_status()
            logger.info(f"心跳发送成功 [状态: {data['user_state']}], 响应码: {response.status_code}")

        except requests.RequestException as e:
            logger.error(f"心跳发送失败: {str(e)}")

        # 等待60秒后再次发送
        await asyncio.sleep(60)


# 为每个listener单独发送ping
async def send_ping(listener):
    while True:
        await asyncio.sleep(5)
        await listener.send_to_all_stream(requestId="ping", body="#ping", type=-1)


async def main():
    try:
        # 获取访问令牌
        token_res = requests.post(
            url="http://accounts-4j.sit.ninetechone.com/oauth/token",
            headers=auth_config["headers"],
            params=auth_config["params"]
        )
        token_res.raise_for_status()
        access_token = token_res.json()["access_token"]
        auth_token = f"Bearer {access_token}"  # 统一的认证token

        # 获取gRPC配置
        config_res = requests.get("http://bitagent.sit.ninetechone.com/api/system/config/client")
        config_res.raise_for_status()
        config_data = json.loads(config_res.json()["data"])
        grpc_host = config_data["grpc_host"]
        grpc_use_ssl = config_data["grpc_use_ssl"]
        logging.info(f"gRPC配置加载完成: host={grpc_host}, use_ssl={grpc_use_ssl}")

        # 启动心跳任务
        heartbeat_task = asyncio.create_task(send_heartbeat(auth_token))

        # 为每个client_id创建独立的gRPC连接和监听器
        listeners = []
        ping_tasks = []
        for client_id in CLIENT_IDS:
            # 创建gRPC客户端
            client = GrpcClient()
            client.set_header(token=auth_token, client_id=client_id)  # 使用统一token和当前client_id

            # 连接gRPC服务器
            connected = await client.connect(address=grpc_host, use_ssl=grpc_use_ssl)
            if not connected:
                logger.error(f"client_id={client_id} 连接gRPC服务器失败，跳过该客户端")
                continue

            # 创建并启动监听器
            listener = GrpcListener(client)
            listener.message_received = handle_message  # 共用消息处理逻辑
            listener.start()
            listeners.append(listener)
            logger.info(f"client_id={client_id} gRPC连接成功并启动监听器")

            # 为该监听器创建ping任务
            ping_task = asyncio.create_task(send_ping(listener))
            ping_tasks.append(ping_task)

        # 运行所有任务（所有ping任务 + 心跳任务）
        all_tasks = ping_tasks + [heartbeat_task]
        await asyncio.gather(*all_tasks)

    except requests.RequestException as e:
        logger.error(f"HTTP请求错误: {e}")
    except grpc.aio.AioRpcError as e:
        logger.error(f"gRPC通信错误: {e}")
    except Exception as e:
        logger.error(f"未预期错误: {e}")
    finally:
        # 优雅关闭所有监听器
        for listener in listeners:
            await listener.stop()
        logger.info("所有gRPC监听器已停止")


if __name__ == "__main__":
    asyncio.run(main())
