
from typing import Generator, Optional
from .handlers.base_handler import BaseHandler
from ..transport.request import Request
from ..transport.response import Response


class RequestChain:
    """
    Class representing a chain of request handlers.
    Handlers are added to the chain and the request is passed through each handler in the order they were added.

    :ivar Optional[BaseHandler] _head: The first handler in the chain.
    :ivar Optional[BaseHandler] _tail: The last handler in the chain.
    """

    def __init__(self):
        """
        Initialize a new instance of RequestChain.
        """
        self._head: Optional[BaseHandler] = None
        self._tail: Optional[BaseHandler] = None

    def add_handler(self, handler: BaseHandler) -> "RequestChain":
        """
        Add a handler to the chain.

        :param BaseHandler handler: The handler to add.
        :return: The current instance of RequestChain to allow for method chaining.
        :rtype: RequestChain
        """
        if self._head is None:
            self._head = handler
            self._tail = handler
        else:
            self._tail.set_next(handler)
            self._tail = handler

        return self

    def send(self, request: Request) -> Response:
        """
        Send the request through the chain of handlers.

        :param Request request: The request to send.
        :return: The response from the request.
        :rtype: Response
        :raises RuntimeError: If the RequestChain is empty.
        """
        if self._head is not None:
            response, error = self._head.handle(request)

            if error is not None:
                raise error

            return response
        else:
            raise RuntimeError("RequestChain is empty")

    def stream(self, request: Request) -> Generator[Response, None, None]:
        """
        Send the request through the chain of handlers.

        :param Request request: The request to send.
        :return: The response from the request.
        :rtype: Generator[Response, None, None]
        :raises RuntimeError: If the RequestChain is empty.
        """
        if self._head is not None:
            stream = self._head.stream(request)
            for response, error in stream:
                if error is not None:
                    raise error

                yield response
        else:
            raise RuntimeError("RequestChain is empty")
