from dataclasses import dataclass
from typing import Type, Optional, Dict, Any, Callable, List

@dataclass(frozen=True)
class Transport:
    """
    Defines the contract for a transport layer.

    Attributes:
        name: The name of the library (e.g., "requests").
        response_type: The base class for a successful response object.
        exception_type: The base exception class for network/request errors.
    """

    name: str
    response_type: Type
    exception_type: Type

class TransportRegistry:
    """
    A registry for managing and retrieving HTTP transports.
    This allows for lazy loading and custom registration.
    """
    def __init__(self):
        # Stores Transport instances
        self._transports: Dict[str, Transport] = {}
        # Stores callables that can create Transport instances
        self._transport_factories: Dict[str, Callable[[], Optional[Transport]]] = {}
        # Ordered list of transport names to try for default
        self._default_order: List[str] = []

    def register(self, name: str, factory: Callable[[], Optional[Transport]], default_order_priority: int = 100):
        """
        Registers a transport factory.

        Args:
            name: The unique name of the transport (e.g., "requests").
            factory: A callable (function) that attempts to create and return
                     a Transport instance. It should return None if the
                     underlying library is not installed.
            default_order_priority: A number indicating preference for default.
                                    Lower numbers are preferred.
        """
        if name in self._transport_factories:
            raise ValueError(f"Transport '{name}' already registered.")
        self._transport_factories[name] = factory
        # Insert into default order list based on priority
        # Maintain sorted order by priority
        new_entry = (default_order_priority, name)
        for i, (p, n) in enumerate(self._default_order):
            if default_order_priority < p:
                self._default_order.insert(i, new_entry)
                break
        else:
            self._default_order.append(new_entry)
        self._default_order.sort() # Ensure correct sorting

    def get(self, name: str) -> Optional[Transport]:
        """
        Retrieves a registered transport by name.
        The transport is created (if not already) via its factory upon first request.
        """
        if name not in self._transport_factories:
            return None
        if name not in self._transports:
            # Lazily create and cache the transport instance
            transport = self._transport_factories[name]()
            if transport:
                self._transports[name] = transport
            return transport
        return self._transports[name]

    def get_default(self) -> Transport:
        """
        Returns the first available transport based on the default order.
        Raises RuntimeError if no supported transport is installed.
        """
        for _, name in self._default_order:
            transport = self.get(name)
            if transport:
                return transport
        raise RuntimeError(
            "No supported HTTP transport found. "
            "Please install 'requests', 'httpx', or 'niquests'."
        )

transport_registry = TransportRegistry()

def _create_requests_transport() -> Optional[Transport]:
    try:
        import requests
        return Transport(
            name="requests",
            response_type=requests.Response,
            exception_type=requests.exceptions.RequestException,
        )
    except ImportError:
        return None

def _create_httpx_transport() -> Optional[Transport]:
    try:
        import httpx
        return Transport(
            name="httpx",
            response_type=httpx.Response,
            exception_type=httpx.RequestError,
        )
    except ImportError:
        return None

def _create_niquests_transport() -> Optional[Transport]:
    try:
        import niquests
        return Transport(
            name="niquests",
            response_type=niquests.Response,
            exception_type=niquests.exceptions.RequestException,
        )
    except ImportError:
        return None

# Register the default transports with their preferred order
transport_registry.register("requests", _create_requests_transport, default_order_priority=10)
transport_registry.register("httpx", _create_httpx_transport, default_order_priority=20)
transport_registry.register("niquests", _create_niquests_transport, default_order_priority=30) # Least preferred by default

# Expose common transports for easy import and direct usage
# These will now get them from the registry.
REQUESTS_TRANSPORT = transport_registry.get("requests")
HTTPX_TRANSPORT = transport_registry.get("httpx")
NIQUESTS_TRANSPORT = transport_registry.get("niquests")

# Export the registry itself and the Transport type
__all__ = [
    "Transport",
    "transport_registry",
    "REQUESTS_TRANSPORT",
    "HTTPX_TRANSPORT",
    "NIQUESTS_TRANSPORT",
]
