"""Helpers and types related to waiting for a command response."""

from __future__ import annotations

from collections.abc import Callable
import logging
import threading
from typing import Protocol

from .error import RequestFailed, RequestTimeout

_LOGGER = logging.getLogger(__name__)

CallbackType = Callable[[bool, dict | None], None]
"""Signature of optional callback functions supported by methods sending messages.

The callback function will be called with a bool indicating if the message was sent
and an optional response dict.
"""


class AcceptsCallbackFunc(Protocol):
    """A function which accepts a callback_function kwarg."""

    def __call__(
        self,
        *,
        callback_function: CallbackType | None,
    ) -> None: ...


class WaitResponse:
    """Wait for a response."""

    msg_sent: bool
    response: dict | None

    def __init__(self, timeout: float, request: str) -> None:
        """Initialize."""
        self._event = threading.Event()
        self._request = request
        self._timeout = timeout

    def callback(self, msg_sent: bool, response: dict | None) -> None:
        """Called when the request is finished."""
        self.response = response
        self.msg_sent = msg_sent
        self._event.set()

    def wait_response(self) -> None:
        """Wait for the request to finish."""
        request_completed = self._event.wait(self._timeout)
        if not request_completed:
            raise RequestTimeout(self._request, self._timeout)

        if not self.msg_sent:
            raise RequestFailed(self._request)


def chain_on_success(
    on_success: AcceptsCallbackFunc, callback_function: CallbackType | None
) -> CallbackType:
    """Helper to chain callbacks."""

    def _callback(msg_sent: bool, response: dict | None) -> None:
        if not msg_sent:
            _LOGGER.debug("Not calling on_success %s", on_success)
            if callback_function:
                callback_function(msg_sent, response)
            return
        on_success(callback_function=callback_function)

    return _callback
