"""测试 Router 核心接口

用法：
    uv run python tests/test_router_api.py                               # 只跑 health check
    uv run python tests/test_router_api.py --search                      # 搜索工具列表
    uv run python tests/test_router_api.py --search image                # 关键词搜索
    uv run python tests/test_router_api.py --status                      # 工具运行状态
    uv run python tests/test_router_api.py --select image_stitcher       # 调用指定工具
    uv run python tests/test_router_api.py --stitch                      # 测试图片拼接
    uv run python tests/test_router_api.py --create                      # 默认任务
    uv run python tests/test_router_api.py --create image_stitcher       # 指定任务文件
"""

import argparse
import base64
import json
import sys
import time
from pathlib import Path

import httpx

BASE_URL = "http://43.106.118.91:8001"
TASKS_DIR = Path(__file__).parent / "tasks"
TEST_IMAGES_DIR = TASKS_DIR / "stitcher_images"
OUTPUT_DIR = Path(__file__).parent / "output"


def check_connection():
    try:
        httpx.get(f"{BASE_URL}/health", timeout=3)
    except httpx.ConnectError:
        print(f"ERROR: Cannot connect to {BASE_URL}")
        print("Please start the service first:")
        print("  uv run python -m tool_agent")
        sys.exit(1)


def test_health():
    print("=== Health Check ===")
    resp = httpx.get(f"{BASE_URL}/health")
    print(f"  Status : {resp.status_code}")
    print(f"  Body   : {json.dumps(resp.json(), ensure_ascii=False, indent=4)}")
    assert resp.status_code == 200
    print("  [PASS]")


def test_search_tools(keyword: str = None):
    print(f"=== Search Tools{f' (keyword={keyword!r})' if keyword else ''} ===")
    payload = {"keyword": keyword} if keyword else {}
    resp = httpx.post(f"{BASE_URL}/search_tools", json=payload)
    print(f"  Status : {resp.status_code}")
    if resp.status_code != 200:
        print(f"  Body   : {resp.text}")
        print("  [FAIL]")
        return
    data = resp.json()
    print(f"  Total  : {data['total']}")
    for t in data["tools"]:
        print(f"\n  [{t['tool_id']}]")
        print(f"    name          : {t['name']}")
        print(f"    category      : {t.get('category', '')}")
        print(f"    state         : {t['state']}")
        print(f"    runtime       : {t.get('runtime_type', '')}  host_dir={t.get('host_dir', '')}")
        print(f"    endpoint      : {t.get('http_method', '')} {t.get('endpoint_path', '')}  port={t.get('port')}")
        print(f"    stream_support: {t.get('stream_support', False)}")
        print(f"    description   : {t.get('description', '')}")
        print(f"    params ({len(t.get('params', []))}):")
        for p in t.get("params", []):
            req_mark = "*" if p["required"] else " "
            default_str = f"  default={p['default']}" if p.get("default") is not None else ""
            enum_str = f"  enum={p['enum']}" if p.get("enum") else ""
            print(f"      {req_mark} {p['name']:<25} {p['type']:<12} {p.get('description', '')}{default_str}{enum_str}")
        if t.get("output_schema"):
            out_props = t["output_schema"].get("properties", {})
            print(f"    output ({len(out_props)}):")
            for oname, odef in out_props.items():
                print(f"        {oname:<25} {odef.get('type', ''):<12} {odef.get('description', '')}")
    print("\n  [PASS]")


def test_tools_status():
    print("=== Tools Status ===")
    resp = httpx.get(f"{BASE_URL}/tools/status")
    print(f"  Status : {resp.status_code}")
    data = resp.json()
    print(f"  Total  : {len(data['tools'])}")
    for t in data["tools"]:
        print(f"    - {t['tool_id']}")
        print(f"        state  : {t['state']}")
        print(f"        port   : {t.get('port')}")
        print(f"        pid    : {t.get('pid')}")
        print(f"        sources: {[s['type'] for s in t.get('sources', [])]}")
        if t.get("last_error"):
            print(f"        error  : {t['last_error']}")
    print("  [PASS]")


def test_select_tool(tool_id: str):
    print(f"=== Select Tool (tool_id={tool_id!r}) ===")
    resp = httpx.post(f"{BASE_URL}/select_tool", json={
        "tool_id": tool_id,
        "params": {}
    }, timeout=30)
    print(f"  Status : {resp.status_code}")
    data = resp.json()
    print(f"  Result :")
    print(f"    status: {data.get('status')}")
    if data.get("error"):
        print(f"    error : {data['error']}")
    else:
        result_str = json.dumps(data.get("result"), ensure_ascii=False, indent=6)
        print(f"    result: {result_str[:500]}")
    print("  [PASS]")


def test_stitch_images():
    print("=== Test Image Stitcher ===")
    if not TEST_IMAGES_DIR.exists():
        print(f"  ERROR: Test images directory not found: {TEST_IMAGES_DIR}")
        print("  [SKIP]")
        return

    image_files = sorted(TEST_IMAGES_DIR.glob("*.png"))
    if len(image_files) < 2:
        print(f"  ERROR: Need at least 2 images, found {len(image_files)}")
        print("  [SKIP]")
        return

    print(f"  Images : {len(image_files)} found")
    images_b64 = []
    for img_path in image_files[:6]:
        with open(img_path, "rb") as f:
            images_b64.append(base64.b64encode(f.read()).decode())
        print(f"    - {img_path.name}")

    print(f"  Calling image_stitcher (grid, 2 columns)...")
    try:
        resp = httpx.post(f"{BASE_URL}/select_tool", json={
            "tool_id": "image_stitcher",
            "params": {
                "images": images_b64,
                "direction": "grid",
                "columns": 2,
                "spacing": 10,
                "background_color": "#FFFFFF",
            }
        }, timeout=60)
        print(f"  Status : {resp.status_code}")
        data = resp.json()
        if data["status"] == "success":
            result = data["result"]
            print(f"  Result :")
            print(f"    width : {result['width']}")
            print(f"    height: {result['height']}")
            OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
            output_path = OUTPUT_DIR / "stitched_result.png"
            with open(output_path, "wb") as f:
                f.write(base64.b64decode(result["image"]))
            print(f"    saved : {output_path}")
            print("  [PASS]")
        else:
            print(f"  ERROR : {data.get('error', 'unknown')}")
            print("  [FAIL]")
    except httpx.TimeoutException:
        print("  ERROR : Request timeout")
        print("  [FAIL]")
    except Exception as e:
        print(f"  ERROR : {e}")
        print("  [FAIL]")


def load_task_spec(task_name: str) -> dict:
    task_file = TASKS_DIR / f"{task_name}.json"
    if not task_file.exists():
        print(f"  ERROR: Task file not found: {task_file}")
        print("  Available tasks:")
        if TASKS_DIR.exists():
            for f in TASKS_DIR.glob("*.json"):
                print(f"    - {f.stem}")
        sys.exit(1)
    with open(task_file, "r", encoding="utf-8") as f:
        return json.load(f)


def test_create_tool(task_name: str = None):
    print(f"=== Create Tool{f' (task={task_name!r})' if task_name else ''} ===")
    if task_name:
        task_data = load_task_spec(task_name)
        print(f"  File       : tests/tasks/{task_name}.json")
        print(f"  Description: {task_data['description'][:80]}")
    else:
        task_data = {"description": "创建一个简单的文本计数工具，输入文本，返回字数和字符数"}
        print(f"  Description: {task_data['description']}")

    resp = httpx.post(f"{BASE_URL}/create_tool", json=task_data)
    data = resp.json()
    task_id = data["task_id"]
    print(f"  Task ID    : {task_id}")
    print(f"  Status     : {data['status']}")
    assert data["status"] == "pending"
    print("  [SUBMITTED]")

    print(f"\n  Polling task {task_id} (timeout 10min)...")
    for i in range(120):
        time.sleep(5)
        resp = httpx.get(f"{BASE_URL}/tasks/{task_id}", timeout=30)
        task = resp.json()
        status = task["status"]

        if i % 6 == 0:
            print(f"    [{i*5}s] status={status}")

        if status == "completed":
            print(f"\n  Completed!")
            print(f"  Result     : {str(task.get('result', ''))[:300]}")
            resp2 = httpx.post(f"{BASE_URL}/search_tools", json={})
            tools = resp2.json()["tools"]
            print(f"  Registered : {[t['tool_id'] for t in tools]}")
            print("  [PASS]")
            return

        if status == "failed":
            print(f"\n  Failed!")
            print(f"  Error      : {task.get('error', 'unknown')}")
            print("  [FAIL]")
            return

    print(f"\n  Timeout after 600s")
    print("  [TIMEOUT]")


def main():
    parser = argparse.ArgumentParser(description="Router API Test")
    parser.add_argument("--search", nargs="?", const="", metavar="KEYWORD",
                        help="search tools, optional keyword")
    parser.add_argument("--status", action="store_true",
                        help="show tools status")
    parser.add_argument("--select", metavar="TOOL_ID",
                        help="call a tool by tool_id")
    parser.add_argument("--stitch", action="store_true",
                        help="test image stitcher with sample images")
    parser.add_argument("--create", nargs="?", const="", metavar="TASK_NAME",
                        help="create tool, optional task file name")
    args = parser.parse_args()

    print(f"Target: {BASE_URL}\n")
    check_connection()

    # 始终跑 health check
    test_health()

    ran_any = False

    if args.search is not None:
        print()
        test_search_tools(args.search or None)
        ran_any = True

    if args.status:
        print()
        test_tools_status()
        ran_any = True

    if args.select:
        print()
        test_select_tool(args.select)
        ran_any = True

    if args.stitch:
        print()
        test_stitch_images()
        ran_any = True

    if args.create is not None:
        print()
        test_create_tool(args.create or None)
        ran_any = True

    if not ran_any:
        print()
        print("No test specified. Available options:")
        parser.print_help()

    print("\n=== DONE ===")


if __name__ == "__main__":
    main()
