"""A2A server-mode HTTP transport for remote agent communication."""

from __future__ import annotations

import json
from http.client import HTTPResponse
from ssl import CERT_NONE, SSLContext, create_default_context
from urllib.error import URLError
from urllib.request import Request, urlopen

import structlog

from cleveragents.a2a.errors import A2aError
from cleveragents.a2a.models import (
    A2aErrorDetail,
    A2aRequest,
    A2aResponse,
)

logger: structlog.stdlib.BoundLogger = structlog.get_logger(__name__)


class A2aHttpTransport:
    """HTTP transport for server-mode A2A communication."""

    def __init__(
        self,
        tls_verify: bool = True,
        auth_token_ref: str | None = None,
    ) -> None:
        self._base_url: str = ""
        self._tls_verify: bool = tls_verify
        self._is_connected: bool = False
        self._auth_token_ref: str | None = auth_token_ref

    def connect(self, base_url: str) -> None:
        if not base_url or not isinstance(base_url, str):
            raise ValueError("base_url must be a non-empty string")
        stripped = base_url.strip()
        if not stripped.startswith(("http://", "https://")):
            raise ValueError("base_url must start with http:// or https://")
        self._base_url = stripped.rstrip("/") + "/"
        self._is_connected = True
        if stripped.startswith("https://"):
            logger.info(
                "a2a.http.connect",
                base_url=self._base_url,
                tls_verify=self._tls_verify,
            )
        else:
            logger.warning(
                "a2a.http.connect",
                base_url=self._base_url,
                warning="connecting over plain HTTP",
            )

    def disconnect(self) -> None:
        self._is_connected = False
        logger.info("a2a.http.disconnect", base_url=self._base_url)

    def is_connected(self) -> bool:
        return self._is_connected

    def send(self, request: A2aRequest) -> A2aResponse:
        if not isinstance(request, A2aRequest):
            raise TypeError("request must be an A2aRequest instance")
        if not self._is_connected:
            raise RuntimeError("Not connected to server. Call connect() before send().")

        request_dict = request.model_dump(exclude_none=True)
        payload = json.dumps(request_dict).encode("utf-8")

        try:
            http_response = self._do_request(payload, request)
            return self._parse_response(http_response, request)
        except A2aError:
            raise
        except (ValueError, RuntimeError) as exc:
            logger.error("a2a.http.send", method=request.method, error=str(exc))
            raise
        except URLError as exc:
            reason = str(exc.reason) if hasattr(exc, "reason") else str(exc)
            logger.error(
                "a2a.http.send_url_error",
                method=request.method,
                reason=reason,
            )
            raise A2aError(f"Network error connecting to server: {exc}") from exc
        except Exception as exc:
            logger.error("a2a.http.send_error", method=request.method, error=str(exc))
            raise

    def _get_ssl_context(self) -> SSLContext | None:
        if not self._base_url.startswith("https://"):
            return None
        if self._tls_verify:
            return create_default_context()
        context = SSLContext()
        context.check_hostname = False
        context.verify_mode = CERT_NONE
        return context

    def _build_headers(self) -> dict[str, str]:
        headers: dict[str, str] = {
            "Content-Type": "application/json",
            "Accept": "application/json",
        }
        if self._auth_token_ref:
            headers["Authorization"] = f"Bearer {self._auth_token_ref}"
        return headers

    def _do_request(self, payload: bytes, request: A2aRequest) -> HTTPResponse:
        url = self._base_url + "_rpc"
        req = Request(
            url,
            data=payload,
            headers=self._build_headers(),
            method="POST",
        )
        ssl_ctx = self._get_ssl_context()
        return urlopen(req, context=ssl_ctx if ssl_ctx else None, timeout=30)

    def _parse_response(
        self,
        resp: HTTPResponse,
        request: A2aRequest,
    ) -> A2aResponse:
        status_code = resp.status
        body_str = resp.read().decode("utf-8")
        logger.debug(
            "a2a.http.receive",
            method=request.method,
            request_id=request.id,
            status=status_code,
        )

        if status_code >= 400:
            return self._handle_http_status(body_str, request, status_code)

        try:
            response_dict = json.loads(body_str)
        except json.JSONDecodeError as exc:
            raise A2aError(f"Invalid JSON response from server: {exc}") from exc

        parsed_jsonrpc: str = response_dict.get("jsonrpc", "")
        if parsed_jsonrpc != "2.0":
            raise A2aError(f"Expected JSON-RPC 2.0, got version {parsed_jsonrpc!r}")

        try:
            return A2aResponse(**response_dict)
        except Exception as exc:
            raise A2aError(f"Failed to parse response: {exc}") from exc

    def _handle_http_status(
        self,
        body_str: str,
        request: A2aRequest,
        status_code: int,
    ) -> A2aResponse:
        if 400 <= status_code <= 499:
            error_code = -32000 + (status_code - 400)
        else:
            error_code = -32603

        error_message = f"HTTP {status_code}"
        try:
            response_dict = json.loads(body_str)
            if "jsonrpc" in response_dict and "error" in response_dict:
                error_code = response_dict["error"].get("code", error_code)
                error_message = response_dict["error"].get(
                    "message",
                    f"HTTP {status_code}",
                )
        except json.JSONDecodeError:
            pass

        detail = A2aErrorDetail(code=error_code, message=error_message)
        return A2aResponse(id=request.id, error=detail)


__all__ = ["A2aHttpTransport"]
