"""NATIVE handler: system.http_request — Make HTTP requests from customer network.

This is a key qRemoteX use case: make HTTP calls from within the customer's
VPC/network, reaching internal APIs that the hosted qRunX cannot access.

Architecture reference: Section 12.2 — Tool Categories & Execution Matrix.

Security:
  - SSRF protection: private/loopback IPs blocked by default (configurable)
  - Timeout enforcement on all requests
  - No credentials logged
  - Response body size limited
"""

from __future__ import annotations

import ipaddress
import logging
from typing import Any
from urllib.parse import urlparse

import aiohttp

logger = logging.getLogger("qremotex.tools.http_request")

# Maximum response body size (5 MB)
MAX_RESPONSE_BYTES = 5 * 1024 * 1024


async def handle_http_request(
    tool_params: dict[str, Any],
    context: dict[str, Any],
) -> dict[str, Any]:
    """Execute an HTTP request.

    Expected tool_params:
        url (str): Target URL (required)
        method (str): HTTP method (default: GET)
        headers (dict): Request headers (optional)
        body (dict|str): Request body (optional)
        timeout (int): Request timeout in seconds (default: 30)
        allow_private_ips (bool): Allow requests to private/loopback IPs (default: False)

    Returns:
        dict with keys: status_code (int), body (str|dict), headers (dict)
    """
    url = tool_params.get("url")
    if not url:
        return {"error": "Missing required parameter: url", "status_code": 0}

    method = tool_params.get("method", "GET").upper()
    headers = tool_params.get("headers", {})
    body = tool_params.get("body")
    timeout_sec = min(int(tool_params.get("timeout", 30)), 300)
    allow_private = tool_params.get("allow_private_ips", False)

    # Validate method
    valid_methods = {"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}
    if method not in valid_methods:
        return {"error": f"Unsupported HTTP method: {method}", "status_code": 0}

    # SSRF protection: block private IPs unless explicitly allowed
    if not allow_private:
        ssrf_error = _check_ssrf(url)
        if ssrf_error:
            return {"error": ssrf_error, "status_code": 0}

    try:
        timeout = aiohttp.ClientTimeout(total=timeout_sec)
        async with aiohttp.ClientSession(timeout=timeout) as session:
            kwargs: dict[str, Any] = {"headers": headers}

            if body is not None and method in ("POST", "PUT", "PATCH"):
                if isinstance(body, dict):
                    kwargs["json"] = body
                else:
                    kwargs["data"] = str(body)

            async with session.request(method, url, **kwargs) as resp:
                # Read response with size limit
                raw_body = await resp.read()
                if len(raw_body) > MAX_RESPONSE_BYTES:
                    return {
                        "error": f"Response body exceeds limit ({MAX_RESPONSE_BYTES} bytes)",
                        "status_code": resp.status,
                        "headers": dict(resp.headers),
                    }

                content_type = resp.headers.get("content-type", "")

                if "application/json" in content_type:
                    try:
                        parsed_body = await resp.json(content_type=None)
                    except Exception:
                        parsed_body = raw_body.decode("utf-8", errors="replace")
                else:
                    parsed_body = raw_body.decode("utf-8", errors="replace")

                return {
                    "status_code": resp.status,
                    "body": parsed_body,
                    "headers": {k: v for k, v in resp.headers.items()},
                }

    except aiohttp.ClientError as e:
        logger.error("HTTP request failed: %s", type(e).__name__)
        return {
            "error": f"HTTP request failed: {type(e).__name__}: {e}",
            "status_code": 0,
        }
    except Exception as e:
        logger.error("HTTP request error: %s", type(e).__name__)
        return {
            "error": f"HTTP request error: {type(e).__name__}: {e}",
            "status_code": 0,
        }


def _check_ssrf(url: str) -> str | None:
    """Check URL for SSRF risks — block private/loopback IPs.

    Returns error message if blocked, None if safe.
    """
    try:
        parsed = urlparse(url)
        hostname = parsed.hostname
        if not hostname:
            return "Invalid URL: no hostname"

        # Try to resolve as IP
        try:
            ip = ipaddress.ip_address(hostname)
            if ip.is_private or ip.is_loopback or ip.is_reserved or ip.is_link_local:
                return (
                    f"SSRF protection: requests to private/loopback IP {hostname} are blocked. "
                    f"Set allow_private_ips=true to override."
                )
        except ValueError:
            # Not a raw IP — hostname like "internal.corp". Allow DNS names
            # since on customer network they may legitimately resolve to private IPs.
            pass

        # Block known dangerous schemes
        if parsed.scheme not in ("http", "https"):
            return f"Unsupported URL scheme: {parsed.scheme}. Only http/https allowed."

        return None
    except Exception as e:
        return f"URL validation error: {e}"
