"""
agentpaas wechat — 微信 ClawBot iLink 桥接模块

将微信消息转发到 AgentPaaS agent，回复自动发回微信。

流程:
    微信用户发消息 → iLink 长轮询收到 → AgentPaaS agent 处理 → iLink 回复微信

用法:
    agentpaas wechat <agent_id>              # 绑定 agent 并开始监听
    agentpaas wechat <agent_id> --daemon     # 后台运行
    agentpaas wechat status                  # 查看连接状态
    agentpaas wechat stop                    # 停止后台进程
"""
from __future__ import annotations

import asyncio
import json
import os
import signal
import sys
import time
from pathlib import Path

# ── 配置 ────────────────────────────────────────────────────

CREDENTIALS_DIR = Path.home() / ".agentpaas" / "wechat"
CREDENTIALS_FILE = CREDENTIALS_DIR / "credentials.json"
PID_FILE = CREDENTIALS_DIR / "daemon.pid"

DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com"
DEFAULT_CDN_URL = "https://novac2c.cdn.weixin.qq.com/c2c"


def _save_credentials(data):
    CREDENTIALS_DIR.mkdir(parents=True, exist_ok=True)
    CREDENTIALS_DIR.chmod(0o700)
    CREDENTIALS_FILE.write_text(json.dumps(data, indent=2, ensure_ascii=False))
    CREDENTIALS_FILE.chmod(0o600)


def _load_credentials():
    if CREDENTIALS_FILE.exists():
        return json.loads(CREDENTIALS_FILE.read_text())
    return None


# ── 登录 ────────────────────────────────────────────────────

async def login():
    """扫码登录微信 ClawBot，返回凭证。"""
    from wechat_clawbot.auth.login_qr import (
        start_weixin_login_with_qr,
        wait_for_weixin_login,
    )

    print("🔗 微信 ClawBot 绑定")
    print("  1. 打开微信 → 设置 → 插件 → 开启 ClawBot")
    print("  2. 扫描下方二维码:\n")

    # 获取二维码
    qr_result = await start_weixin_login_with_qr(
        api_base_url=DEFAULT_BASE_URL,
        bot_type="3",
    )

    if qr_result.qrcode_url:
        try:
            import qrcode
            # 保存为图片并打开
            qr_img_path = CREDENTIALS_DIR / "login_qr.png"
            CREDENTIALS_DIR.mkdir(parents=True, exist_ok=True)
            qr = qrcode.QRCode(border=2, box_size=10)
            qr.add_data(qr_result.qrcode_url)
            qr.make(fit=True)
            img = qr.make_image(fill_color="black", back_color="white")
            img.save(str(qr_img_path))
            print("  二维码已保存: {}".format(qr_img_path))

            # macOS 自动打开图片
            import platform, subprocess as sp
            if platform.system() == "Darwin":
                sp.Popen(["open", str(qr_img_path)])
                print("  (已自动打开图片，请用微信扫码)")
            else:
                print("  请打开图片扫码: {}".format(qr_img_path))

            # 同时终端小尺寸显示
            qr_small = qrcode.QRCode(border=1, box_size=1)
            qr_small.add_data(qr_result.qrcode_url)
            qr_small.make(fit=True)
            qr_small.print_tty()
        except Exception:
            pass
        print("\n  备用链接: {}".format(qr_result.qrcode_url))
    else:
        print("  {}".format(qr_result.message))

    print("\n  等待扫码...", end="", flush=True)

    # 等待扫码确认
    login_result = await wait_for_weixin_login(
        session_key=qr_result.session_key,
        api_base_url=DEFAULT_BASE_URL,
        bot_type="3",
        timeout_ms=480_000,  # 8 分钟超时
        verbose=False,
    )

    if not login_result.connected:
        print(" 失败")
        print("  {}".format(login_result.message))
        return None

    print(" 成功!")

    creds = {
        "bot_token": login_result.bot_token,
        "account_id": login_result.account_id,
        "base_url": login_result.base_url or DEFAULT_BASE_URL,
        "user_id": login_result.user_id,
        "login_time": time.strftime("%Y-%m-%dT%H:%M:%S"),
    }
    _save_credentials(creds)
    print("  凭证已保存到 {}".format(CREDENTIALS_FILE))

    return creds


# ── Agent 调用 ──────────────────────────────────────────────

def call_agent_sync(agent_id, text):
    """同步调用 AgentPaaS agent (通过 HTTP API)。"""
    from agentpaas.__main__ import api
    try:
        data = api("POST", "/api/v1/agents/{}/run".format(agent_id),
                    {"input": text})
        return data.get("output", "(无回复)")
    except SystemExit:
        return "(Agent 调用失败，请检查 PaaS 服务是否在运行)"


_assistant = None


def call_agent_claude_code_sync(text):
    """通过 Claude Code CLI 调用 agent (无需 API Key)。"""
    global _assistant
    if _assistant is None:
        from pathlib import Path as _P
        _project_root = _P(__file__).resolve().parent.parent
        _agent_dir = _project_root / "agentexample"
        if str(_agent_dir) not in sys.path:
            sys.path.insert(0, str(_agent_dir))
        if str(_project_root) not in sys.path:
            sys.path.insert(0, str(_project_root))
        from agent67.core.assistant import PersonalAssistant
        _assistant = PersonalAssistant(model="sonnet", use_api=False)
    return _assistant.chat(text)


async def call_agent_async(agent_id, text, claude_code=False):
    """异步包装 agent 调用。"""
    loop = asyncio.get_event_loop()
    if claude_code:
        return await loop.run_in_executor(None, call_agent_claude_code_sync, text)
    return await loop.run_in_executor(None, call_agent_sync, agent_id, text)


# ── 消息处理 ────────────────────────────────────────────────

def _make_dispatch_handler(agent_id, base_url, bot_token, claude_code=False):
    """创建消息分发处理器。"""
    # 缓存每个用户的 context_token
    context_tokens = {}

    async def dispatch_reply(msg):
        """收到微信消息 → 调用 Agent → 回复微信。"""
        from wechat_clawbot.messaging.send import send_message_weixin
        from wechat_clawbot.api.client import WeixinApiOptions

        user_id = msg.from_user
        text = msg.body or ""
        media_info = ""

        # 保存 context_token
        if msg.context_token:
            context_tokens[user_id] = msg.context_token

        # 处理媒体消息
        if msg.media_type and msg.media_path:
            if msg.media_type.startswith("audio/"):
                media_info = " [语音消息]"
            elif msg.media_type.startswith("image/"):
                media_info = " [图片]"
            elif msg.media_type.startswith("video/"):
                media_info = " [视频]"
            else:
                media_info = " [文件: {}]".format(msg.media_type)

        if not text and not media_info:
            return

        input_text = text + media_info
        print("  📩 {} → \"{}\"".format(user_id[:16], input_text[:80]))

        # 调用 Agent
        t0 = time.time()
        reply = await call_agent_async(agent_id, input_text, claude_code=claude_code)
        elapsed = time.time() - t0
        print("  💬 ({:.1f}s) → \"{}\"".format(elapsed, reply[:80]))

        # 回复微信
        ctx_token = context_tokens.get(user_id, msg.context_token)
        if not ctx_token:
            print("  ⚠️  无 context_token，无法回复")
            return

        try:
            await send_message_weixin(
                to=user_id,
                text=reply,
                opts=WeixinApiOptions(
                    base_url=base_url,
                    token=bot_token,
                    context_token=ctx_token,
                ),
            )
            print("  ✓ 已回复")
        except Exception as e:
            print("  ✗ 回复失败: {}".format(e))

    return dispatch_reply


# ── 主监听循环 ──────────────────────────────────────────────

async def run_bridge(agent_id, creds, claude_code=False):
    """启动 iLink 长轮询桥接。"""
    from wechat_clawbot.monitor.monitor import MonitorOpts, monitor_weixin_provider

    base_url = creds["base_url"]
    bot_token = creds["bot_token"]
    account_id = creds["account_id"]
    backend = "Claude Code CLI (无需 API Key)" if claude_code else "PaaS API"

    print("\n🟢 微信 ↔ AgentPaaS 桥接已启动")
    print("  Agent:      {}".format(agent_id))
    print("  后端:       {}".format(backend))
    print("  Account:    {}".format(account_id))
    print("  API:        {}".format(base_url))
    print("  按 Ctrl+C 停止\n")

    stop_event = asyncio.Event()

    def _signal_handler():
        print("\n🔴 正在停止...")
        stop_event.set()

    loop = asyncio.get_event_loop()
    for sig in (signal.SIGINT, signal.SIGTERM):
        loop.add_signal_handler(sig, _signal_handler)

    dispatch = _make_dispatch_handler(agent_id, base_url, bot_token, claude_code=claude_code)

    opts = MonitorOpts(
        base_url=base_url,
        cdn_base_url=creds.get("cdn_base_url", DEFAULT_CDN_URL),
        token=bot_token,
        account_id=account_id,
        dispatch_reply=dispatch,
        log=lambda msg: print("  [iLink] {}".format(msg)),
        err_log=lambda msg: print("  [iLink ERROR] {}".format(msg)),
    )

    try:
        await monitor_weixin_provider(opts, stop_event)
    except Exception as e:
        print("  桥接异常: {}".format(e))
    finally:
        from wechat_clawbot.api.client import close_shared_client
        await close_shared_client()

    print("🔴 桥接已停止")


# ── 守护进程 ────────────────────────────────────────────────

def _write_pid():
    CREDENTIALS_DIR.mkdir(parents=True, exist_ok=True)
    PID_FILE.write_text(str(os.getpid()))


def _read_pid():
    if PID_FILE.exists():
        try:
            return int(PID_FILE.read_text().strip())
        except ValueError:
            pass
    return None


def _remove_pid():
    if PID_FILE.exists():
        PID_FILE.unlink()


# ── 公开入口 ────────────────────────────────────────────────

def _check_wechat_deps():
    """检查 wechat-clawbot 是否已安装。"""
    try:
        import wechat_clawbot  # noqa: F401
        return True
    except ImportError:
        print("wechat-clawbot 未安装。请运行:")
        print("  pip install wechat-clawbot>=0.2.0")
        return False


def cmd_wechat_start(agent_id, daemon=False, claude_code=False):
    """启动微信桥接。"""
    if not _check_wechat_deps():
        return

    # 检查凭证
    creds = _load_credentials()
    if not creds or not creds.get("bot_token"):
        creds = asyncio.run(login())
        if not creds:
            print("登录失败，退出。")
            return

    # 关联 agent_id
    creds["agent_id"] = agent_id
    _save_credentials(creds)

    if daemon:
        pid = os.fork()
        if pid > 0:
            print("后台运行中，PID: {}".format(pid))
            print("  查看状态: agentpaas wechat status")
            print("  停止: agentpaas wechat stop")
            return
        os.setsid()
        _write_pid()
        log_file = CREDENTIALS_DIR / "daemon.log"
        sys.stdout = open(log_file, "a")
        sys.stderr = sys.stdout

    try:
        asyncio.run(run_bridge(agent_id, creds, claude_code=claude_code))
    finally:
        if daemon:
            _remove_pid()


def cmd_wechat_status():
    """查看微信桥接状态。"""
    creds = _load_credentials()
    if not creds:
        print("未配置微信 ClawBot，请先运行: agentpaas wechat <agent_id>")
        return

    print("=== 微信 ClawBot 状态 ===")
    print("  Account:    {}".format(creds.get("account_id", "未知")))
    print("  Agent:      {}".format(creds.get("agent_id", "未绑定")))
    print("  登录时间:   {}".format(creds.get("login_time", "未知")))

    pid = _read_pid()
    if pid:
        try:
            os.kill(pid, 0)
            print("  守护进程:   运行中 (PID {})".format(pid))
        except OSError:
            print("  守护进程:   已停止 (PID {} 不存在)".format(pid))
            _remove_pid()
    else:
        print("  守护进程:   未运行")


def cmd_wechat_stop():
    """停止后台桥接进程。"""
    pid = _read_pid()
    if not pid:
        print("没有运行中的守护进程")
        return

    try:
        os.kill(pid, signal.SIGTERM)
        print("已发送停止信号到 PID {}".format(pid))
        # 等待退出
        for _ in range(10):
            try:
                os.kill(pid, 0)
                time.sleep(0.5)
            except OSError:
                break
        _remove_pid()
        print("守护进程已停止")
    except OSError:
        print("进程 {} 不存在".format(pid))
        _remove_pid()


def cmd_wechat_relogin(agent_id=None):
    """重新扫码登录。"""
    creds = asyncio.run(login())
    if creds and agent_id:
        creds["agent_id"] = agent_id
        _save_credentials(creds)
    print("重新登录完成")
