#!/usr/bin/env python3
"""KOGUN CLI - 算力贡献 + 超算工坊 双模式客户端"""
import argparse
import asyncio
import json
import logging
import os
import signal
import subprocess
import sys
from pathlib import Path

from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.layout import Layout
from rich.live import Live
from rich.text import Text

from . import __version__
from .device import detect_device
from .worker import (
    KogunWorker, load_config, save_config, load_state, save_state,
    is_worker_running, write_pid, remove_pid, get_worker_status,
    CONFIG_DIR, PID_FILE, STATE_FILE,
)
from .forge import (
    cmd_forge_list, cmd_forge_new, cmd_forge_chat, cmd_forge_search,
    cmd_forge_share, cmd_forge_summary, cmd_forge_points, cmd_forge_buy,
    cmd_stats, cmd_leaderboard, cmd_ticket, cmd_tickets,
)

console = Console()

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(name)s] %(levelname)s: %(message)s",
    datefmt="%H:%M:%S",
    stream=sys.stdout,
)
logger = logging.getLogger("kogun")


# ========== Logo ==========

LOGO = """
 ⚡  K O G U N   C L I   v%s
 ════════════════════════════
 算力共享 · 超算工坊  双模式客户端
"""


def print_logo():
    console.print(LOGO % __version__, style="bold cyan")


# ========== Mode 1: 算力贡献 ==========

def cmd_login(args):
    """登录并保存配置"""
    print_logo()

    server = args.server or console.input("[bold]🔗 服务器地址: [/]")
    server = server.rstrip("/")
    if not server.startswith("http"):
        server = f"https://{server}"

    username = args.username or console.input("[bold]👤 用户名: [/]")
    password = args.password or console.input("[bold]🔑 密码: [/]", password=True)

    import httpx
    try:
        console.print(f"\n⏳ 正在连接到 {server}...")
        resp = httpx.post(
            f"{server}/api/auth/login",
            json={"username": username, "password": password},
            timeout=15,
        )
        if resp.status_code != 200:
            console.print(f"[bold red]❌ 登录失败: {resp.json().get('detail', '未知错误')}[/]")
            sys.exit(1)

        data = resp.json()
        token = data["token"]
        user = data.get("user", {})

        upstream = {}
        if args.upstream_url:
            up_url = args.upstream_url
            up_key = args.upstream_key or ""
            if not up_key:
                up_key = console.input("[dim]上游API Key: [/]")
            upstream = {"url": up_url, "key": up_key, "models": {}}

        config = {
            "server": server,
            "token": token,
            "username": username,
            "upstream": upstream,
            "region": args.region or "",
            "community": args.community or "",
        }
        save_config(config)

        console.print(f"\n[bold green]✅ 登录成功！[/]")
        console.print(f"   用户名: [cyan]{username}[/]")
        console.print(f"   服务器: [cyan]{server}[/]")
        console.print(f"\n   💡 运行 [bold]kogun start[/] 开始贡献算力！")
        console.print(f"   💡 运行 [bold]kogun forge list[/] 进入超算工坊！")

    except httpx.ConnectError:
        console.print(f"[bold red]❌ 无法连接到服务器: {server}[/]")
        sys.exit(1)
    except Exception as e:
        console.print(f"[bold red]❌ 错误: {e}[/]")
        sys.exit(1)


def cmd_start(args):
    """启动Worker守护进程"""
    config = load_config()
    if not config.get("token"):
        console.print("[bold red]❌ 未登录！请先运行 kogun login[/]")
        sys.exit(1)

    if is_worker_running():
        console.print("[bold yellow]⚠️  Worker 已经在运行中！[/]")
        pid = PID_FILE.read_text().strip() if PID_FILE.exists() else "?"
        console.print(f"   PID: {pid}")
        console.print(f"   💡 运行 [bold]kogun stop[/] 停止后再试")
        sys.exit(1)

    if not args.foreground:
        console.print("🚀 启动Worker后台守护进程...")
        pid = os.fork()
        if pid > 0:
            write_pid()
            console.print(f"[bold green]✅ Worker 已启动！PID: {pid}[/]")
            console.print(f"   💡 运行 [bold]kogun status[/] 查看状态")
            console.print(f"   💡 运行 [bold]kogun stop[/] 停止")
            sys.exit(0)
        write_pid()

    console.print("[bold]🚀 启动 KOGUN Worker...[/]\n")

    worker = KogunWorker(
        base_url=config["server"],
        token=config["token"],
        upstream=config.get("upstream", {}),
        region=config.get("region", ""),
        community=config.get("community", ""),
    )

    def handle_signal(sig, frame):
        console.print("\n[bold yellow]收到停止信号，正在退出...[/]")
        worker.stop()

    signal.signal(signal.SIGTERM, handle_signal)
    signal.signal(signal.SIGINT, handle_signal)

    try:
        asyncio.run(worker.start())
    except KeyboardInterrupt:
        pass
    finally:
        remove_pid()
        console.print("[bold]Worker 已停止[/]")
        if worker.tasks_completed > 0:
            console.print(f"   📊 本次运行:")
            console.print(f"      完成: [cyan]{worker.tasks_completed}[/] 个任务")
            console.print(f"      收益: [bold green]¥{worker.total_earned:.4f}[/]")


def cmd_stop(args):
    """停止Worker"""
    if not is_worker_running():
        console.print("[bold yellow]⚠️  Worker 未运行[/]")
        return

    try:
        pid = int(PID_FILE.read_text().strip())
        os.kill(pid, signal.SIGTERM)
        console.print(f"[bold green]✅ 已发送停止信号到 PID {pid}[/]")
        try:
            os.waitpid(pid, 0)
        except ChildProcessError:
            pass
        remove_pid()
    except ProcessLookupError:
        console.print("[yellow]进程已不存在，清理状态...[/]")
        remove_pid()
    except Exception as e:
        console.print(f"[bold red]❌ 停止失败: {e}[/]")


def cmd_status(args):
    """查看Worker状态"""
    print_logo()

    config = load_config()
    state = load_state()
    status = get_worker_status()

    table = Table(show_header=False, box=None)
    table.add_column("Key", style="bold cyan")
    table.add_column("Value")

    if config:
        table.add_row("服务器", config.get("server", "-"))
        table.add_row("用户", config.get("username", "-"))

    if status["running"]:
        table.add_row("运行状态", "[bold green]● 运行中[/]")
        table.add_row("PID", str(status.get("pid", "-")))
        table.add_row("节点ID", str(status.get("node_id", "-")))
    else:
        table.add_row("运行状态", "[bold red]● 已停止[/]")
        if status.get("node_id"):
            table.add_row("最后节点ID", str(status["node_id"]))

    console.print(table)
    console.print()

    console.print("[bold]💻 本机硬件:[/]")
    device = detect_device()
    hw_table = Table(show_header=False, box=None)
    hw_table.add_column("项", style="cyan")
    hw_table.add_column("值")
    hw_table.add_row("CPU", f"{device.get('cpu_model', 'Unknown')} ({device.get('cpu_cores', 0)}核)")
    hw_table.add_row("GPU", device.get('gpu_model', '未检测到') or '未检测到')
    hw_table.add_row("内存", f"{device.get('ram_mb', 0) // 1024}GB")
    hw_table.add_row("硬盘", f"{device.get('disk_total_mb', 0) // 1024}GB ({device.get('disk_type', 'unknown')})")
    hw_table.add_row("系统", device.get('os_info', ''))
    console.print(hw_table)

    upstream = config.get("upstream", {})
    if upstream and upstream.get("url"):
        console.print(f"\n[dim]🔗 上游API: {upstream['url']}[/]")


def cmd_detect(args):
    """检测本机设备"""
    print_logo()
    console.print("[bold]🔍 检测本机硬件...[/]\n")

    device = detect_device()

    table = Table(show_header=False, box=None)
    table.add_column("项", style="bold cyan")
    table.add_column("值")
    table.add_row("设备类型", device.get("device_type", "pc"))
    table.add_row("操作系统", device.get("os_info", ""))
    table.add_row("CPU", f"{device.get('cpu_model', 'Unknown')}")
    table.add_row("CPU核心", str(device.get("cpu_cores", 0)))
    table.add_row("GPU", device.get("gpu_model", "未检测到"))
    table.add_row("GPU显存", f"{device.get('gpu_vram_mb', 0)}MB" if device.get("gpu_vram_mb") else "未知")
    # 等级估算（仅本地计算，不依赖服务器）
    gpu_model = (device.get("gpu_model") or "").upper()
    gpu_vram = device.get("gpu_vram_mb", 0) or 0
    # 简易评分
    est_score = 0
    if "4090" in gpu_model: est_score = 95
    elif "4080" in gpu_model: est_score = 82
    elif "4070" in gpu_model: est_score = 72
    elif "4060" in gpu_model: est_score = 58
    elif "3090" in gpu_model: est_score = 80
    elif "3080" in gpu_model: est_score = 70
    elif "3070" in gpu_model: est_score = 65
    elif "3060" in gpu_model: est_score = 55
    elif "3050" in gpu_model: est_score = 40
    elif "1660" in gpu_model: est_score = 12
    elif "1060" in gpu_model: est_score = 8
    elif gpu_vram >= 24000: est_score = 90
    elif gpu_vram >= 16000: est_score = 75
    elif gpu_vram >= 8000: est_score = 55
    elif gpu_vram >= 4000: est_score = 35
    elif gpu_vram > 0: est_score = 20
    # 定级
    if est_score >= 90: tier, price = "SS", 10.00
    elif est_score >= 65: tier, price = "S", 5.00
    elif est_score >= 40: tier, price = "A", 3.00
    elif est_score >= 20: tier, price = "B", 1.50
    elif est_score >= 8: tier, price = "C", 0.80
    else: tier, price = "D", 0.40
    factor = max(est_score / 100, 0.15)
    contrib_h = price * factor * 0.70
    table.add_row("预估等级", f"[bold]{tier}[/] (评分{est_score})")
    table.add_row("算力因子", f"{factor:.2f}")
    table.add_row("预估贡献", f"¥{contrib_h:.2f}/h")
    table.add_row("内存", f"{device.get('ram_mb', 0) // 1024}GB" if device.get("ram_mb") else "未知")
    table.add_row("硬盘总量", f"{device.get('disk_total_mb', 0) // 1024}GB" if device.get("disk_total_mb") else "未知")
    table.add_row("硬盘类型", device.get("disk_type", "unknown"))

    console.print(table)

    if args.json:
        console.print("\n[dim]JSON:[/]")
        console.print(json.dumps(device, indent=2, ensure_ascii=False))


def cmd_earnings(args):
    """查看收益"""
    config = load_config()
    if not config.get("token"):
        console.print("[bold red]❌ 未登录！请先运行 kogun login[/]")
        sys.exit(1)

    import httpx
    try:
        headers = {"Authorization": f"Bearer {config['token']}"}

        resp = httpx.get(
            f"{config['server']}/api/my-devices",
            headers=headers,
            timeout=10,
        )
        if resp.status_code != 200:
            console.print(f"[red]获取节点信息失败: {resp.text}[/]")
            return

        nodes = resp.json()

        resp2 = httpx.get(
            f"{config['server']}/api/auth/me",
            headers=headers,
            timeout=10,
        )
        user_info = resp2.json() if resp2.status_code == 200 else {}

        print_logo()

        table = Table(title="💰 收益概览")
        table.add_column("节点", style="cyan")
        table.add_column("等级", style="bold")
        table.add_column("GPU", style="yellow")
        table.add_column("状态")
        table.add_column("因子")
        table.add_column("预估/h")
        table.add_column("总收益(¥)")
        table.add_column("在线时长")

        total_earnings = 0
        for n in nodes:
            status_style = "green" if n.get("status") == "online" else "red"
            total_earnings += n.get("total_earnings", 0) or 0
            online_sec = n.get("online_seconds", 0) or 0
            online_str = f"{online_sec // 3600}h{(online_sec % 3600) // 60}m"
            tier = n.get("tier", "?")
            factor = n.get("compute_factor", 0) or 0
            est_h = n.get("estimated_contributor_hourly", 0) or 0
            table.add_row(
                str(n["id"]),
                f"[bold]{tier}[/]",
                n.get("gpu_name", "-"),
                f"[{status_style}]{n.get('status', 'unknown')}[/]",
                f"{factor:.2f}",
                f"¥{est_h:.2f}",
                f"{n.get('total_earnings', 0):.4f}",
                online_str,
            )

        console.print(table)
        console.print(f"\n📊 账户余额: [bold green]¥{user_info.get('credit_balance', 0):.4f}[/]")
        console.print(f"📊 节点总收益: [bold green]¥{total_earnings:.4f}[/]")

        resp3 = httpx.get(
            f"{config['server']}/api/my-earnings",
            headers=headers,
            timeout=10,
        )
        if resp3.status_code == 200:
            txs = resp3.json()
            if txs:
                tx_table = Table(title="📋 最近交易记录")
                tx_table.add_column("时间")
                tx_table.add_column("类型")
                tx_table.add_column("金额(¥)")
                tx_table.add_column("说明")
                for tx in txs[:10]:
                    amount = tx.get("amount", 0)
                    amt_style = "green" if tx.get("type") == "earning" else "red"
                    tx_table.add_row(
                        (tx.get("created_at") or "")[:19],
                        tx.get("type", "-"),
                        f"[{amt_style}]{amount:.4f}[/]",
                        (tx.get("description") or "-")[:30],
                    )
                console.print(tx_table)

    except httpx.ConnectError:
        console.print(f"[red]❌ 无法连接服务器: {config['server']}[/]")
    except Exception as e:
        console.print(f"[red]❌ 错误: {e}[/]")
        import traceback
        console.print(traceback.format_exc())


def cmd_logout(args):
    """退出登录"""
    from .worker import CONFIG_FILE
    if CONFIG_FILE.exists():
        CONFIG_FILE.unlink()
    console.print("[bold green]✅ 已退出登录[/]")


# ========== 主入口 ==========

def main():
    parser = argparse.ArgumentParser(
        description="KOGUN CLI - 算力共享 + 超算工坊 双模式客户端",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
╔══════════════════════════════════════════╗
║          K O G U N   C L I              ║
║  算力贡献 | 超算工坊  双模式客户端       ║
╚══════════════════════════════════════════╝

📡 模式1：算力贡献
  kogun login                         登录
  kogun start                         启动Worker
  kogun stop                          停止Worker
  kogun status                        查看状态
  kogun detect                        检测硬件
  kogun earnings                      查看收益
  kogun logout                        退出登录

🔧 模式2：超算工坊
  kogun forge list                    列出对话
  kogun forge new <场景>             创建对话
  kogun forge chat <ID> "<消息>"     发送消息
  kogun forge search "<关键词>"       搜索对话
  kogun forge share <ID>              分享对话
  kogun forge summary <ID>            生成总结
  kogun forge points                  查看积分
  kogun forge buy <数量>              充值积分

📊 通用工具
  kogun stats                         数据看板
  kogun leaderboard                   排行榜
  kogun ticket <主题> <内容>          提交工单
  kogun tickets                       查看工单

场景: science(🔬) ai(🤖) ecommerce(🛒) mechanical(⚙️)
      eda(📊) game(🎮) dev(💻) research(📚)
        """,
    )
    parser.add_argument("--version", action="version", version=f"kogun-cli {__version__}")

    sub = parser.add_subparsers(dest="command", help="子命令")

    # ===== 模式1：算力贡献 =====
    p_login = sub.add_parser("login", help="登录平台")
    p_login.add_argument("-s", "--server", help="服务器地址")
    p_login.add_argument("-u", "--username", help="用户名")
    p_login.add_argument("-p", "--password", help="密码")
    p_login.add_argument("--upstream-url", help="上游API地址（可选）")
    p_login.add_argument("--upstream-key", help="上游API Key（可选）")
    p_login.add_argument("--region", help="🏘️ 所在地区，例如: 北京市-朝阳区-望京西园")
    p_login.add_argument("--community", help="🏘️ 小区/社区名称")

    p_start = sub.add_parser("start", help="启动Worker守护进程")
    p_start.add_argument("-f", "--foreground", action="store_true", help="前台运行")

    sub.add_parser("stop", help="停止Worker守护进程")
    sub.add_parser("status", help="查看Worker状态")

    p_detect = sub.add_parser("detect", help="检测本机硬件")
    p_detect.add_argument("--json", action="store_true", help="JSON格式输出")

    sub.add_parser("earnings", help="查看收益")
    sub.add_parser("logout", help="退出登录")

    # ===== 模式2：超算工坊 (forge) =====
    p_forge = sub.add_parser("forge", help="超算工坊 (对话/积分管理)")
    forge_sub = p_forge.add_subparsers(dest="forge_cmd", help="forge子命令")

    # forge list
    forge_sub.add_parser("list", help="列出所有对话")

    # forge new
    p_forge_new = forge_sub.add_parser("new", help="创建新对话")
    p_forge_new.add_argument("scenario", nargs="?", default="ai",
                             help="场景: science/ai/ecommerce/mechanical/eda/game/dev/research")

    # forge chat
    p_forge_chat = forge_sub.add_parser("chat", help="发送消息到对话")
    p_forge_chat.add_argument("conv_id", help="对话ID")
    p_forge_chat.add_argument("message", nargs="+", help="消息内容")

    # forge search
    p_forge_search = forge_sub.add_parser("search", help="搜索对话")
    p_forge_search.add_argument("keyword", help="搜索关键词")

    # forge share
    p_forge_share = forge_sub.add_parser("share", help="分享对话")
    p_forge_share.add_argument("conv_id", help="对话ID")

    # forge summary
    p_forge_summary = forge_sub.add_parser("summary", help="生成对话总结")
    p_forge_summary.add_argument("conv_id", help="对话ID")

    # forge points
    forge_sub.add_parser("points", help="查看积分余额")

    # forge buy
    p_forge_buy = forge_sub.add_parser("buy", help="充值积分")
    p_forge_buy.add_argument("amount", type=float, help="充值数量")

    # ===== 通用工具 =====
    sub.add_parser("stats", help="数据看板")

    p_lb = sub.add_parser("leaderboard", help="排行榜")
    p_lb.add_argument("-n", "--limit", type=int, default=20, help="显示条数 (默认20)")

    p_ticket = sub.add_parser("ticket", help="提交工单")
    p_ticket.add_argument("subject", help="工单主题")
    p_ticket.add_argument("content", nargs="+", help="工单内容")

    sub.add_parser("tickets", help="查看工单列表")


    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        return

    # ===== 命令路由 =====

    # 模式1：算力贡献
    if args.command == "login":
        cmd_login(args)
    elif args.command == "start":
        cmd_start(args)
    elif args.command == "stop":
        cmd_stop(args)
    elif args.command == "status":
        cmd_status(args)
    elif args.command == "detect":
        cmd_detect(args)
    elif args.command == "earnings":
        cmd_earnings(args)
    elif args.command == "logout":
        cmd_logout(args)

    # 模式2：超算工坊
    elif args.command == "forge":
        if not args.forge_cmd:
            p_forge.print_help()
            return
        if args.forge_cmd == "list":
            cmd_forge_list(args)
        elif args.forge_cmd == "new":
            cmd_forge_new(args)
        elif args.forge_cmd == "chat":
            # 合并多个消息token为一个字符串
            args.message = " ".join(args.message)
            cmd_forge_chat(args)
        elif args.forge_cmd == "search":
            cmd_forge_search(args)
        elif args.forge_cmd == "share":
            cmd_forge_share(args)
        elif args.forge_cmd == "summary":
            cmd_forge_summary(args)
        elif args.forge_cmd == "points":
            cmd_forge_points(args)
        elif args.forge_cmd == "buy":
            cmd_forge_buy(args)

    # 通用工具
    elif args.command == "stats":
        cmd_stats(args)
    elif args.command == "leaderboard":
        cmd_leaderboard(args)
    elif args.command == "ticket":
        args.content = " ".join(args.content)
        cmd_ticket(args)
    elif args.command == "tickets":
        cmd_tickets(args)

    else:
        parser.print_help()


if __name__ == "__main__":
    main()
