"""``Transport`` ABC — the I/O seam between :class:`Suite` and the network.

See: ``DESIGN.md`` §14 (``transport/`` directory) and ``HANDOFF.md``
§3.5 (layering — core consumes Transport via dependency injection;
the concrete httpx implementation lives in ``mgf.apiprobe.transport``
so ``core`` stays I/O-free).

A :class:`Transport` takes a probe + run context, applies the
probe's auth flow, sends the request, and returns a typed
:class:`mgf.apiprobe.core.response.Response`. Callers (:class:`Suite`)
don't know whether the call hit a real network, a recorded cassette,
or an in-process stub — that's the closed-box win.

Implementations:

  - :class:`mgf.apiprobe.transport.HttpxTransport` (Phase 1.6b)
  - Test stubs (subclass with overridden :meth:`send`)
  - Cassette-replay transport (v0.3)
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from mgf.apiprobe.core.context import Context
    from mgf.apiprobe.core.probe import Probe
    from mgf.apiprobe.core.response import Response


class Transport(ABC):
    """Abstract transport.

    Concrete transports MUST raise
    :class:`mgf.apiprobe.exceptions.TransportError` for any I/O-level
    failure (DNS, TLS, connection refused, timeout). Provider-side
    issues (4xx, 5xx, schema mismatch) are NOT transport errors —
    they produce a real :class:`Response` and surface as Findings via
    the suite's check chain.
    """

    @abstractmethod
    async def send(self, probe: Probe, ctx: Context) -> Response:
        """Send ``probe``, return the typed :class:`Response`."""

    async def aclose(self) -> None:
        """Release transport-level resources (connection pools, etc.).

        Default is a no-op for stateless transports. Concrete impls
        override when they hold long-lived state. Safe to call
        multiple times.
        """
        return


__all__ = ["Transport"]
