"""HTTP request tool: make arbitrary REST API calls."""

from __future__ import annotations

import json
import time
from typing import Any
from urllib.parse import urlparse

import httpx
from loguru import logger

from madpod_agent.agent.tools.base import Tool

_MAX_RESPONSE_CHARS = 100_000
_DEFAULT_TIMEOUT = 30.0
_MAX_TIMEOUT = 120.0
_USER_AGENT = "madpod-agent/1.0"
_MAX_BODY_SIZE = 10 * 1024 * 1024  # 10 MB


class HTTPRequestTool(Tool):
    """Make HTTP requests to REST APIs and web services."""

    def __init__(self, proxy: str | None = None):
        self._proxy = proxy

    @property
    def name(self) -> str:
        return "http_request"

    @property
    def description(self) -> str:
        return (
            "Make an HTTP request to a URL (GET, POST, PUT, PATCH, DELETE). "
            "Use for calling REST APIs, webhooks, health checks, and data submission. "
            "Returns status code, headers, and response body."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "url": {"type": "string", "description": "The URL to request"},
                "method": {
                    "type": "string",
                    "enum": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
                    "description": "HTTP method (default: GET)",
                },
                "headers": {
                    "type": "object",
                    "description": "Request headers as key-value pairs",
                },
                "body": {
                    "type": "object",
                    "description": "JSON request body (for POST/PUT/PATCH)",
                },
                "body_raw": {
                    "type": "string",
                    "description": "Raw string body (use instead of body for non-JSON)",
                },
                "query_params": {
                    "type": "object",
                    "description": "URL query parameters as key-value pairs",
                },
                "timeout": {
                    "type": "number",
                    "description": "Request timeout in seconds (default: 30, max: 120)",
                },
                "follow_redirects": {
                    "type": "boolean",
                    "description": "Follow HTTP redirects (default: true)",
                },
            },
            "required": ["url"],
        }

    async def execute(
        self,
        url: str,
        method: str = "GET",
        headers: dict[str, str] | None = None,
        body: dict[str, Any] | None = None,
        body_raw: str | None = None,
        query_params: dict[str, str] | None = None,
        timeout: float | None = None,
        follow_redirects: bool = True,
        **kwargs: Any,
    ) -> str:
        parsed = urlparse(url)
        if parsed.scheme not in ("http", "https"):
            return json.dumps({"error": f"Only http/https URLs supported, got '{parsed.scheme}'"})

        method = method.upper()
        if method not in {"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}:
            return json.dumps({"error": f"Unsupported method: {method}"})

        resolved_timeout = min(timeout or _DEFAULT_TIMEOUT, _MAX_TIMEOUT)

        req_headers = {"User-Agent": _USER_AGENT}
        if headers:
            req_headers.update(headers)

        try:
            start = time.monotonic()
            async with httpx.AsyncClient(
                follow_redirects=follow_redirects,
                timeout=resolved_timeout,
                proxy=self._proxy,
            ) as client:
                req_kwargs: dict[str, Any] = {
                    "method": method,
                    "url": url,
                    "headers": req_headers,
                }
                if query_params:
                    req_kwargs["params"] = query_params
                if body is not None:
                    req_kwargs["json"] = body
                elif body_raw is not None:
                    if len(body_raw) > _MAX_BODY_SIZE:
                        return json.dumps({"error": f"Body too large: {len(body_raw):,} chars (max {_MAX_BODY_SIZE:,})"})
                    req_kwargs["content"] = body_raw

                response = await client.request(**req_kwargs)

            elapsed = time.monotonic() - start

            response_text = response.text
            truncated = False
            if len(response_text) > _MAX_RESPONSE_CHARS:
                response_text = response_text[:_MAX_RESPONSE_CHARS]
                truncated = True

            content_type = response.headers.get("content-type", "").split(";")[0].strip()

            # Try to parse JSON response
            response_body: Any = response_text
            is_json = False
            if "json" in content_type:
                try:
                    response_body = response.json()
                    is_json = True
                except Exception:
                    pass  # Keep as text

            result: dict[str, Any] = {
                "ok": 200 <= response.status_code < 400,
                "status_code": response.status_code,
                "content_type": content_type,
                "elapsed_seconds": round(elapsed, 3),
                "body": response_body,
                "is_json": is_json,
            }
            if truncated:
                result["truncated"] = True
                result["original_size"] = len(response.text)

            # Include selected response headers
            resp_headers = {}
            for key in ("content-type", "content-length", "location", "set-cookie",
                        "x-request-id", "retry-after", "www-authenticate"):
                if key in response.headers:
                    resp_headers[key] = response.headers[key]
            if resp_headers:
                result["response_headers"] = resp_headers

            logger.info("HTTP {} {} -> {} ({:.1f}s)", method, url, response.status_code, elapsed)
            return json.dumps(result, default=str)

        except httpx.TimeoutException:
            return json.dumps({"error": f"Request timed out after {resolved_timeout}s", "url": url})
        except httpx.ConnectError as exc:
            return json.dumps({"error": f"Connection failed: {exc}", "url": url})
        except Exception as exc:
            logger.error("HTTP request failed for {} {}: {}", method, url, exc)
            return json.dumps({"error": str(exc), "url": url})
