"""Async HTTP client for the VPS.org REST API."""

from __future__ import annotations

import logging
import sys
from typing import Any

import httpx

logger = logging.getLogger("vps-mcp")
logger.addHandler(logging.StreamHandler(sys.stderr))
logger.setLevel(logging.INFO)

DEFAULT_BASE_URL = "https://admin.vps.org/api/v1"


class VPSClient:
    """Thin async wrapper around the VPS.org admin API."""

    def __init__(self, api_token: str, base_url: str = DEFAULT_BASE_URL) -> None:
        self.base_url = base_url.rstrip("/")
        self._client = httpx.AsyncClient(
            base_url=self.base_url,
            headers={
                "Authorization": f"Bearer {api_token}",
                "Accept": "application/json",
                "Content-Type": "application/json",
            },
            timeout=30.0,
        )

    async def close(self) -> None:
        await self._client.aclose()

    # ── Core request method ──────────────────────────────────────────

    async def _request(
        self,
        method: str,
        path: str,
        **kwargs: Any,
    ) -> dict | list | str:
        """Execute an API request and return parsed JSON or error text."""
        url = path if path.startswith("http") else path
        logger.info("%s %s", method.upper(), url)

        try:
            resp = await self._client.request(method, url, **kwargs)
        except httpx.ConnectError:
            return "Error: Could not connect to VPS.org API. Check your network and VPS_API_URL."
        except httpx.TimeoutException:
            return "Error: Request timed out. The API may be slow — try again."
        except httpx.HTTPError as exc:
            return f"Error: HTTP request failed — {exc}"

        if resp.status_code == 204:
            return {"detail": "Success (no content)"}

        try:
            data = resp.json()
        except ValueError:
            if resp.is_success:
                return {"detail": resp.text or "Success"}
            return f"Error ({resp.status_code}): {resp.text}"

        if not resp.is_success:
            return _format_error(resp.status_code, data)

        return data

    # ── Convenience wrappers ─────────────────────────────────────────

    async def _get(self, path: str, params: dict | None = None) -> Any:
        return await self._request("GET", path, params=params)

    async def _post(self, path: str, json: dict | None = None) -> Any:
        return await self._request("POST", path, json=json)

    async def _put(self, path: str, json: dict | None = None) -> Any:
        return await self._request("PUT", path, json=json)

    async def _patch(self, path: str, json: dict | None = None) -> Any:
        return await self._request("PATCH", path, json=json)

    async def _delete(self, path: str) -> Any:
        return await self._request("DELETE", path)


# ── Response formatting helpers ──────────────────────────────────────


def _format_error(status_code: int, data: Any) -> str:
    """Turn an API error response into a readable string."""
    if isinstance(data, dict):
        detail = data.get("detail")
        if detail:
            return f"Error ({status_code}): {detail}"
        # Field-level validation errors
        parts = []
        for field, msgs in data.items():
            if isinstance(msgs, list):
                parts.append(f"  {field}: {'; '.join(str(m) for m in msgs)}")
            else:
                parts.append(f"  {field}: {msgs}")
        if parts:
            return f"Error ({status_code}):\n" + "\n".join(parts)
    return f"Error ({status_code}): {data}"


def format_paginated(data: Any, item_formatter: Any) -> str:
    """Format a DRF paginated response into readable text."""
    if isinstance(data, str):
        return data  # Already an error string

    if isinstance(data, dict) and "results" in data:
        items = data["results"]
        count = data.get("count", len(items))
        page_size = len(items) if items else 1
        current_page = 1
        if data.get("previous") is None and data.get("next") is not None:
            current_page = 1
        elif data.get("previous") is not None:
            # Rough estimate
            current_page = (count - len(items)) // page_size + 1 if page_size else 1
        total_pages = max(1, -(-count // page_size)) if page_size else 1

        if not items:
            return "No results found."

        lines = [item_formatter(item) for item in items]
        lines.append(f"\nPage {current_page} of {total_pages} ({count} total)")
        return "\n\n".join(lines)

    # Non-paginated list
    if isinstance(data, list):
        if not data:
            return "No results found."
        return "\n\n".join(item_formatter(item) for item in data)

    # Single object
    return item_formatter(data)


def format_server(s: dict) -> str:
    """Format a server object into readable text.

    Handles both list response (flat fields like location_name, plan_name)
    and detail response (nested objects like location, plan).
    """
    lines = [f"Server: {s.get('hostname', 'N/A')}"]
    if s.get("uuid"):
        lines.append(f"  UUID: {s['uuid']}")
    if s.get("status"):
        lines.append(f"  Status: {s['status']}")
    # IPs
    for field in ("ipv4_addresses", "ipv6_addresses"):
        addrs = s.get(field)
        if addrs:
            label = "IPv4" if "v4" in field else "IPv6"
            if isinstance(addrs, list):
                lines.append(f"  {label}: {', '.join(str(a) for a in addrs)}")
            else:
                lines.append(f"  {label}: {addrs}")
    # Location — list view uses location_name, detail view uses nested object
    if s.get("location_name"):
        lines.append(f"  Location: {s['location_name']}")
    elif s.get("location") and isinstance(s["location"], dict):
        lines.append(f"  Location: {s['location'].get('name', 'N/A')}")
    # Plan — list view uses plan_name, detail view uses nested object
    if s.get("plan_name"):
        lines.append(f"  Plan: {s['plan_name']}")
    elif s.get("plan") and isinstance(s["plan"], dict):
        p = s["plan"]
        lines.append(f"  Plan: {p.get('ram', '?')} {p.get('ram_unit', 'GB')} RAM, {p.get('cpu', '?')} vCPUs, {p.get('disk_space', '?')} GB disk")
    # OS — list view uses os_name, detail view uses nested objects
    if s.get("os_name"):
        lines.append(f"  OS: {s['os_name']}")
    elif s.get("operating_system_version") and isinstance(s["operating_system_version"], dict):
        lines.append(f"  OS: {s['operating_system_version'].get('name', 'N/A')}")
    elif s.get("operating_system") and isinstance(s["operating_system"], dict):
        lines.append(f"  OS: {s['operating_system'].get('name', 'N/A')}")
    if s.get("deployed") is not None:
        lines.append(f"  Deployed: {s['deployed']}")
    if s.get("created_at"):
        lines.append(f"  Created: {s['created_at']}")
    # Detail-only: password (shown once after creation)
    if s.get("password"):
        lines.append(f"  Password: {s['password']}")
    return "\n".join(lines)


def format_domain(d: dict) -> str:
    """Format a domain object into readable text."""
    lines = [f"Domain: {d.get('domain_name', 'N/A')}"]
    if d.get("uuid"):
        lines.append(f"  UUID: {d['uuid']}")
    if d.get("status"):
        lines.append(f"  Status: {d['status']}")
    if d.get("registrar"):
        lines.append(f"  Registrar: {d['registrar']}")
    if d.get("expiration_date"):
        lines.append(f"  Expires: {d['expiration_date']}")
    if d.get("days_until_expiration") is not None:
        lines.append(f"  Days until expiration: {d['days_until_expiration']}")
    if d.get("auto_renew") is not None:
        lines.append(f"  Auto-renew: {d['auto_renew']}")
    # Nameservers as individual fields
    ns = [d.get(f"nameserver{i}") for i in range(1, 5)]
    ns = [n for n in ns if n]
    if ns:
        lines.append(f"  Nameservers: {', '.join(ns)}")
    if d.get("whois_privacy") is not None:
        lines.append(f"  WHOIS Privacy: {d['whois_privacy']}")
    if d.get("transfer_lock") is not None:
        lines.append(f"  Transfer Lock: {d['transfer_lock']}")
    return "\n".join(lines)


def format_dns_zone(z: dict) -> str:
    """Format a DNS zone into readable text."""
    lines = [f"DNS Zone: {z.get('domain', 'N/A')}"]
    if z.get("uuid"):
        lines.append(f"  UUID: {z['uuid']}")
    if z.get("record_count") is not None:
        lines.append(f"  Record count: {z['record_count']}")
    if z.get("created_at"):
        lines.append(f"  Created: {z['created_at']}")
    records = z.get("records", [])
    if records:
        lines.append(f"  Records ({len(records)}):")
        for r in records:
            rtype = r.get("record_type", "?")
            name = r.get("name", "@")
            value = r.get("value", "")
            ttl = r.get("ttl", "")
            priority = r.get("priority")
            pri_str = f" (priority: {priority})" if priority is not None else ""
            lines.append(f"    {rtype:6} {name:30} -> {value}{pri_str}  TTL: {ttl}")
            if r.get("uuid"):
                lines.append(f"           Record UUID: {r['uuid']}")
    return "\n".join(lines)


def format_plan(p: dict) -> str:
    """Format a plan into readable text."""
    ram = p.get("ram", "?")
    ram_unit = p.get("ram_unit", "GB")
    label = f"{ram} {ram_unit}"
    lines = [f"Plan: {label}"]
    if p.get("uuid"):
        lines.append(f"  UUID: {p['uuid']}")
    if p.get("cpu"):
        cpu_name = p.get("cpu_type_name", "")
        cpu_str = f"{p['cpu']} vCPUs"
        if cpu_name:
            cpu_str += f" ({cpu_name})"
        lines.append(f"  CPU: {cpu_str}")
    lines.append(f"  RAM: {ram} {ram_unit}")
    if p.get("disk_space"):
        lines.append(f"  Disk: {p['disk_space']} GB")
    if p.get("transfer"):
        lines.append(f"  Transfer: {p['transfer']} GB")
    if p.get("monthly_price"):
        lines.append(f"  Price: ${p['monthly_price']}/mo")
    elif p.get("price"):
        lines.append(f"  Price: ${p['price']}/mo")
    if p.get("hourly_price"):
        lines.append(f"  Hourly: ${p['hourly_price']}/hr")
    return "\n".join(lines)


def format_location(loc: dict) -> str:
    """Format a location into readable text."""
    parts = [f"Location: {loc.get('name', 'N/A')}"]
    if loc.get("id"):
        parts.append(f"  ID: {loc['id']}")
    if loc.get("country_name"):
        parts.append(f"  Country: {loc['country_name']} ({loc.get('country_code', '')})")
    if loc.get("is_active") is not None:
        parts.append(f"  Active: {loc['is_active']}")
    return "\n".join(parts)


def format_os(os_obj: dict) -> str:
    """Format an OS into readable text."""
    lines = [f"OS: {os_obj.get('name', 'N/A')}"]
    if os_obj.get("uuid"):
        lines.append(f"  UUID: {os_obj['uuid']}")
    versions = os_obj.get("versions", [])
    if versions:
        for v in versions:
            if isinstance(v, dict):
                lines.append(f"  Version: {v.get('name', '?')} (UUID: {v.get('uuid', '?')})")
            else:
                lines.append(f"  Version: {v}")
    return "\n".join(lines)


def format_ssh_key(k: dict) -> str:
    """Format an SSH key into readable text."""
    lines = [f"SSH Key: {k.get('name', 'N/A')}"]
    if k.get("uuid"):
        lines.append(f"  UUID: {k['uuid']}")
    if k.get("fingerprint"):
        lines.append(f"  Fingerprint: {k['fingerprint']}")
    if k.get("created_at"):
        lines.append(f"  Created: {k['created_at']}")
    if k.get("last_used_at"):
        lines.append(f"  Last used: {k['last_used_at']}")
    return "\n".join(lines)


def format_generic(obj: dict) -> str:
    """Fallback formatter for unknown response shapes."""
    if isinstance(obj, str):
        return obj
    lines = []
    for key, val in obj.items():
        lines.append(f"  {key}: {val}")
    return "\n".join(lines)
