"""微信三项验收 + P0 路由离线回归（不依赖真机）

确保 frida 一上线即可跑通：请求模型字段、路由可导入、159 action 对齐。
"""

from __future__ import annotations

import sys
from pathlib import Path

import pytest

APP = Path(__file__).resolve().parents[1] / "app"
if str(APP) not in sys.path:
    sys.path.insert(0, str(APP))


def test_send_message_request_fields():
    from routers.unified import SendMessageRequest
    f = SendMessageRequest.model_fields
    for k in ("device_id", "platform", "to_id", "content", "msg_type"):
        assert k in f, f"SendMessageRequest 缺字段 {k}"


def test_get_messages_request_fields():
    from routers.unified import GetMessagesRequest
    f = GetMessagesRequest.model_fields
    for k in ("device_id", "platform", "conversation_id", "limit"):
        assert k in f, f"GetMessagesRequest 缺字段 {k}"


def test_post_moments_request_fields():
    from routers.unified import PostMomentsRequest
    f = PostMomentsRequest.model_fields
    for k in ("device_id", "platform", "content", "images"):
        assert k in f, f"PostMomentsRequest 缺字段 {k}"


def test_wechat_159_actions_aligned():
    # 仓库根 sdk/skills 会抢占 "skills" 命名空间，这里按 app 下的真实文件稳健加载
    import importlib.util

    skill_path = APP / "skills" / "wechat" / "skill_v2.py"
    assert skill_path.is_file(), f"skill_v2.py 缺失: {skill_path}"
    spec = importlib.util.spec_from_file_location("_wechat_skill_v2_offline", skill_path)
    mod = importlib.util.module_from_spec(spec)
    assert spec and spec.loader
    spec.loader.exec_module(mod)
    WECHAT_ACTIONS = mod.WECHAT_ACTIONS
    WechatSkillV2 = mod.WechatSkillV2
    assert len(WECHAT_ACTIONS) == 159, f"WECHAT_ACTIONS={len(WECHAT_ACTIONS)} != 159"
    catalog = WechatSkillV2.get_catalog_action_list()
    assert len(catalog) >= 174, f"catalog={len(catalog)} < 174"


def test_friend_add_route_importable():
    """TODO-02 friend-add → 存客宝线索链路可导入。"""
    from services.cunke_bao_service import CunKeBaoService, LeadData
    assert hasattr(CunKeBaoService, "report_friend_add")
    assert hasattr(CunKeBaoService, "report_lead")
    lead = LeadData(wechat_id="wxid_test", wechat_nickname="t", source="测试")
    params = lead.to_api_params()
    assert params.get("wechatId") == "wxid_test"


def test_ai_chat_orchestrator_importable():
    """TODO-03 AI chat → send_message 编排可导入。"""
    from services.karuo_device_ai import chat_and_execute_on_device, device_status
    assert callable(chat_and_execute_on_device)
    assert callable(device_status)


def test_unblock_customer_service_exists():
    """TODO-01 解封客服 Skill 文件存在。"""
    p = Path(__file__).resolve().parents[1] / "agent" / "skills" / "wechat" / "unblock_customer_service.py"
    assert p.is_file(), "解封客服 Skill 缺失"


def test_three_acceptance_scripts_exist():
    base = Path(__file__).resolve().parents[1] / "scripts"
    for s in ("run_three_acceptance.sh", "auto_accept_when_ready.sh", "termux_frida_up_onphone.sh"):
        assert (base / s).is_file(), f"脚本缺失 {s}"


def test_wp_wx04_scan_routes_and_agent_skill():
    """WP-WX-04 离线路由 + Agent u2 skill 对齐（不替代真机 E2E）。"""
    from routers import unified

    assert callable(getattr(unified, "extract_qr_from_image", None))
    assert callable(getattr(unified, "add_friend_from_image", None))
    skill_path = Path(__file__).resolve().parents[1] / "agent" / "skills" / "wechat" / "skill.py"
    src = skill_path.read_text(encoding="utf-8")
    assert "def add_friend_from_image" in src
    assert "_save_qr_image_to_album" in src
    agent_py = Path(__file__).resolve().parents[1] / "agent" / "agent.py"
    assert '"add_friend_from_image"' in agent_py.read_text(encoding="utf-8")


if __name__ == "__main__":
    sys.exit(pytest.main([__file__, "-q"]))
