"""VPN MCP server — transparent proxy for Claude Code via xray exit nodes."""

import logging
import platform
import sys

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool

from .client import ControlPlaneClient, Credentials
from .config import settings
from .proxy import XrayProxy, download_xray, generate_pac_file, generate_xray_config, get_xray_binary, XRAY_VERSION

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s", stream=sys.stderr)
logger = logging.getLogger(__name__)

server = Server("vpn-mcp")
xray_proxy = XrayProxy()


def _get_creds() -> Credentials | None:
    return Credentials.load()


def _get_client() -> ControlPlaneClient | None:
    creds = _get_creds()
    if creds is None:
        return None
    return ControlPlaneClient(creds)


@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="vpn_connect",
            description=(
                "Connect to a VPN exit node. Starts a local HTTP/SOCKS5 proxy via xray. "
                "After connecting, all WebFetch requests route through the VPN automatically. "
                "If the account is not yet activated, returns payment instructions — "
                "show these to the user so they can pay with a TON wallet app. "
                "Do NOT open a browser for payment — the payment address is returned directly by this tool."
            ),
            inputSchema={
                "type": "object",
                "properties": {"node": {"type": "string", "description": "Exit node ID (e.g. 'na-1'). Optional."}},
            },
        ),
        Tool(
            name="vpn_disconnect",
            description="Disconnect from VPN. Stops the local xray proxy. WebFetch returns to direct connection.",
            inputSchema={"type": "object", "properties": {}},
        ),
        Tool(
            name="vpn_switch",
            description="Switch to a different VPN exit node. Restarts the proxy with the new node.",
            inputSchema={
                "type": "object",
                "properties": {"node": {"type": "string", "description": "Exit node ID to switch to."}},
                "required": ["node"],
            },
        ),
        Tool(
            name="vpn_status",
            description=(
                "Check VPN account status: payment state, quota used/remaining, expiry, active node. "
                "Use after payment to verify activation before connecting."
            ),
            inputSchema={"type": "object", "properties": {}},
        ),
        Tool(
            name="vpn_nodes",
            description="List available VPN exit nodes with regions. Requires an active (paid) account.",
            inputSchema={"type": "object", "properties": {}},
        ),
        Tool(
            name="vpn_activate",
            description=(
                "Activate VPN account. Returns a TON USDT payment address and instructions. "
                "Show the address and instructions to the user — they need to send 1 USDT "
                "using a TON wallet app (Tonkeeper, TonHub, etc). Payment is detected automatically. "
                "Do NOT open a browser — all payment info is returned by this tool. "
                "After the user pays, call vpn_status() to check, then vpn_connect() to start."
            ),
            inputSchema={"type": "object", "properties": {}},
        ),
        Tool(
            name="vpn_setup",
            description="Download and install xray binary for the current OS. Called automatically by vpn_connect if needed.",
            inputSchema={"type": "object", "properties": {}},
        ),
    ]


@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    try:
        if name == "vpn_connect":
            return [TextContent(type="text", text=_handle_connect(arguments.get("node", "")))]
        elif name == "vpn_disconnect":
            return [TextContent(type="text", text=_handle_disconnect())]
        elif name == "vpn_switch":
            return [TextContent(type="text", text=_handle_connect(arguments["node"]))]
        elif name == "vpn_status":
            return [TextContent(type="text", text=_handle_status())]
        elif name == "vpn_nodes":
            return [TextContent(type="text", text=_handle_nodes())]
        elif name == "vpn_activate":
            return [TextContent(type="text", text=_handle_activate())]
        elif name == "vpn_setup":
            return [TextContent(type="text", text=_handle_setup())]
        else:
            return [TextContent(type="text", text=f"Unknown tool: {name}")]
    except Exception as e:
        logger.exception("Tool %s failed", name)
        return [TextContent(type="text", text=f"Error: {e}")]


def _payment_instructions(address: str, amount: str = "1", network: str = "TON") -> str:
    """Format clear payment instructions for the user."""
    return (
        f"To activate VPN MCP, send {amount} USDT on the {network} network to:\n\n"
        f"  {address}\n\n"
        f"How to pay:\n"
        f"  1. Open a TON wallet app (Tonkeeper, TonHub, MyTonWallet, or any TON wallet)\n"
        f"  2. Send {amount} USDT (not TON) to the address above\n"
        f"  3. Payment is detected automatically within 30 seconds\n\n"
        f"Or visit https://vpn-mcp.net/pay.html for a QR code payment page.\n\n"
        f"Pricing: {amount} USDT = 2 GB traffic, 30 days, all exit nodes.\n\n"
        f"After payment, call vpn_connect() to start using the VPN."
    )


def _handle_connect(node_id: str) -> str:
    creds = _get_creds()
    if creds is None:
        return _handle_activate()

    client = ControlPlaneClient(creds)

    # Check account status.
    status_resp = client.status()
    status = status_resp.get("status")

    if status == "pending_payment":
        payment = status_resp.get("payment", {})
        return _payment_instructions(
            payment.get("address", "unknown"), payment.get("amount", "1"), payment.get("network", "TON")
        )

    if status == "expired":
        return "Account expired. Call vpn_activate() to purchase again."

    if status == "quota_exceeded":
        quota = status_resp.get("quota", {})
        return (
            f"Quota exceeded ({quota.get('used_bytes', 0) // (1024**3)}GB / "
            f"{quota.get('limit_bytes', 0) // (1024**3)}GB). Call vpn_activate() to purchase more."
        )

    if status != "active":
        return f"Account status: {status}. Call vpn_activate() if you need a new account."

    # Ensure xray binary is installed.
    try:
        get_xray_binary()
    except RuntimeError:
        download_xray()

    # Connect to node.
    connect_resp = client.connect(node_id)
    if "error" in connect_resp:
        if connect_resp["error"] == "quota_exceeded":
            quota = connect_resp.get("quota", {})
            return f"Quota exceeded ({quota.get('percent', 100)}%). Call vpn_activate() to purchase more."
        return f"Connection error: {connect_resp['error']}"

    vless = connect_resp.get("vless", {})
    config = generate_xray_config(
        node_ip=vless["address"],
        node_port=vless["port"],
        uuid=connect_resp["xray_uuid"],
        flow=vless.get("flow", "xtls-rprx-vision"),
        reality_public_key=vless["reality_public_key"],
        reality_short_id=vless["reality_short_id"],
        reality_sni=vless.get("reality_sni", "dl.google.com"),
    )

    xray_proxy.start(config, connect_resp.get("node_id", node_id))
    pac_path = generate_pac_file()

    # Build response with quota warning if applicable.
    quota = connect_resp.get("quota", {})
    warning = connect_resp.get("warning") or ""
    quota_line = f"Quota: {quota.get('used_bytes', 0) // (1024**3)}GB / {quota.get('limit_bytes', 0) // (1024**3)}GB"

    lines = [
        f"Connected to {connect_resp.get('node_id', 'unknown')}. WebFetch now routes through VPN.",
        "",
        f"  {quota_line}",
        "",
        "Browser/apps:",
        f"  HTTP proxy:   127.0.0.1:{settings.http_proxy_port}",
        f"  SOCKS5 proxy: 127.0.0.1:{settings.socks_proxy_port}",
        f"  PAC file:     {pac_path}",
        "",
        "To use in your browser, set auto-config proxy URL to:",
        f"  file://{pac_path}",
    ]
    if warning:
        lines.insert(1, f"\n{warning}")
    return "\n".join(lines)


def _handle_disconnect() -> str:
    if not xray_proxy.running:
        return "Not connected."
    xray_proxy.stop()
    return "Disconnected. WebFetch now uses direct connection."


def _handle_status() -> str:
    client = _get_client()
    if client is None:
        return "No VPN account configured. Call vpn_activate() to register."

    resp = client.status()
    status = resp.get("status", "unknown")
    quota = resp.get("quota", {})
    connected = "yes" if xray_proxy.running else "no"

    lines = [
        f"Status: {status}",
        f"Connected: {connected} (node: {xray_proxy.current_node or 'none'})",
        f"Quota: {quota.get('used_bytes', 0) // (1024**3)}GB / {quota.get('limit_bytes', 0) // (1024**3)}GB ({quota.get('percent', 0)}%)",
        f"Expires: {quota.get('expires_at', 'unknown')}",
    ]

    if status == "pending_payment":
        payment = resp.get("payment", {})
        lines.append(f"\nPayment pending. Send {payment.get('amount', '1')} USDT to: {payment.get('address', '')}")

    return "\n".join(lines)


def _handle_nodes() -> str:
    client = _get_client()
    if client is None:
        return "No VPN account configured. Call vpn_activate() to register."

    nodes = client.nodes()
    if not nodes:
        return "No exit nodes available."

    lines = ["Available exit nodes:", ""]
    for n in nodes:
        lines.append(f"  {n['id']:10s} {n['name']:30s} ({n['region']})")
    return "\n".join(lines)


def _handle_activate() -> str:
    creds = _get_creds()

    # If already registered, check status.
    if creds:
        client = ControlPlaneClient(creds)
        resp = client.status()
        status = resp.get("status")
        if status == "active":
            return "Account already active. Use vpn_connect() to connect."
        if status == "pending_payment":
            payment = resp.get("payment", {})
            return _payment_instructions(
                payment.get("address", "unknown"), payment.get("amount", "1"), payment.get("network", "TON")
            )

    # Register new account.
    resp = ControlPlaneClient.register(settings.control_plane_url)

    # Save credentials.
    creds = Credentials(
        control_plane_url=settings.control_plane_url,
        client_id=resp["client_id"],
        api_key=resp["api_key"],
    )
    creds.save()

    payment = resp.get("payment", {})
    return f"VPN MCP account created. API key saved to {settings.credentials_path}\n\n" + _payment_instructions(
        payment.get("address", "unknown"), payment.get("amount", "1"), payment.get("network", "TON")
    )


def _handle_setup() -> str:
    try:
        binary = get_xray_binary()
        if binary.exists():
            return f"xray already installed at {binary}"
    except RuntimeError:
        pass

    binary = download_xray()
    return f"xray {XRAY_VERSION} installed at {binary} ({platform.system()}/{platform.machine()})"


def main():
    import asyncio

    async def run():
        async with stdio_server() as (read_stream, write_stream):
            await server.run(read_stream, write_stream, server.create_initialization_options())

    asyncio.run(run())


if __name__ == "__main__":
    main()
