"""HTTP transports (sync + async) with runtime middleware.

This module defines two HTTP execution adapters used by the SDK's Direct Access (DA) path:

- `HttpClient`: sync transport built on `requests`
- `AsyncHttpClient`: async transport built on `aiohttp`

Both transports:
- accept an underlying session object (hidden from `repr`)
- route requests through the shared runtime middleware pipeline
- use `operation` + `meta` to feed logging/metrics/retry/error mapping consistently
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Awaitable, TYPE_CHECKING, TypeAlias

from relationalai.runtime.execution import Executor, HttpRequest
from relationalai.runtime.pipeline import build_executor

from ..errors.handlers import ErrorHandlerChain, ExecContext

if TYPE_CHECKING:
    import requests
    from aiohttp import ClientSession as _ClientSession

    ClientSession: TypeAlias = _ClientSession
    HttpResponse: TypeAlias = requests.Response
else:
    ClientSession: TypeAlias = Any
    HttpResponse: TypeAlias = Any


@dataclass
class HttpClient:
    """HTTP executor (requests-backed) with middleware pipeline.

    This wraps `requests.Session.request(...)` behind a stable interface (`request(...)`)
    that emits runtime `HttpRequest` objects and applies middleware consistently.
    """

    # Session may contain auth headers/cookies; never show in `repr`.
    session: "requests.Session" = field(repr=False)
    middlewares: list[Any] | None = None
    error_handlers: ErrorHandlerChain | None = None
    execution_metrics: Any | None = None

    def __post_init__(self) -> None:
        session = self.session

        class _Base(Executor[HttpRequest, HttpResponse]):
            def execute(_self: object, req: HttpRequest) -> HttpResponse:
                return session.request(req.method, req.url, json=req.json, headers=req.headers)

        def _ctx_factory(req: HttpRequest) -> ExecContext:
            return ExecContext(statement=req.url, params=None, operation=req.operation, meta=req.meta)

        self._executor = build_executor(
            base=_Base(),
            middlewares=self.middlewares,
            error_handlers=self.error_handlers or ErrorHandlerChain(),
            ctx_factory=_ctx_factory,
        )

    def request(
        self,
        *,
        operation: str,
        method: str,
        url: str,
        payload: dict[str, Any] | None = None,
        headers: dict[str, str] | None = None,
        meta: dict[str, Any] | None = None,
    ) -> HttpResponse:
        """Perform an HTTP request and return the underlying response object.

        `payload` is encoded as JSON and passed as `json=...` to requests.
        """
        req = HttpRequest(operation=operation, method=method, url=url, json=payload, headers=headers, meta=meta or {})
        return self._executor.execute(req)


@dataclass
class AsyncHttpClient:
    """Async HTTP executor (aiohttp-backed) with middleware pipeline.

    This wraps `aiohttp.ClientSession.request(...)` and normalizes responses to
    `(status, content)` where `content` is JSON-decoded when possible.
    """

    # Session may contain auth headers/cookies; never show in `repr`.
    session: ClientSession = field(repr=False)
    middlewares: list[Any] | None = None
    error_handlers: ErrorHandlerChain | None = None
    execution_metrics: Any | None = None

    def __post_init__(self) -> None:
        session = self.session

        class _Base(Executor[HttpRequest, Awaitable[tuple[int, Any]]]):
            async def execute(_self: object, req: HttpRequest) -> tuple[int, Any]:
                async with session.request(req.method, req.url, json=req.json, headers=req.headers) as resp:
                    try:
                        return int(resp.status), await resp.json(content_type=None)
                    except Exception:
                        return int(resp.status), await resp.text(errors="replace")

        def _ctx_factory(req: HttpRequest) -> ExecContext:
            return ExecContext(statement=req.url, params=None, operation=req.operation, meta=req.meta)

        self._executor = build_executor(
            base=_Base(),
            middlewares=self.middlewares,
            error_handlers=self.error_handlers or ErrorHandlerChain(),
            ctx_factory=_ctx_factory,
        )

    async def request(
        self,
        *,
        operation: str,
        method: str,
        url: str,
        payload: dict[str, Any] | None = None,
        headers: dict[str, str] | None = None,
        meta: dict[str, Any] | None = None,
    ) -> tuple[int, Any]:
        """Perform an HTTP request asynchronously and return `(status, content)`.

        - `content` is JSON-decoded when possible; otherwise a decoded text body.
        """
        req = HttpRequest(
            operation=operation,
            method=method,
            url=url,
            json=payload,
            headers=headers,
            meta=meta or {},
        )
        return await self._executor.execute(req)

