#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Hermes Agent EavaryTech API 配置修補腳本
==========================================

解決: "No inference provider configured"

原理: 新版 Hermes 要求 providers.openai.api_key，
     僅在 model.api_key 放 key 會 doctor 過但實際跑時失敗。

用法 (PowerShell):
    $env:HERMES_EAVARY_KEY="sk-SFt...Ufg"
    python setup_hermes.py
"""

import os
import sys
import re
import shutil
import subprocess
import argparse
from pathlib import Path
from datetime import datetime

# ============================================================
#  配置（可從環境變量 HERMES_EAVARY_KEY 帶入）
# ============================================================
BASE_URL    = "https://llm-api-ai.eavarytech.com/v1"
DEFAULT_M   = "Chat"
ALT_MODEL   = "Reasoning"
CONTEXT_LEN = 32768
# ============================================================


def step(s): print(f"\n-- {s} --")
def info(s): print(f"   [i] {s}")
def warn(s): print(f"   [!] {s}")
def ok(s):   print(f"   [+] {s}")
def err(s):  print(f"   [x] {s}")


def run(cmd, timeout=30):
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, encoding="utf-8")
        return r.returncode, r.stdout, r.stderr
    except FileNotFoundError:
        return -1, "", f"{cmd[0]} 命令不存在"
    except subprocess.TimeoutExpired:
        return -2, "", "超时"
    except Exception as e:
        return -3, "", str(e)


def get_api_key():
    # 1. 命令列
    p = argparse.ArgumentParser(add_help=False)
    p.add_argument("--key", default=None)
    p.add_argument("--help", "-h", action="store_true")
    args, _ = p.parse_known_args()

    if args.help:
        print(__doc__)
        sys.exit(0)

    if args.key:
        return args.key.strip()

    # 2. 環境變量
    env = os.environ.get("HERMES_EAVARY_KEY", "").strip()
    if env:
        return env

    # 3. 互動輸入
    print()
    print("   [提示] API key 可透過三種方式提供:")
    print("          1) python setup_hermes.py --key sk-...")
    print("          2) $env:HERMES_EAVARY_KEY='sk-...'; python setup_hermes.py")
    print("          3) 直接粘貼到下面 (隱藏顯示)")
    print()
    import getpass
    key = getpass.getpass("   請輸入 EavaryTech API key: ").strip()
    if not key.startswith("sk-"):
        warn("key 不像標準 sk-... 格式，但仍繼續")
    return key


def main():
    print()
    print("=" * 60)
    print("  Hermes Agent EavaryTech API 配置工具")
    print("=" * 60)
    print()

    # ── 0. 取 API key
    step("0/7  取得 API key")
    api_key = get_api_key()
    if not api_key:
        err("未提供 API key")
        sys.exit(1)
    ok(f"key 長度: {len(api_key)} chars, 前 4 字: {api_key[:4]}")

    # ── Windows-aware HOME
    if os.name == "nt":
        HERMES_DIR = Path(os.environ.get("USERPROFILE", str(Path.home()))) / ".hermes"
    else:
        HERMES_DIR = Path.home() / ".hermes"

    CFG = HERMES_DIR / "config.yaml"
    BACKUP_DIR = HERMES_DIR / "backups"

    # ── 1. 檢測 hermes
    step("1/7  檢測 hermes")
    rc, out, errmsg = run(["hermes", "--version"], timeout=10)
    if rc != 0:
        err(f"hermes CLI 不存在: {errmsg.strip()}")
        info("安裝方法：")
        print("        iwr -useb https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1 | iex")
        sys.exit(1)
    ok(f"版本: {(out or errmsg).strip()}")

    # ── 2. 確保目錄
    step("2/7  定位 ~/.hermes")
    HERMES_DIR.mkdir(parents=True, exist_ok=True)
    BACKUP_DIR.mkdir(exist_ok=True)
    if not CFG.exists():
        warn(f"{CFG} 不存在，建立空檔")
        CFG.touch()
    ok(f"config: {CFG}")
    ok(f"backup: {BACKUP_DIR}")

    # ── 3. 備份
    step("3/7  備份現有 config.yaml")
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    backup = BACKUP_DIR / f"config.yaml.bak.{ts}"
    shutil.copy2(CFG, backup)
    ok(f"備份: {backup}")

    # ── 4. 讀現有
    step("4/7  讀現有 config")
    content = CFG.read_text(encoding="utf-8")
    info(f"size: {len(content)} chars")

    # ── 5. 修補
    step("5/7  修補 model 和 providers")

    new_model = (
        "model:\n"
        "  default: Chat\n"
        "  provider: openai\n"
        "  context_length: 32768\n"
        "  temperature: 0.7\n"
        "  max_tokens: 4096"
    )

    new_providers = (
        "providers:\n"
        "  openai:\n"
        f'    api_key: "{api_key}"\n'
        f"    base_url: {BASE_URL}\n"
        "    default_model: Chat\n"
        "    timeout: 60"
    )

    original = content
    matched = 0

    # 5a. 空 model: ''
    if re.search(r"^model:\s*['\"]?\s*['\"]?\s*$", content, re.MULTILINE):
        content = re.sub(
            r"^model:\s*['\"]?\s*['\"]?\s*$",
            new_model,
            content, count=1, flags=re.MULTILINE,
        )
        matched += 1
        ok("替換空 model 段")

    # 5b. 空 providers: {}
    if re.search(r"^providers:\s*\{\}\s*$", content, re.MULTILINE):
        content = re.sub(
            r"^providers:\s*\{\}\s*$",
            new_providers,
            content, count=1, flags=re.MULTILINE,
        )
        matched += 1
        ok("填入 providers.openai 段")

    # 5c. 有 providers 段但沒 api_key
    if matched == 0 and re.search(r"^providers:\s*$", content, re.MULTILINE):
        content = re.sub(
            r"^(providers:\s*\n)",
            r"\1  openai:\n"
            f'    api_key: "{api_key}"\n'
            f"    base_url: {BASE_URL}\n"
            "    default_model: Chat\n"
            "    timeout: 60\n\n",
            content, count=1, flags=re.MULTILINE,
        )
        matched += 1
        ok("在現有 providers 段注入 openai 配置")

    if matched == 0:
        err("沒匹配到需要修補的內容")
        info("可能 config.yaml 已經填好了，或格式特殊")
        info("請貼前 30 行給我看")
        sys.exit(1)

    # ── 6. 寫入
    step("6/7  寫回 config.yaml")
    CFG.write_text(content, encoding="utf-8")
    ok(f"寫入: {CFG}")

    print()
    print("   修補後前 40 行:")
    print("   " + "-" * 56)
    for line in content.split("\n")[:40]:
        print(f"   | {line}")
    print("   " + "-" * 56)
    print()

    # ── 7. 驗證
    step("7/7  驗證")

    info("停 gateway...")
    run(["hermes", "gateway", "stop"], timeout=15)
    info("啟 gateway...")
    rc, out, errmsg = run(["hermes", "gateway", "start"], timeout=30)
    if out.strip():                print(f"       {out.strip()}")
    if errmsg.strip():             print(f"       {errmsg.strip()}")

    info("hermes doctor...")
    rc, out, errmsg = run(["hermes", "doctor"], timeout=60)
    if out.strip():
        for ln in out.split("\n"):
            if ln.strip():         print(f"       {ln}")

    info("hermes chat -q '回 PONG'")
    rc, out, errmsg = run(["hermes", "chat", "-q", "回 PONG"], timeout=90)
    output = out + errmsg
    print(f"       {output.strip()}")

    print()
    if "PONG" in output or "pong" in output.lower():
        ok("成功！chat 已通 EavaryTech API")
        print()
        info("用法提示：")
        print("        /model Chat          切到 Qwen")
        print("        /model Reasoning     切到 DeepSeek V4 Flash")
        print("        /model               查看當前模型")
        sys.exit(0)
    elif "No inference provider" in output:
        err("仍報 No inference provider")
        info("排查：")
        print("        1) 完全關閉此 PowerShell，重開一個")
        print("        2) 再跑一次: python setup_hermes.py")
        print("        3) 貼 config.yaml 前 30 行給我")
        sys.exit(1)
    else:
        warn("無法判斷是否成功")
        info("貼上面 doctor + chat 輸出給我")
        sys.exit(0)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n   [x] 用戶中斷")
        sys.exit(1)
    except Exception as e:
        err(f"未預期錯誤: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)
