"""HTTP client for the NetScope API — used by MCP tool handlers.

All methods are synchronous (MCP tool handlers run via asyncio.to_thread).
Authentication is HTTP Basic Auth. TLS verification is configurable for
self-signed certificates common in scanner deployments.

Environment variables:
    NETSCOPE_URL          Base URL (e.g. https://scanner.corp:8443)  [required]
    NETSCOPE_USER         Basic auth username (default: admin)
    NETSCOPE_PASS         Basic auth password                        [required]
    NETSCOPE_VERIFY_TLS   Set to 'false' to skip TLS verification
"""

from __future__ import annotations

import logging
import os
from typing import Any
from urllib.parse import quote

import httpx

from netscope_mcp import __version__

logger = logging.getLogger("netscope-mcp")

USER_AGENT = f"netscope-mcp/{__version__}"
TIMEOUT = httpx.Timeout(connect=10.0, read=30.0, write=10.0, pool=10.0)


class APIError(Exception):
    """Raised on non-2xx responses or network errors."""

    def __init__(self, status_code: int, message: str):
        self.status_code = status_code
        self.message = message
        super().__init__(message)


def _get_base_url() -> str:
    url = os.environ.get("NETSCOPE_URL", "").rstrip("/")
    if not url:
        raise APIError(0, "NETSCOPE_URL environment variable is required")
    return url


def _get_auth() -> tuple[str, str] | None:
    user = os.environ.get("NETSCOPE_USER", "admin")
    password = os.environ.get("NETSCOPE_PASS", "")
    if not password:
        return None
    return (user, password)


def _verify_tls() -> bool:
    return os.environ.get("NETSCOPE_VERIFY_TLS", "true").lower() not in ("false", "0", "no")


def _safe_path_segment(segment: str) -> str:
    """Percent-encode a single URL path segment."""
    return quote(segment, safe="")


def _handle(resp: httpx.Response) -> dict[str, Any]:
    """Process response, raising APIError on failure."""
    if resp.status_code == 404:
        raise APIError(404, "Not found")
    if resp.status_code == 401:
        raise APIError(401, "Authentication failed — check NETSCOPE_USER and NETSCOPE_PASS")
    if resp.status_code == 429:
        retry = resp.headers.get("Retry-After", "60")
        raise APIError(429, f"Rate limited. Retry after {retry}s.")
    if resp.status_code >= 400:
        raise APIError(resp.status_code, f"HTTP {resp.status_code}")
    try:
        return resp.json()
    except Exception:
        raise APIError(resp.status_code, "Invalid JSON response from API")


def _request_json(
    path: str,
    *,
    params: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Perform an authenticated GET request and return decoded JSON."""
    base = _get_base_url()
    url = f"{base}{path}"
    auth = _get_auth()
    verify = _verify_tls()

    with httpx.Client(
        timeout=TIMEOUT,
        headers={"User-Agent": USER_AGENT, "Accept": "application/json"},
        auth=auth,
        verify=verify,
        follow_redirects=True,
        max_redirects=3,
    ) as client:
        try:
            resp = client.get(url, params=params)
        except httpx.RequestError as exc:
            raise APIError(0, f"Network error: {exc.__class__.__name__}")

    return _handle(resp)


# ---------------------------------------------------------------------------
# API methods — one per endpoint
# ---------------------------------------------------------------------------

def search_services(params: dict[str, Any]) -> dict[str, Any]:
    clean = {k: v for k, v in params.items() if v is not None}
    return _request_json("/api/search/services", params=clean)


def search_hosts(params: dict[str, Any]) -> dict[str, Any]:
    clean = {k: v for k, v in params.items() if v is not None}
    return _request_json("/api/search/hosts", params=clean)


def get_facets(params: dict[str, Any]) -> dict[str, Any]:
    clean = {k: v for k, v in params.items() if v is not None}
    return _request_json("/api/search/facets", params=clean)


def get_service_detail(service_id: int) -> dict[str, Any]:
    return _request_json(f"/api/services/{service_id}")


def get_host_detail(ip: str) -> dict[str, Any]:
    ip_seg = _safe_path_segment(ip)
    return _request_json(f"/api/hosts/{ip_seg}")


def get_dashboard() -> dict[str, Any]:
    return _request_json("/api/dashboard")


def get_findings(params: dict[str, Any]) -> dict[str, Any]:
    clean = {k: v for k, v in params.items() if v is not None}
    return _request_json("/api/findings", params=clean)


def get_webapps(params: dict[str, Any]) -> dict[str, Any]:
    clean = {k: v for k, v in params.items() if v is not None}
    return _request_json("/api/webapps", params=clean)


def get_ports(params: dict[str, Any]) -> dict[str, Any]:
    clean = {k: v for k, v in params.items() if v is not None}
    return _request_json("/api/ports", params=clean)


def get_explore(category: str) -> dict[str, Any]:
    cat_seg = _safe_path_segment(category)
    return _request_json(f"/api/explore/{cat_seg}")


def get_drones() -> dict[str, Any]:
    return _request_json("/api/drones")


def search_templates(params: dict[str, Any]) -> dict[str, Any]:
    clean = {k: v for k, v in params.items() if v is not None}
    return _request_json("/api/templates", params=clean)


def get_gallery(params: dict[str, Any]) -> dict[str, Any]:
    clean = {k: v for k, v in params.items() if v is not None}
    return _request_json("/api/gallery", params=clean)


def check_health() -> dict[str, Any]:
    return _request_json("/api/health")
