"""Helper functions for Homematicip Cloud Integration."""

from collections.abc import Callable, Coroutine
from functools import wraps
import json
import logging
from typing import Any, Concatenate, TypeGuard

from homematicip.base.enums import FunctionalChannelType
from homematicip.device import Device

from homeassistant.exceptions import HomeAssistantError

from .entity import HomematicipGenericEntity

_LOGGER = logging.getLogger(__name__)


def is_error_response(response: Any) -> TypeGuard[dict[str, Any]]:
    """Response from async call contains errors or not."""
    if isinstance(response, dict):
        return response.get("errorCode") not in ("", None)

    return False


def handle_errors[_HomematicipGenericEntityT: HomematicipGenericEntity, **_P](
    func: Callable[
        Concatenate[_HomematicipGenericEntityT, _P], Coroutine[Any, Any, Any]
    ],
) -> Callable[Concatenate[_HomematicipGenericEntityT, _P], Coroutine[Any, Any, Any]]:
    """Handle async errors."""

    @wraps(func)
    async def inner(
        self: _HomematicipGenericEntityT, *args: _P.args, **kwargs: _P.kwargs
    ) -> None:
        """Handle errors from async call."""
        result = await func(self, *args, **kwargs)
        if is_error_response(result):
            _LOGGER.error(
                "Error while execute function %s: %s",
                __name__,
                json.dumps(result),
            )
            raise HomeAssistantError(
                f"Error while execute function {func.__name__}:"
                f" {result.get('errorCode')}."
                " See log for more information."
            )

    return inner


def get_channels_from_device(device: Device, channel_type: FunctionalChannelType):
    """Get all channels matching with channel_type from device."""
    return [
        ch
        for ch in device.functionalChannels
        if ch.functionalChannelType == channel_type
    ]


def smoke_detector_channel_data_exists(device: Device, field: str) -> bool:
    """Check if a smoke detector's channel payload contains a specific field.

    The library always initializes device attributes with defaults, so hasattr
    cannot distinguish between actual API data and defaults. This checks the
    raw channel payload to determine if the field was actually sent by the API.
    """
    channels = get_channels_from_device(
        device, FunctionalChannelType.SMOKE_DETECTOR_CHANNEL
    )
    return bool(channels and field in getattr(channels[0], "_rawJSONData", {}))
