"""VPS.org MCP Server — 26 tools for managing VPS infrastructure via AI assistants."""

from __future__ import annotations

import os
import sys

from mcp.server.fastmcp import FastMCP

from vps_mcp.client import (
    VPSClient,
    format_dns_zone,
    format_domain,
    format_generic,
    format_location,
    format_os,
    format_paginated,
    format_plan,
    format_server,
    format_ssh_key,
)

mcp = FastMCP(
    "VPS.org",
    instructions="Manage VPS servers, domains, DNS records, SSH keys, and more via the VPS.org API",
)

_client: VPSClient | None = None


def _get_client() -> VPSClient:
    """Lazy-init the API client using environment variables."""
    global _client
    if _client is None:
        token = os.environ.get("VPS_API_TOKEN", "")
        if not token:
            raise RuntimeError(
                "VPS_API_TOKEN environment variable is required. "
                "Get your token at https://admin.vps.org/account/developers/"
            )
        base_url = os.environ.get("VPS_API_URL", "https://admin.vps.org/api/v1")
        _client = VPSClient(api_token=token, base_url=base_url)
    return _client


# ══════════════════════════════════════════════════════════════════════
# Servers (8 tools)
# ══════════════════════════════════════════════════════════════════════


@mcp.tool()
async def list_servers(page: int = 1, page_size: int = 20) -> str:
    """List all VPS servers in your account.

    Args:
        page: Page number (default 1)
        page_size: Results per page (default 20, max 100)
    """
    client = _get_client()
    data = await client._get("/servers/", params={"page": page, "page_size": page_size})
    return format_paginated(data, format_server)


@mcp.tool()
async def get_server(server_uuid: str) -> str:
    """Get detailed information about a specific VPS server.

    Args:
        server_uuid: The UUID of the server
    """
    client = _get_client()
    data = await client._get(f"/servers/{server_uuid}/")
    if isinstance(data, str):
        return data
    return format_server(data)


@mcp.tool()
async def create_server(
    hostname: str,
    location_id: int,
    plan_uuid: str,
    os_version_uuid: str,
    ssh_key_ids: list[str] | None = None,
) -> str:
    """Create a new VPS server.

    Args:
        hostname: Hostname for the server (e.g. "my-server")
        location_id: Location ID (use list_locations to find available locations)
        plan_uuid: UUID of the plan (use list_plans to find available plans)
        os_version_uuid: UUID of the OS version (use list_operating_systems to find options)
        ssh_key_ids: Optional list of SSH key UUIDs to install on the server
    """
    client = _get_client()
    body: dict = {
        "hostname": hostname,
        "location": location_id,
        "plan": plan_uuid,
        "os_version": os_version_uuid,
    }
    if ssh_key_ids:
        body["ssh_keys"] = ssh_key_ids
    data = await client._post("/servers/", json=body)
    if isinstance(data, str):
        return data
    return f"Server creation initiated.\n\n{format_server(data)}"


@mcp.tool()
async def delete_server(server_uuid: str) -> str:
    """Permanently delete a VPS server. This action is irreversible.

    Args:
        server_uuid: The UUID of the server to delete
    """
    client = _get_client()
    data = await client._delete(f"/servers/{server_uuid}/")
    if isinstance(data, str):
        return data
    return f"Server {server_uuid} has been deleted."


@mcp.tool()
async def server_power(server_uuid: str, action: str) -> str:
    """Control the power state of a VPS server.

    Args:
        server_uuid: The UUID of the server
        action: Power action — one of: "start", "stop", "restart", "hard_stop"
    """
    valid_actions = {"start", "stop", "restart", "hard_stop"}
    if action not in valid_actions:
        return f"Invalid action '{action}'. Must be one of: {', '.join(sorted(valid_actions))}"
    client = _get_client()
    data = await client._post(f"/servers/{server_uuid}/power/", json={"action": action})
    if isinstance(data, str):
        return data
    return f"Power action '{action}' sent to server {server_uuid}."


@mcp.tool()
async def create_backup(server_uuid: str) -> str:
    """Create a backup of a VPS server.

    Args:
        server_uuid: The UUID of the server to back up
    """
    client = _get_client()
    data = await client._post(f"/servers/{server_uuid}/backup/")
    if isinstance(data, str):
        return data
    return f"Backup initiated for server {server_uuid}.\n\n{format_generic(data)}"


@mcp.tool()
async def create_snapshot(server_uuid: str, label: str | None = None) -> str:
    """Create a snapshot of a VPS server.

    Args:
        server_uuid: The UUID of the server to snapshot
        label: Optional label for the snapshot
    """
    client = _get_client()
    body: dict = {}
    if label:
        body["label"] = label
    data = await client._post(f"/servers/{server_uuid}/snapshot/", json=body or None)
    if isinstance(data, str):
        return data
    return f"Snapshot initiated for server {server_uuid}.\n\n{format_generic(data)}"


@mcp.tool()
async def resize_server(server_uuid: str, new_plan_uuid: str) -> str:
    """Resize a VPS server to a different plan. The server may be restarted.

    Args:
        server_uuid: The UUID of the server to resize
        new_plan_uuid: The UUID of the new plan (use list_plans to see options)
    """
    client = _get_client()
    data = await client._post(
        f"/servers/{server_uuid}/resize/", json={"plan": new_plan_uuid}
    )
    if isinstance(data, str):
        return data
    return f"Resize initiated for server {server_uuid}.\n\n{format_generic(data)}"


# ══════════════════════════════════════════════════════════════════════
# Domains (5 tools)
# ══════════════════════════════════════════════════════════════════════


@mcp.tool()
async def list_domains(page: int = 1) -> str:
    """List all domains in your account.

    Args:
        page: Page number (default 1)
    """
    client = _get_client()
    data = await client._get("/domains/", params={"page": page})
    return format_paginated(data, format_domain)


@mcp.tool()
async def get_domain(domain_uuid: str) -> str:
    """Get detailed information about a specific domain.

    Args:
        domain_uuid: The UUID of the domain
    """
    client = _get_client()
    data = await client._get(f"/domains/{domain_uuid}/")
    if isinstance(data, str):
        return data
    return format_domain(data)


@mcp.tool()
async def search_domains(domain: str) -> str:
    """Search for domain availability.

    Args:
        domain: The domain name to search for (e.g. "example.com")
    """
    client = _get_client()
    data = await client._post("/domains/search/", json={"domain": domain})
    if isinstance(data, str):
        return data
    return format_generic(data)


@mcp.tool()
async def update_nameservers(
    domain_uuid: str,
    ns1: str,
    ns2: str | None = None,
    ns3: str | None = None,
    ns4: str | None = None,
) -> str:
    """Update the nameservers for a domain.

    Args:
        domain_uuid: The UUID of the domain
        ns1: Primary nameserver (required)
        ns2: Secondary nameserver
        ns3: Third nameserver
        ns4: Fourth nameserver
    """
    client = _get_client()
    nameservers = [ns for ns in [ns1, ns2, ns3, ns4] if ns]
    data = await client._post(
        f"/domains/{domain_uuid}/update_nameservers/",
        json={"nameservers": nameservers},
    )
    if isinstance(data, str):
        return data
    return f"Nameservers updated for domain {domain_uuid}.\n\n{format_generic(data)}"


@mcp.tool()
async def renew_domain(domain_uuid: str, years: int = 1) -> str:
    """Renew a domain registration.

    Args:
        domain_uuid: The UUID of the domain to renew
        years: Number of years to renew for (default 1)
    """
    client = _get_client()
    data = await client._post(
        f"/domains/{domain_uuid}/renew/", json={"years": years}
    )
    if isinstance(data, str):
        return data
    return f"Domain {domain_uuid} renewed for {years} year(s).\n\n{format_generic(data)}"


# ══════════════════════════════════════════════════════════════════════
# DNS (7 tools)
# ══════════════════════════════════════════════════════════════════════


@mcp.tool()
async def list_dns_zones(domain: str | None = None) -> str:
    """List DNS zones, optionally filtered by domain name.

    Args:
        domain: Optional domain name to filter by
    """
    client = _get_client()
    params: dict = {}
    if domain:
        params["domain"] = domain
    data = await client._get("/dns-zones/", params=params or None)
    return format_paginated(data, format_dns_zone)


@mcp.tool()
async def get_dns_zone(zone_uuid: str) -> str:
    """Get a DNS zone with all its records.

    Args:
        zone_uuid: The UUID of the DNS zone
    """
    client = _get_client()
    data = await client._get(f"/dns-zones/{zone_uuid}/")
    if isinstance(data, str):
        return data
    return format_dns_zone(data)


@mcp.tool()
async def create_dns_zone(domain: str) -> str:
    """Create a new DNS zone for a domain.

    Args:
        domain: The domain name (e.g. "example.com")
    """
    client = _get_client()
    data = await client._post("/dns-zones/", json={"domain": domain})
    if isinstance(data, str):
        return data
    return f"DNS zone created.\n\n{format_dns_zone(data)}"


@mcp.tool()
async def delete_dns_zone(zone_uuid: str) -> str:
    """Delete a DNS zone and all its records.

    Args:
        zone_uuid: The UUID of the DNS zone to delete
    """
    client = _get_client()
    data = await client._delete(f"/dns-zones/{zone_uuid}/")
    if isinstance(data, str):
        return data
    return f"DNS zone {zone_uuid} deleted."


@mcp.tool()
async def create_dns_record(
    zone_uuid: str,
    record_type: str,
    name: str,
    value: str,
    ttl: int = 3600,
    priority: int | None = None,
) -> str:
    """Create a DNS record in a zone.

    Args:
        zone_uuid: The UUID of the DNS zone
        record_type: Record type (A, AAAA, CNAME, MX, TXT, NS, SRV, CAA)
        name: Record name (e.g. "@" for root, "www", "mail")
        value: Record value (e.g. IP address, hostname)
        ttl: Time to live in seconds (default 3600)
        priority: Priority for MX/SRV records
    """
    client = _get_client()
    body: dict = {
        "record_type": record_type.upper(),
        "name": name,
        "value": value,
        "ttl": ttl,
    }
    if priority is not None:
        body["priority"] = priority
    data = await client._post(f"/dns-zones/{zone_uuid}/records/", json=body)
    if isinstance(data, str):
        return data
    return f"DNS record created.\n\n{format_generic(data)}"


@mcp.tool()
async def update_dns_record(
    record_uuid: str,
    record_type: str | None = None,
    name: str | None = None,
    value: str | None = None,
    ttl: int | None = None,
    priority: int | None = None,
) -> str:
    """Update an existing DNS record.

    Args:
        record_uuid: The UUID of the DNS record to update
        record_type: New record type
        name: New record name
        value: New record value
        ttl: New TTL in seconds
        priority: New priority (MX/SRV records)
    """
    client = _get_client()
    body: dict = {}
    if record_type is not None:
        body["record_type"] = record_type.upper()
    if name is not None:
        body["name"] = name
    if value is not None:
        body["value"] = value
    if ttl is not None:
        body["ttl"] = ttl
    if priority is not None:
        body["priority"] = priority
    if not body:
        return "No fields to update. Provide at least one of: record_type, name, value, ttl, priority."
    data = await client._patch(f"/dns-records/{record_uuid}/", json=body)
    if isinstance(data, str):
        return data
    return f"DNS record updated.\n\n{format_generic(data)}"


@mcp.tool()
async def delete_dns_record(record_uuid: str) -> str:
    """Delete a DNS record.

    Args:
        record_uuid: The UUID of the DNS record to delete
    """
    client = _get_client()
    data = await client._delete(f"/dns-records/{record_uuid}/")
    if isinstance(data, str):
        return data
    return f"DNS record {record_uuid} deleted."


# ══════════════════════════════════════════════════════════════════════
# Info (3 tools)
# ══════════════════════════════════════════════════════════════════════


@mcp.tool()
async def list_plans() -> str:
    """List all available VPS plans with pricing, specs, and UUIDs."""
    client = _get_client()
    data = await client._get("/plans/")
    return format_paginated(data, format_plan)


@mcp.tool()
async def list_locations() -> str:
    """List all available data center locations."""
    client = _get_client()
    data = await client._get("/locations/")
    return format_paginated(data, format_location)


@mcp.tool()
async def list_operating_systems() -> str:
    """List all available operating systems and their versions."""
    client = _get_client()
    data = await client._get("/operating-systems/")
    return format_paginated(data, format_os)


# ══════════════════════════════════════════════════════════════════════
# SSH Keys (3 tools)
# ══════════════════════════════════════════════════════════════════════


@mcp.tool()
async def list_ssh_keys() -> str:
    """List all SSH keys in your account."""
    client = _get_client()
    data = await client._get("/ssh-keys/")
    return format_paginated(data, format_ssh_key)


@mcp.tool()
async def add_ssh_key(name: str, public_key: str) -> str:
    """Add a new SSH public key to your account.

    Args:
        name: A label for the key (e.g. "work-laptop")
        public_key: The SSH public key (e.g. "ssh-ed25519 AAAA...")
    """
    client = _get_client()
    data = await client._post("/ssh-keys/", json={"name": name, "public_key": public_key})
    if isinstance(data, str):
        return data
    return f"SSH key added.\n\n{format_ssh_key(data)}"


@mcp.tool()
async def delete_ssh_key(ssh_key_uuid: str) -> str:
    """Delete an SSH key from your account.

    Args:
        ssh_key_uuid: The UUID of the SSH key to delete
    """
    client = _get_client()
    data = await client._delete(f"/ssh-keys/{ssh_key_uuid}/")
    if isinstance(data, str):
        return data
    return f"SSH key {ssh_key_uuid} deleted."


# ══════════════════════════════════════════════════════════════════════
# Entry point
# ══════════════════════════════════════════════════════════════════════


def main() -> None:
    """Run the VPS.org MCP server."""
    print("Starting VPS.org MCP server...", file=sys.stderr)
    mcp.run()


if __name__ == "__main__":
    main()
