"""CLI 共享工具。

提供凭据加载、SDK 实例创建、Rich 输出格式化等公共工具函数。
"""

import asyncio
import json
import sys
from collections.abc import Callable
from functools import wraps
from pathlib import Path
from typing import Any, TypeVar

import typer
from rich.console import Console
from rich.table import Table

from hezor_common.cli.credential import CredentialManager, ProfileData

console = Console()
err_console = Console(stderr=True)

F = TypeVar("F", bound=Callable[..., Any])


def require_login(func: F) -> F:
    """装饰器：要求用户已登录。

    未登录时输出错误信息并退出。
    """

    @wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        cm = CredentialManager()
        profile = cm.load_profile()
        if not profile:
            err_console.print("[red]✗[/red] 未登录。请先运行 [bold]hezor2 login <host>[/bold]")
            raise typer.Exit(code=1)
        return func(*args, **kwargs)

    return wrapper  # type: ignore[return-value]


def get_profile() -> ProfileData:
    """获取当前活跃 Profile。

    Returns
    -------
    ProfileData
        当前登录的 Profile 数据

    Raises
    ------
    typer.Exit
        未登录时退出
    """
    cm = CredentialManager()
    profile = cm.load_profile()
    if not profile:
        err_console.print("[red]✗[/red] 未登录。请先运行 [bold]hezor2 login <host>[/bold]")
        raise typer.Exit(code=1)
    return profile


def create_sdk_from_profile(profile: ProfileData) -> Any:
    """从 Profile 创建 SDK 实例。

    Parameters
    ----------
    profile : ProfileData
        已登录的 Profile

    Returns
    -------
    Hezor2SDK
        SDK 实例（需要在 async with 中使用）
    """
    from hezor_common.transfer.base_sdk import MetaInfo
    from hezor_common.transfer.hezor2_sdk.sdk import Hezor2SDK

    meta_info: MetaInfo | None = None
    if profile.caller_id:
        meta_info = MetaInfo(
            subject="anonymous",
            subject_code="anonymous",
            caller_id=profile.caller_id,
        )

    # 如果 Profile 中保存了证书信息，使用 PEM 内容签名
    private_key_pem: str | None = None
    password: bytes | None = None
    if profile.cert_content:
        private_key_pem = profile.cert_content
        if profile.client_secret:
            password = profile.client_secret.encode("utf-8")

    return Hezor2SDK(
        base_url=profile.base_url,
        api_key=profile.api_key,
        app_name=profile.app_name,
        meta_info=meta_info,
        private_key_pem=private_key_pem,
        password=password,
    )


def run_async(coro: Any) -> Any:
    """运行异步函数。

    Parameters
    ----------
    coro : Coroutine
        异步协程

    Returns
    -------
    Any
        协程返回值
    """
    return asyncio.run(coro)


def print_json_output(data: Any, raw: bool = False) -> None:
    """输出 JSON 数据。

    Parameters
    ----------
    data : Any
        要输出的数据（Pydantic model、dict 或 list）
    raw : bool
        是否输出原始 JSON（无格式化）
    """
    if hasattr(data, "model_dump"):
        obj = data.model_dump(mode="json")
    elif hasattr(data, "dict"):
        obj = data.dict()
    else:
        obj = data

    text = json.dumps(obj, ensure_ascii=False, indent=None if raw else 2)
    if raw:
        sys.stdout.write(text + "\n")
    else:
        console.print_json(text)


def print_table(
    title: str,
    columns: list[tuple[str, str]],
    rows: list[list[str]],
) -> None:
    """输出 Rich 表格。

    Parameters
    ----------
    title : str
        表格标题
    columns : list[tuple[str, str]]
        列定义 [(name, style), ...]
    rows : list[list[str]]
        行数据
    """
    table = Table(title=title, show_lines=True)
    for name, style in columns:
        table.add_column(name, style=style)
    for row in rows:
        table.add_row(*row)
    console.print(table)


def read_json_file(path: str) -> dict[str, Any]:
    """读取本地 JSON 文件。

    Parameters
    ----------
    path : str
        JSON 文件路径

    Returns
    -------
    dict[str, Any]
        解析后的 JSON 数据
    """
    p = Path(path).expanduser().resolve()
    if not p.exists():
        err_console.print(f"[red]✗[/red] 文件不存在: {p}")
        raise typer.Exit(code=1)
    if p.suffix.lower() != ".json":
        err_console.print(f"[red]✗[/red] 不是 JSON 文件: {p}")
        raise typer.Exit(code=1)

    try:
        return json.loads(p.read_text(encoding="utf-8"))
    except json.JSONDecodeError as e:
        err_console.print(f"[red]✗[/red] JSON 解析失败: {e}")
        raise typer.Exit(code=1) from e
