"""CLI entry point for reddit-mcp."""

from __future__ import annotations

import asyncio
import subprocess
import sys
from pathlib import Path
from shutil import which

from fivemghost_shared_client import TokenManager, run_oauth_flow
from fivemghost_shared_client.config_helpers import AUTH_URL, SHARED_AUTH_JSON
from fivemghost_shared_client.registration import install_skills, register_all

from . import __version__
from .console import console_print
from .config import CONFIG_DIR, ENV_TOKEN, ensure_config_dir

PACKAGE_NAME = "5mghost-rover"
EXECUTABLE_NAME = "reddit-mcp"
SKILL_NAME = "use-reddit-mcp"
SETUP_PHASES = ("install", "auth", "cookies", "register", "verify")


def main() -> None:
    """Main CLI dispatcher."""
    args = sys.argv[1:]
    if not args or args[0] in {"help", "--help", "-h"} or any(arg in {"--help", "-h"} for arg in args[1:]):
        _print_help()
        return
    command = args[0] if args else "help"
    command_args = args[1:]

    match command:
        case "serve":
            _cmd_serve()
        case "setup":
            _cmd_setup(command_args)
        case "setup-cookies":
            _cmd_setup_cookies(command_args)
        case "update":
            _cmd_update()
        case "install-skills":
            _cmd_install_skills()
        case "check":
            _cmd_check()
        case "uninstall":
            _cmd_uninstall()
        case "version" | "--version" | "-v":
            console_print(f"reddit-mcp {__version__}")
        case _:
            _print_help()


def _cmd_serve() -> None:
    """Start the MCP server (stdio transport)."""
    from .server import init_server, mcp

    asyncio.run(init_server())
    mcp.run()


def _cmd_setup(args: list[str]) -> None:
    """Full setup: install validation + auth + cookies + registration + verification."""
    asyncio.run(_async_setup(shutdown_browser=_has_shutdown_browser_flag(args)))


async def _async_setup(*, shutdown_browser: bool = False) -> None:
    from .cookie_manager import setup_cookies

    tm = TokenManager(AUTH_URL, auth_json_path=SHARED_AUTH_JSON, env_token_name=ENV_TOKEN)
    _run_install_phase()
    await _run_auth_phase(tm)
    console_print("🍪 Setting up Reddit cookies...\n")
    try:
        await setup_cookies(shutdown_browser=shutdown_browser)
    except Exception as e:
        console_print(f"❌ Setup phase 'cookies' failed: {e}")
        sys.exit(1)
    _run_register_phase()
    await _run_verify_phase(tm)
    console_print("\n🎉 Setup complete! You can now use reddit-mcp.")

def _run_install_phase() -> None:
    """Validate the local install shape before setup continues."""
    ensure_config_dir()
    console_print(f"📦 Setup phases: {', '.join(SETUP_PHASES)}")
    console_print(f"✅ Setup phase 'install' ready: canonical runtime is `{EXECUTABLE_NAME} serve`.")


async def _run_auth_phase(tm: TokenManager) -> None:
    """Complete the auth phase or confirm existing shared auth."""
    token = await tm.get_valid_token()
    if token:
        console_print(f"✅ Setup phase 'auth' already configured: {SHARED_AUTH_JSON}")
        return

    console_print("🔑 Starting OAuth login flow...\n")
    try:
        tokens = await run_oauth_flow(AUTH_URL, client_name="reddit-mcp-cli")
        await tm.save_tokens(
            access_token=tokens["access_token"],
            refresh_token=tokens["refresh_token"],
            expires_in=tokens["expires_in"],
            client_id=tokens.get("client_id"),
        )
        console_print(f"✅ Setup phase 'auth' saved tokens to shared auth: {SHARED_AUTH_JSON}\n")
        console_print("ℹ️  Other first-party local MCPs on this machine can reuse this login.\n")
    except Exception as e:
        console_print(f"❌ Setup phase 'auth' failed: {e}")
        sys.exit(1)


def _run_register_phase() -> Path:
    """Refresh AI client registration using the current bridge implementation."""
    register_all(
        server_name=EXECUTABLE_NAME,
        command=EXECUTABLE_NAME,
        args=["serve"],
    )
    console_print("✅ Setup phase 'register' completed.")
    _print_manual_configs()
    _install_bundled_skills()
    return CONFIG_DIR


def _print_manual_configs() -> None:
    """Print manual MCP config snippets for clients without auto-registration."""
    console_print("\n📋 Manual MCP config (for Cursor, Windsurf, etc.):")
    console_print(f'''{{
  "mcpServers": {{
    "reddit-mcp": {{
      "command": "{EXECUTABLE_NAME}",
      "args": ["serve"]
    }}
  }}
}}''')
    console_print("\n📋 OpenClaw manual config:")
    console_print(f'''{{
  "servers": {{
    "{EXECUTABLE_NAME}": {{
      "transport": "stdio",
      "command": "{EXECUTABLE_NAME}",
      "args": ["serve"]
    }}
  }}
}}''')


def _install_bundled_skills() -> None:
    """Install bundled skill to detected AI clients."""
    skill_source = Path(__file__).resolve().parent / "skills" / SKILL_NAME

    console_print("\n🧠 Installing bundled skill...")
    install_skills(
        skill_name=SKILL_NAME,
        source_dir=skill_source,
        include_openclaw=True,
    )


async def _run_verify_phase(tm: TokenManager) -> None:
    """Verify the required setup conditions before declaring success."""
    from .cookie_manager import load_cookies

    token = await tm.get_valid_token()
    if not token:
        console_print("❌ Setup phase 'verify' failed: shared auth token is missing.")
        sys.exit(1)

    cookies = load_cookies()
    if not cookies:
        console_print("❌ Setup phase 'verify' failed: Reddit cookies are missing.")
        sys.exit(1)

    console_print("✅ Setup phase 'verify' completed.")


def _cmd_setup_cookies(args: list[str]) -> None:
    """Cookie-only setup (for users who already have a PAT)."""
    asyncio.run(_async_setup_cookies(shutdown_browser=_has_shutdown_browser_flag(args)))


async def _async_setup_cookies(*, shutdown_browser: bool = False) -> None:
    from .cookie_manager import setup_cookies

    console_print("🍪 Setting up Reddit cookies...\n")
    try:
        await setup_cookies(shutdown_browser=shutdown_browser)
    except Exception as e:
        console_print(f"❌ Cookie setup failed: {e}")
        sys.exit(1)

    console_print("\n✅ Cookies saved! Restart your MCP client to start using reddit-mcp.")


def _cmd_update() -> None:
    """Upgrade the installed uv-managed tool."""
    console_print(f"Current version: {__version__}")
    console_print("Updating via uv tool upgrade...\n")

    try:
        _run_package_upgrade()
        console_print("\n✅ Update complete.")
        _install_bundled_skills()

    except subprocess.TimeoutExpired:
        console_print("❌ Timed out checking for updates.")
        console_print("Try manually: uv tool install --upgrade 5mghost-rover")
        sys.exit(1)
    except Exception as e:
        console_print(f"❌ Update failed: {e}")
        console_print("Try manually: uv tool install --upgrade 5mghost-rover")
        sys.exit(1)


def _run_package_upgrade() -> None:
    try:
        subprocess.run(
            [*_resolve_uv_command(), "tool", "upgrade", PACKAGE_NAME],
            check=True,
        )
    except subprocess.CalledProcessError as exc:
        _raise_update_error(exc)


def _resolve_uv_command() -> list[str]:
    uv_on_path = which("uv")
    if uv_on_path:
        return [uv_on_path]

    home_dir = Path.home()
    candidates = [
        home_dir / ".local" / "bin" / ("uv.exe" if sys.platform == "win32" else "uv"),
        home_dir / ".cargo" / "bin" / ("uv.exe" if sys.platform == "win32" else "uv"),
    ]
    for candidate in candidates:
        if candidate.exists():
            return [str(candidate)]

    raise RuntimeError(
        "uv is required for reddit-mcp updates but was not found. "
        "Install uv or rerun the hosted installer, then use "
        "`uv tool install --upgrade 5mghost-rover`."
    )


def _raise_update_error(exc: subprocess.CalledProcessError) -> None:
    details = _extract_update_error_text(exc)
    lower = details.lower()
    if sys.platform == "win32" and (
        "winerror 32" in lower
        or "being used by another process" in lower
        or "进程无法访问" in details
    ):
        raise RuntimeError(
            "Windows could not replace reddit-mcp.exe because another program is still using it. "
            "Close OpenClaw, Codex, Claude, Gemini, and any terminal still running "
            "`reddit-mcp serve`, then rerun "
            "`uv tool install --upgrade 5mghost-rover`."
        )
    raise exc


def _extract_update_error_text(exc: subprocess.CalledProcessError) -> str:
    parts: list[str] = []
    if exc.stderr:
        parts.append(exc.stderr)
    if exc.stdout:
        parts.append(exc.stdout)
    if not parts:
        parts.append(str(exc))
    return "\n".join(parts)


def _cmd_install_skills() -> None:
    """Install or refresh bundled reddit-mcp skill in detected AI clients."""
    _install_bundled_skills()


def _cmd_check() -> None:
    """Check auth and cookie status."""
    asyncio.run(_async_check())


def _cmd_uninstall() -> None:
    """Remove local config and MCP registrations."""
    from .uninstall import run_uninstall

    run_uninstall()


async def _async_check() -> None:
    from .cookie_manager import (
        check_cookies_valid,
        cookies_age_warning,
        get_cookie_age_days,
        get_session_expiry_days,
        load_cookies,
    )

    console_print(f"reddit-mcp {__version__}\n")

    # Token check
    tm = TokenManager(AUTH_URL, auth_json_path=SHARED_AUTH_JSON, env_token_name=ENV_TOKEN)
    token = await tm.get_valid_token()
    if token:
        masked = token[:8] + "..." if len(token) > 8 else "***"
        console_print(f"🔑 Shared auth: ✅ present ({masked})")
        console_print(f"   Path: {SHARED_AUTH_JSON}")

        # Server-side verification
        verify = await tm.verify_token_with_server(token)
        if verify.get("active"):
            console_print("🔐 Server verification: ✅ active")
        else:
            error = verify.get("error")
            if error:
                console_print(f"🔐 Server verification: ❌ {error}")
            else:
                console_print("🔐 Server verification: ❌ token invalid or revoked")
            console_print("   Run `reddit-mcp setup` to re-authenticate.")
    else:
        console_print("🔑 Shared auth: ❌ not configured")
        console_print(f"   Path: {SHARED_AUTH_JSON}")
        console_print("   Run `reddit-mcp setup` or set REDDIT_MCP_TOKEN env var")

    # Cookie check
    cookies = load_cookies()
    if not cookies:
        console_print("🍪 Cookies: ❌ not found")
        console_print("   Run `reddit-mcp setup-cookies`")
        return

    age = get_cookie_age_days()
    age_str = f" ({age:.0f} days old)" if age else ""
    remaining = get_session_expiry_days()
    expiry_str = f", expires in {remaining:.0f} days" if remaining and remaining > 0 else ""
    valid = await check_cookies_valid(cookies)
    if valid:
        console_print(f"🍪 Cookies: ✅ valid{age_str}{expiry_str}")
    else:
        console_print(f"🍪 Cookies: ❌ expired or invalid{age_str}")
        console_print("   Run `reddit-mcp setup-cookies` to refresh")

    warning = cookies_age_warning()
    if warning:
        console_print(f"\n{warning}")


def _print_help() -> None:
    console_print(f"""reddit-mcp {__version__} — Reddit MCP server

Usage:
  reddit-mcp serve           Start MCP server (stdio)
  reddit-mcp setup [--shutdown-browser]
                           Full setup: OAuth login + Reddit cookies
  reddit-mcp setup-cookies [--shutdown-browser|--shutdownbrowser]
                           Cookie-only setup (if you already have a PAT)
  reddit-mcp uninstall       Remove MCP registrations and local config
  reddit-mcp update          Check for updates and install latest version
  reddit-mcp install-skills  Install/refresh bundled reddit-mcp skill
  reddit-mcp check           Check token & cookie status
  reddit-mcp version         Show version

Quick start:
  1. uv tool install {PACKAGE_NAME}
  2. {EXECUTABLE_NAME} setup
  3. Configure in your AI client:
     "command": "{EXECUTABLE_NAME}", "args": ["serve"]
""")


def _has_shutdown_browser_flag(args: list[str]) -> bool:
    return any(arg in {"--shutdown-browser", "--shutdownbrowser"} for arg in args)


if __name__ == "__main__":
    main()
