#!/usr/bin/env python3
"""
usage_collector.py — 多 Provider 用量采集主控
=============================================
编排所有 provider 的采集，合并到统一的 usage_stats.json。

Provider:
  - opencode_go: 本地 HTTP/CDP 采集（usage_collector_ocg.py）
  - kimi: SSH 远程调用 Windows 采集器（usage_collector_kimi.py）

输出: gateway/temp/usage_stats.json（Dashboard /api/usage 读取）
"""
import json, os, sys, subprocess
from pathlib import Path
from datetime import datetime, timezone

SCRIPT_DIR = Path(__file__).resolve().parent
GATEWAY_DIR = SCRIPT_DIR.parent
TEMP_DIR = GATEWAY_DIR / "temp"
LOGS_DIR = GATEWAY_DIR / "logs"
os.makedirs(str(TEMP_DIR), exist_ok=True)
os.makedirs(str(LOGS_DIR), exist_ok=True)

OUTPUT_FILE = TEMP_DIR / "usage_stats.json"
WINDOWS_HOST = "192.168.1.16"
WINDOWS_PYTHON = r"C:\Users\hmo\AppData\Local\Programs\Python\Python310\python.exe"
WINDOWS_SCRIPT = r"D:\F\NewI\opencode\daily-workspace\projects\AgentsMeeting\gateway\scripts\usage_collector_kimi.py"


def log(msg):
    ts = datetime.now().strftime("%H:%M:%S")
    line = f"[{ts}] {msg}"
    print(line, flush=True)


def collect_opencode_go():
    """运行本地 OCG 采集器。"""
    log("OCG: starting...")
    ocg_script = SCRIPT_DIR / "usage_collector_ocg.py"
    if not ocg_script.exists():
        # Fallback: old name
        ocg_script = SCRIPT_DIR / "usage_collector.py"

    try:
        r = subprocess.run(
            [sys.executable, str(ocg_script)],
            capture_output=True, text=True, timeout=120,
            cwd=str(SCRIPT_DIR),
        )
        if r.returncode != 0 and r.stderr:
            log(f"OCG: FAILED (rc={r.returncode})")
            log(f"OCG stderr: {r.stderr[-500:]}")
        else:
            log(f"OCG: done (rc={r.returncode})")
    except Exception as e:
        log(f"OCG: EXCEPTION: {e}")


def collect_kimi():
    """SSH 到 Windows 运行 Kimi 采集器，scp 结果回来。"""
    log("Kimi: SSH to Windows...")
    try:
        r = subprocess.run(
            ["ssh", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=10",
             "-o", "BatchMode=yes", f"hmo@{WINDOWS_HOST}",
             f'"{WINDOWS_PYTHON}" "{WINDOWS_SCRIPT}"'],
            capture_output=True, text=True, timeout=45,
        )
        for line in r.stdout.splitlines()[-5:]:
            log(f"Kimi: {line.strip()}")
        if r.returncode != 0:
            log(f"Kimi: SSH FAILED (rc={r.returncode}): {r.stderr[-200:]}")
            # Try scp alternative
            r2 = subprocess.run(
                ["scp", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=10",
                 f"hmo@{WINDOWS_HOST}:{WINDOWS_SCRIPT}", "/dev/null"],
                capture_output=True, timeout=10,
            )
            if r2.returncode == 0:
                log("Kimi: SSH failed but scp works — check script path")
    except subprocess.TimeoutExpired:
        log("Kimi: SSH TIMEOUT")
    except Exception as e:
        log(f"Kimi: SSH EXCEPTION: {e}")

    # Try to scp the output file from Windows
    kimi_output = TEMP_DIR / "usage_stats_kimi.json"
    try:
        r = subprocess.run(
            ["scp", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=10",
             f"hmo@{WINDOWS_HOST}:" + WINDOWS_SCRIPT.replace("usage_collector_kimi.py", "") + "../temp/usage_stats_kimi.json",
             str(kimi_output)],
            capture_output=True, text=True, timeout=15,
        )
        if kimi_output.exists():
            log(f"Kimi: output scp'd ({kimi_output.stat().st_size} bytes)")
        else:
            log("Kimi: scp failed or file empty")
    except Exception as e:
        log(f"Kimi: scp EXCEPTION: {e}")


def merge_all():
    """合并所有 provider 的数据到 usage_stats.json。"""
    accounts = []

    # 1. Read existing OCG cache (from usage_collector_ocg output)
    #    The OCG collector writes directly to usage_stats.json or a cache
    cache_file = TEMP_DIR / "usage_stats.json"
    if cache_file.exists():
        try:
            with open(str(cache_file), "r", encoding="utf-8") as f:
                cached = json.load(f)
            for a in cached.get("accounts", []):
                a["provider"] = a.get("provider", "opencode_go")
                accounts.append(a)
            log(f"Merge: {len(accounts)} OCG accounts from cache")
        except Exception as e:
            log(f"Merge: failed to read cache: {e}")

    # 2. Read Kimi output
    kimi_file = TEMP_DIR / "usage_stats_kimi.json"
    if kimi_file.exists():
        try:
            with open(str(kimi_file), "r", encoding="utf-8") as f:
                kimi_data = json.load(f)
            for a in kimi_data.get("accounts", []):
                a["provider"] = a.get("provider", "kimi")
                # Check if this key already exists (update instead of duplicate)
                existing = [i for i, x in enumerate(accounts) if x.get("key_id") == a.get("key_id")]
                if existing:
                    accounts[existing[0]] = a
                else:
                    accounts.append(a)
            log(f"Merge: +{len(kimi_data.get('accounts',[]))} Kimi accounts")
        except Exception as e:
            log(f"Merge: failed to read Kimi data: {e}")

    # 3. Count by provider
    providers = {}
    for a in accounts:
        p = a.get("provider", "unknown")
        providers[p] = providers.get(p, 0) + 1

    output = {
        "ok": True,
        "last_refresh_iso": datetime.now(timezone.utc).isoformat(timespec="seconds"),
        "account_count": len(accounts),
        "collected_count": sum(1 for a in accounts if a.get("error") is None),
        "providers": providers,
        "accounts": accounts,
    }

    with open(str(OUTPUT_FILE), "w", encoding="utf-8") as f:
        json.dump(output, f, ensure_ascii=False, indent=2)
    log(f"Merge: saved {len(accounts)} accounts ({providers}) to {OUTPUT_FILE}")


def main():
    log("=== Multi-provider collection started ===")

    # Run collectors in sequence (OCG takes longest, Kimi needs CDP sync)
    collect_opencode_go()
    collect_kimi()

    # Merge
    merge_all()

    log("=== Collection complete ===")


if __name__ == "__main__":
    main()
