import asyncio, os, sys, threading
from collections import deque

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from agentmain import GeneraticAgent
from chatapp_common import AgentChatMixin, ensure_single_instance, public_access, redirect_log, require_runtime, split_text
from llmcore import mykeys

try:
    from wecom_aibot_sdk import WSClient, generate_req_id
except Exception:
    print("Please install wecom_aibot_sdk to use WeCom: pip install wecom_aibot_sdk")
    sys.exit(1)

agent = GeneraticAgent(); agent.verbose = False
BOT_ID = str(mykeys.get("wecom_bot_id", "") or "").strip()
SECRET = str(mykeys.get("wecom_secret", "") or "").strip()
WELCOME = str(mykeys.get("wecom_welcome_message", "") or "").strip()
ALLOWED = {str(x).strip() for x in mykeys.get("wecom_allowed_users", []) if str(x).strip()}
PROCESSED_IDS, USER_TASKS = deque(maxlen=1000), {}


class WeComApp(AgentChatMixin):
    label, source, split_limit = "WeCom", "wecom", 1200

    def __init__(self):
        super().__init__(agent, USER_TASKS)
        self.client, self.chat_frames = None, {}

    async def send_text(self, chat_id, content):
        if not self.client or chat_id not in self.chat_frames:
            if chat_id not in self.chat_frames:
                print(f"[WeCom] no frame found for chat: {chat_id}")
            return
        frame = self.chat_frames[chat_id]
        for part in split_text(content, self.split_limit):
            await self.client.reply_stream(frame, generate_req_id("stream"), part, finish=True)

    async def on_text(self, frame):
        try:
            body = frame.body if hasattr(frame, "body") else frame.get("body", frame) if isinstance(frame, dict) else {}
            if not isinstance(body, dict):
                return
            msg_id = body.get("msgid") or f"{body.get('chatid', '')}_{body.get('sendertime', '')}"
            if msg_id in PROCESSED_IDS:
                return
            PROCESSED_IDS.append(msg_id)
            from_info = body.get("from", {}) if isinstance(body.get("from", {}), dict) else {}
            sender_id = str(from_info.get("userid", "") or "unknown")
            chat_id = str(body.get("chatid", "") or sender_id)
            content = str((body.get("text", {}) or {}).get("content", "") or "").strip()
            if not content:
                return
            if not public_access(ALLOWED) and sender_id not in ALLOWED:
                print(f"[WeCom] unauthorized user: {sender_id}")
                return
            self.chat_frames[chat_id] = frame
            print(f"[WeCom] message from {sender_id}: {content}")
            if content.startswith("/"):
                return await self.handle_command(chat_id, content)
            asyncio.create_task(self.run_agent(chat_id, content))
        except Exception:
            import traceback
            print("[WeCom] handle_message error")
            traceback.print_exc()

    async def on_enter_chat(self, frame):
        if WELCOME and self.client:
            try:
                await self.client.reply_welcome(frame, {"msgtype": "text", "text": {"content": WELCOME}})
            except Exception as e:
                print(f"[WeCom] welcome error: {e}")

    async def on_connected(self, frame):
        print("[WeCom] connected")

    async def on_authenticated(self, frame):
        print("[WeCom] authenticated")

    async def on_disconnected(self, frame):
        print("[WeCom] disconnected")

    async def on_error(self, frame):
        print(f"[WeCom] error: {frame}")

    async def start(self):
        self.client = WSClient({"bot_id": BOT_ID, "secret": SECRET, "reconnect_interval": 1000, "max_reconnect_attempts": -1, "heartbeat_interval": 30000})
        for event, handler in {
            "connected": self.on_connected,
            "authenticated": self.on_authenticated,
            "disconnected": self.on_disconnected,
            "error": self.on_error,
            "message.text": self.on_text,
            "event.enter_chat": self.on_enter_chat,
        }.items():
            self.client.on(event, handler)
        print("[WeCom] bot starting...")
        await self.client.connect_async()
        while True:
            await asyncio.sleep(1)


if __name__ == "__main__":
    _LOCK_SOCK = ensure_single_instance(19529, "WeCom")
    require_runtime(agent, "WeCom", wecom_bot_id=BOT_ID, wecom_secret=SECRET)
    redirect_log(__file__, "wecomapp.log", "WeCom", ALLOWED)
    threading.Thread(target=agent.run, daemon=True).start()
    asyncio.run(WeComApp().start())
