"""CLI 认证命令。

提供 login、logout、status、sessions 等认证相关命令。
"""

import platform

import httpx
import typer
from rich.prompt import Prompt

from hezor_common.cli._common import (
    console,
    err_console,
    get_profile,
    print_table,
    run_async,
)
from hezor_common.cli.credential import CredentialManager, ProfileData

app = typer.Typer(no_args_is_help=True)
sessions_app = typer.Typer(name="sessions", help="管理 CLI 登录会话。", no_args_is_help=True)
app.add_typer(sessions_app, name="sessions")

# 获取版本号
try:
    from hezor_common import __version__ as _cli_version
except ImportError:
    _cli_version = "unknown"


def _build_base_url(host: str) -> str:
    """从 host 构建 base_url。"""
    import re

    host = host.rstrip("/")
    if not host.startswith(("http://", "https://")):
        is_local = bool(re.match(r"^(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?$", host))
        scheme = "http" if is_local else "https"
        host = f"{scheme}://{host}"
    return f"{host}/api/v1"


@app.command()
def login(
    host: str = typer.Argument(..., help="Hezor2 服务器地址，如 https://example.hezor.com"),
    profile: str = typer.Option("default", "--profile", "-p", help="Profile 名称"),
    app_name: str | None = typer.Option(
        None, "--app-name", "-a", help="应用名称（用于证书解析，默认 public）"
    ),
) -> None:
    """登录 Hezor2 平台。

    使用 API Key 登录并保存凭据到本地加密文件。
    """
    base_url = _build_base_url(host)

    # 提示输入 API Key（隐藏输入）
    api_key = Prompt.ask("API Key", password=True, console=console)
    if not api_key:
        err_console.print("[red]✗[/red] API Key 不能为空")
        raise typer.Exit(code=1)

    console.print(f"[dim]连接到 {base_url} ...[/dim]")

    async def _do_login() -> ProfileData:
        login_payload = {
            "device_name": platform.node(),
            "os_info": f"{platform.system()} {platform.release()}",
            "cli_version": _cli_version,
            "sdk_type": "python",
        }

        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.post(
                f"{base_url}/auth/cli/login",
                json=login_payload,
                headers={"Authorization": f"Bearer {api_key}"},
            )
            if resp.status_code == 401:
                err_console.print("[red]✗[/red] API Key 无效或已过期")
                raise typer.Exit(code=1)
            if resp.status_code != 200:
                err_console.print(f"[red]✗[/red] 登录失败 (HTTP {resp.status_code}): {resp.text}")
                raise typer.Exit(code=1)

            data = resp.json()

        return ProfileData(
            host=host.rstrip("/"),
            base_url=base_url,
            api_key=api_key,
            session_id=data["session_id"],
            user_id=data["user_id"],
            user_name=data["user_name"],
            display_name=data["display_name"],
            expires_at=data.get("expires_at"),
            app_name=app_name,
        )

    try:
        profile_data = run_async(_do_login())
    except httpx.ConnectError as exc:
        err_console.print(f"[red]✗[/red] 无法连接到 {base_url}")
        raise typer.Exit(code=1) from exc

    cm = CredentialManager()
    cm.save_profile(profile, profile_data)

    console.print("[green]✓[/green] 登录成功！")
    console.print(f"  用户: {profile_data.display_name} ({profile_data.user_name})")
    console.print(f"  服务器: {profile_data.host}")
    console.print(f"  Profile: {profile}")


@app.command()
def connect(
    host: str = typer.Argument(..., help="Hezor2 服务器地址，如 https://example.hezor.com"),
    profile: str = typer.Option("default", "--profile", "-p", help="Profile 名称"),
) -> None:
    """匿名连接 — 通过微信扫码获取 caller_id。

    无需 API Key，扫码后将 openid 作为 caller_id 保存到 Profile。
    """
    import asyncio

    base_url = _build_base_url(host)
    console.print(f"[dim]连接到 {base_url} ...[/dim]")

    async def _get_qr() -> tuple[str, str]:
        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.post(
                f"{base_url}/auth/wechat/login-url",
                json={"redirect": ""},
            )
            if resp.status_code != 200:
                err_console.print(f"[red]✗[/red] 获取微信二维码失败 (HTTP {resp.status_code})")
                raise typer.Exit(code=1)
            data = resp.json()
            inner = data.get("data", data)
            return inner["url"], inner["key"]

    try:
        url, key = run_async(_get_qr())
    except httpx.ConnectError as exc:
        err_console.print(f"[red]✗[/red] 无法连接到 {base_url}")
        raise typer.Exit(code=1) from exc

    console.print("\n[bold]请在浏览器中打开以下链接扫码:[/bold]")
    console.print(f"  {url}\n")
    console.print("[dim]等待扫码...[/dim]")

    async def _poll() -> str:
        max_attempts = 60  # 每 2 秒轮询，最多 120 秒
        async with httpx.AsyncClient(timeout=15.0) as client:
            for _ in range(max_attempts):
                await asyncio.sleep(2)
                try:
                    resp = await client.get(
                        f"{base_url}/auth/wechat/poll-openid",
                        params={"key": key},
                    )
                    if resp.status_code != 200:
                        continue
                    result = resp.json()
                    status = result.get("status", "")
                    if status == "success" and result.get("openid"):
                        return result["openid"]
                    if status == "expired":
                        err_console.print("\n[red]✗[/red] 二维码已过期，请重新运行命令")
                        raise typer.Exit(code=1)
                except httpx.HTTPError:
                    pass  # 网络波动时继续轮询
                console.print(".", end="")

        err_console.print("\n[red]✗[/red] 等待超时，请重新运行命令")
        raise typer.Exit(code=1)

    openid: str = run_async(_poll())

    profile_data = ProfileData(
        host=host.rstrip("/"),
        base_url=base_url,
        api_key="",
        session_id="",
        user_id="",
        user_name="anonymous",
        display_name="匿名用户",
        app_name="public",
        caller_id=openid,
    )

    cm = CredentialManager()
    cm.save_profile(profile, profile_data)

    console.print("\n[green]✓[/green] 连接成功！")
    console.print(f"  caller_id: {openid}")
    console.print(f"  Profile: {profile}")


@app.command()
def logout(
    profile: str | None = typer.Option(
        None, "--profile", "-p", help="Profile 名称（默认使用当前活跃 Profile）"
    ),
) -> None:
    """登出 Hezor2 平台。

    注销服务端会话并删除本地凭据。
    """
    cm = CredentialManager()
    profile_name = profile or cm.get_active_profile_name()
    profile_data = cm.load_profile(profile_name)

    if not profile_data:
        err_console.print(f"[yellow]![/yellow] Profile '{profile_name}' 不存在或未登录")
        raise typer.Exit(code=0)

    # 服务端登出
    async def _do_logout() -> None:
        if not profile_data.api_key:
            return  # 匿名 Profile 无需服务端登出
        try:
            async with httpx.AsyncClient(timeout=15.0) as client:
                await client.post(
                    f"{profile_data.base_url}/auth/cli/logout",
                    headers={"Authorization": f"Bearer {profile_data.api_key}"},
                    json={"session_id": profile_data.session_id},
                )
        except httpx.HTTPError:
            pass  # 服务端登出失败不阻塞本地清理

    run_async(_do_logout())
    cm.delete_profile(profile_name)
    console.print(f"[green]✓[/green] 已登出 (Profile: {profile_name})")


@app.command()
def status() -> None:
    """查看当前登录状态。"""
    cm = CredentialManager()
    profiles = cm.list_profiles()

    if not profiles:
        console.print("[dim]未登录任何账户[/dim]")
        console.print("运行 [bold]hezor2 login <host>[/bold] 开始使用")
        return

    active = cm.get_active_profile_name()

    rows: list[list[str]] = []
    for name in profiles:
        p = cm.load_profile(name)
        if p:
            marker = "★" if name == active else ""
            is_anon = "[dim](匿名)[/dim]" if p.caller_id else ""
            display = f"{p.display_name} {is_anon}".strip()
            rows.append(
                [
                    f"{marker} {name}".strip(),
                    display,
                    p.host,
                    p.logged_in_at or "-",
                    p.expires_at or "-",
                ]
            )

    print_table(
        "登录状态",
        [
            ("Profile", "cyan"),
            ("用户", "green"),
            ("服务器", "blue"),
            ("登录时间", "dim"),
            ("过期时间", "dim"),
        ],
        rows,
    )


@sessions_app.command("list")
def sessions_list() -> None:
    """列出服务端活跃会话。"""
    profile_data = get_profile()

    if not profile_data.api_key:
        err_console.print(
            "[red]✗[/red] 匿名 Profile 无法管理会话。请使用 [bold]hezor2 login[/bold] 登录。"
        )
        raise typer.Exit(code=1)

    async def _list() -> list[dict]:
        async with httpx.AsyncClient(timeout=15.0) as client:
            resp = await client.get(
                f"{profile_data.base_url}/auth/cli/sessions",
                headers={"Authorization": f"Bearer {profile_data.api_key}"},
            )
            if resp.status_code != 200:
                err_console.print(f"[red]✗[/red] 获取会话失败 (HTTP {resp.status_code})")
                raise typer.Exit(code=1)
            data = resp.json()
            return data.get("sessions", [])

    sessions = run_async(_list())

    if not sessions:
        console.print("[dim]没有活跃会话[/dim]")
        return

    rows: list[list[str]] = []
    for s in sessions:
        rows.append(
            [
                s.get("id", "-")[:8] + "...",
                s.get("device_name", "-"),
                s.get("sdk_type", "-"),
                s.get("created_at", "-"),
                s.get("last_active_at", "-"),
            ]
        )

    print_table(
        "活跃会话",
        [
            ("会话 ID", "cyan"),
            ("设备", "green"),
            ("SDK", "blue"),
            ("创建时间", "dim"),
            ("最后活跃", "dim"),
        ],
        rows,
    )


@sessions_app.command("revoke")
def sessions_revoke(
    session_id: str = typer.Argument(..., help="要撤销的会话 ID"),
) -> None:
    """撤销指定会话。"""
    profile_data = get_profile()

    if not profile_data.api_key:
        err_console.print(
            "[red]✗[/red] 匿名 Profile 无法管理会话。请使用 [bold]hezor2 login[/bold] 登录。"
        )
        raise typer.Exit(code=1)

    async def _revoke() -> None:
        async with httpx.AsyncClient(timeout=15.0) as client:
            resp = await client.delete(
                f"{profile_data.base_url}/auth/cli/sessions/{session_id}",
                headers={"Authorization": f"Bearer {profile_data.api_key}"},
            )
            if resp.status_code == 404:
                err_console.print(f"[red]✗[/red] 会话 {session_id} 不存在")
                raise typer.Exit(code=1)
            if resp.status_code != 200:
                err_console.print(f"[red]✗[/red] 撤销失败 (HTTP {resp.status_code})")
                raise typer.Exit(code=1)

    run_async(_revoke())
    console.print(f"[green]✓[/green] 会话 {session_id[:8]}... 已撤销")
