"""sync command - Install/upgrade all isagellm packages with exact-version constraints.

解决问题：pip install isagellm 只能保证"依赖可解析"，不能保证
子包之间的运行时协议兼容性（协议字段名、枚举值、接口签名等）。

本命令通过把所有子包锁定到经过联调测试的精确版本来解决这一问题：
    sagellm sync             # 使用内置 constraints.txt（本次发布的精确版本快照）
    sagellm sync --dry-run   # 预览将安装的版本，不实际安装
"""

from __future__ import annotations

import importlib.resources
import logging
import subprocess
import sys
import tempfile
import urllib.request
from pathlib import Path

import click

from ..utils.console import get_console

logger = logging.getLogger(__name__)

# GitHub 上的 constraints.txt 原始链接（main-dev 分支）
_CONSTRAINTS_URL = (
    "https://raw.githubusercontent.com/intellistream/sagellm/main-dev/constraints.txt"
)

# isagellm 子包列表，按依赖顺序
_SUB_PACKAGES = [
    "isagellm-protocol",
    "isagellm-backend",
    "isagellm-comm",
    "isagellm-kv-cache",
    "isagellm-core",
    "isagellm-compression",
    "isagellm-control-plane",
    "isagellm-gateway",
    "isagellm-benchmark",
    "isagellm-dev-tools",
]


def _get_bundled_constraints() -> str | None:
    """Return path to bundled constraints.txt, or None if not found."""
    # Try 1: Installed package data (via importlib.resources)
    try:
        data = importlib.resources.files("sagellm").joinpath("constraints.txt")
        if data.is_file():
            return str(data)
    except (AttributeError, FileNotFoundError, ModuleNotFoundError):
        pass

    # Try 2: Relative to this file (editable install)
    candidate = Path(__file__).parent.parent.parent.parent.parent / "constraints.txt"
    if candidate.exists():
        return str(candidate)

    return None


def _fetch_constraints_from_github(timeout: int = 10) -> str | None:
    """Fetch constraints content from GitHub. Returns content string or None."""
    try:
        with urllib.request.urlopen(_CONSTRAINTS_URL, timeout=timeout) as resp:
            return resp.read().decode("utf-8")
    except Exception:
        return None


def _parse_constraints(content: str) -> dict[str, str]:
    """Parse 'package==version' lines from constraints content."""
    versions: dict[str, str] = {}
    for line in content.splitlines():
        line = line.strip()
        if line.startswith("#") or not line:
            continue
        if "==" in line:
            pkg, ver = line.split("==", 1)
            versions[pkg.strip()] = ver.strip()
    return versions


@click.command(hidden=True)
@click.option(
    "--dry-run",
    is_flag=True,
    default=False,
    help="预览将安装的版本，不实际安装",
)
@click.option(
    "--online",
    is_flag=True,
    default=False,
    help="从 GitHub 下载最新 constraints.txt（默认使用内置版本）",
)
@click.option(
    "--include-umbrella",
    is_flag=True,
    default=False,
    help="同时升级 isagellm 本身（umbrella 包）",
)
def sync(dry_run: bool, online: bool, include_umbrella: bool) -> None:
    """将所有 isagellm 子包同步到经过联调测试的精确版本。

    解决 pip install isagellm 可能装入不兼容子包版本的问题。
    本命令使用 constraints.txt 将所有子包固定到精确版本快照。

    \b
    示例：
    sagellm sync                   # 使用内置精确版本快照安装
    sagellm sync --dry-run         # 预览将安装的版本
    sagellm sync --online          # 从 GitHub 获取最新约束文件
    sagellm sync --include-umbrella   # 同时升级 isagellm 本身

    \b
    等价的手动命令（参考）：
      pip install isagellm -c constraints.txt
      pip install isagellm -c https://raw.githubusercontent.com/intellistream/sagellm/main-dev/constraints.txt
    """
    console = get_console()
    console.print()
    console.print("[bold blue]🔒 sageLLM Sync — 锁定版本安装[/bold blue]")
    console.print()

    # ── 1. 获取 constraints.txt 内容 ─────────────────────────────────────────
    constraints_content: str | None = None
    source_desc = ""

    if online:
        console.print("[cyan]从 GitHub 下载最新 constraints.txt ...[/cyan]")
        constraints_content = _fetch_constraints_from_github()
        if constraints_content:
            source_desc = f"[dim]在线来源: {_CONSTRAINTS_URL}[/dim]"
            console.print("[green]✓ 下载成功[/green]")
        else:
            console.print("[yellow]⚠  GitHub 下载失败，回退到内置版本[/yellow]")

    if constraints_content is None:
        bundled_path = _get_bundled_constraints()
        if bundled_path:
            constraints_content = Path(bundled_path).read_text(encoding="utf-8")
            source_desc = f"[dim]内置来源: {bundled_path}[/dim]"
        else:
            console.print("[red]✗ 未找到 constraints.txt（内置或在线均不可用）[/red]")
            console.print(
                f"[yellow]  请手动运行：pip install isagellm -c {_CONSTRAINTS_URL}[/yellow]"
            )
            raise SystemExit(1)

    console.print(source_desc)
    console.print()

    # ── 2. 解析版本快照 ───────────────────────────────────────────────────────
    pinned = _parse_constraints(constraints_content)

    if not pinned:
        console.print("[red]✗ constraints.txt 解析失败，文件可能为空或格式有误[/red]")
        raise SystemExit(1)

    # ── 3. 显示将安装的版本 ───────────────────────────────────────────────────
    console.print("[bold]将安装以下精确版本：[/bold]")
    console.print()

    packages_to_install: list[str] = []
    for pkg in _SUB_PACKAGES:
        ver = pinned.get(pkg)
        if ver:
            console.print(f"  [cyan]{pkg}[/cyan]==[green]{ver}[/green]")
            packages_to_install.append(f"{pkg}=={ver}")
        else:
            console.print(f"  [dim]{pkg}  (constraints.txt 中未找到，跳过)[/dim]")

    if include_umbrella:
        console.print("  [cyan]isagellm[/cyan]  (最新兼容版本)")

    console.print()

    if dry_run:
        console.print("[yellow]--dry-run 模式：仅预览，不实际安装[/yellow]")
        console.print()
        console.print("[dim]实际安装请运行: sagellm sync[/dim]")
        return

    # ── 4. 写入临时 constraints 文件并执行安装 ────────────────────────────────
    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".txt", prefix="sagellm_constraints_", delete=False
    ) as f:
        f.write(constraints_content)
        tmp_constraints = f.name

    try:
        console.print("[bold]开始安装...[/bold]")
        console.print()

        # Install sub-packages with constraints
        cmd: list[str] = [
            sys.executable,
            "-m",
            "pip",
            "install",
            *packages_to_install,
            "--constraint",
            tmp_constraints,
        ]

        if include_umbrella:
            cmd.append("isagellm")

        logger.debug("Running: %s", " ".join(cmd))

        result = subprocess.run(cmd, check=False)

        if result.returncode == 0:
            console.print()
            console.print("[bold green]✅ 所有子包已同步到精确版本！[/bold green]")
            console.print()
            console.print("[dim]验证安装：sagellm info[/dim]")
        else:
            console.print()
            console.print(f"[red]✗ 安装失败 (exit code: {result.returncode})[/red]")
            console.print(
                "[yellow]  请检查网络或 PyPI 可用性，也可尝试 sagellm sync --online[/yellow]"
            )
            raise SystemExit(result.returncode)

    finally:
        Path(tmp_constraints).unlink(missing_ok=True)
