"""Platform for Husqvarna Automower base entity."""

import asyncio
from collections.abc import Callable, Coroutine
import functools
import logging
from typing import Any, Concatenate, overload, override

from aioautomower.exceptions import ApiError
from aioautomower.model import MowerActivities, MowerAttributes, MowerStates, WorkArea

from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import AutomowerDataUpdateCoordinator
from .const import DOMAIN, EXECUTION_TIME_DELAY

_LOGGER = logging.getLogger(__name__)

ERROR_ACTIVITIES = (
    MowerActivities.STOPPED_IN_GARDEN,
    MowerActivities.UNKNOWN,
    MowerActivities.NOT_APPLICABLE,
)
ERROR_STATES = [
    MowerStates.FATAL_ERROR,
    MowerStates.ERROR,
    MowerStates.ERROR_AT_POWER_UP,
    MowerStates.NOT_APPLICABLE,
    MowerStates.UNKNOWN,
    MowerStates.STOPPED,
    MowerStates.OFF,
]


@overload
def handle_sending_exception[_Entity: AutomowerBaseEntity, **_P](
    _func: Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, Any]],
) -> Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, None]]: ...


@overload
def handle_sending_exception[_Entity: AutomowerBaseEntity, **_P](
    *,
    poll_after_sending: bool = False,
) -> Callable[
    [Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, Any]]],
    Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, None]],
]: ...


def handle_sending_exception[_Entity: AutomowerBaseEntity, **_P](
    _func: Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, Any]] | None = None,
    *,
    poll_after_sending: bool = False,
) -> (
    Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, None]]
    | Callable[
        [Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, Any]]],
        Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, None]],
    ]
):
    """Handle exceptions while sending a command and optionally refresh coordinator."""

    def decorator(
        func: Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, Any]],
    ) -> Callable[Concatenate[_Entity, _P], Coroutine[Any, Any, None]]:
        @functools.wraps(func)
        async def wrapper(self: _Entity, *args: _P.args, **kwargs: _P.kwargs) -> None:
            try:
                await func(self, *args, **kwargs)
            except ApiError as exception:
                raise HomeAssistantError(
                    translation_domain=DOMAIN,
                    translation_key="command_send_failed",
                    translation_placeholders={"exception": str(exception)},
                ) from exception
            else:
                if poll_after_sending:
                    # As there are no updates from the websocket for
                    # this attribute, we need to wait until the
                    # command is executed and then poll the API.
                    await asyncio.sleep(EXECUTION_TIME_DELAY)
                    await self.coordinator.async_request_refresh()

        return wrapper

    if _func is None:
        # call with brackets: @handle_sending_exception(...)
        return decorator

    # call without brackets: @handle_sending_exception
    return decorator(_func)


@callback
def _work_area_translation_key(work_area_id: int, key: str) -> str:
    """Return the translation key."""
    if work_area_id == 0:
        return f"my_lawn_{key}"
    return f"work_area_{key}"


class AutomowerBaseEntity(CoordinatorEntity[AutomowerDataUpdateCoordinator]):
    """Defining the Automower base Entity."""

    _attr_has_entity_name = True

    def __init__(
        self,
        mower_id: str,
        coordinator: AutomowerDataUpdateCoordinator,
    ) -> None:
        """Initialize AutomowerEntity."""
        super().__init__(coordinator)
        self.mower_id = mower_id
        model_witout_manufacturer = self.mower_attributes.system.model.removeprefix(
            "Husqvarna "
        ).removeprefix("HUSQVARNA ")
        parts = model_witout_manufacturer.split(maxsplit=1)
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, mower_id)},
            manufacturer="Husqvarna",
            model=parts[0].capitalize().removesuffix("®"),
            model_id=parts[1],
            name=self.mower_attributes.system.name,
            serial_number=self.mower_attributes.system.serial_number,
            suggested_area="Garden",
        )

    @property
    def mower_attributes(self) -> MowerAttributes:
        """Get the mower attributes of the current mower."""
        return self.coordinator.data[self.mower_id]

    @property
    @override
    def available(self) -> bool:
        """Return True if the device is available."""
        return super().available and self.mower_id in self.coordinator.data


class AutomowerControlEntity(AutomowerBaseEntity):
    """Replies available when the mower is connected."""

    @property
    @override
    def available(self) -> bool:
        """Return True if the device is available."""
        return (
            super().available
            and self.mower_attributes.metadata.connected
            and self.mower_attributes.mower.state != MowerStates.OFF
        )


class WorkAreaAvailableEntity(AutomowerControlEntity):
    """Base entity for work areas."""

    def __init__(
        self,
        mower_id: str,
        coordinator: AutomowerDataUpdateCoordinator,
        work_area_id: int,
    ) -> None:
        """Initialize AutomowerEntity."""
        super().__init__(mower_id, coordinator)
        self.work_area_id = work_area_id

    @property
    def work_areas(self) -> dict[int, WorkArea] | None:
        """Get the work areas from the mower attributes."""
        return self.mower_attributes.work_areas

    @property
    def work_area_attributes(self) -> WorkArea | None:
        """Get the work area attributes of the current work area."""
        if (work_areas := self.work_areas) is None:
            return None
        return work_areas.get(self.work_area_id)

    @property
    @override
    def available(self) -> bool:
        """Return True if the work area is available and the mower has no errors."""
        return (
            super().available
            and self.work_areas is not None
            and self.work_area_id in self.work_areas
        )


class WorkAreaControlEntity(WorkAreaAvailableEntity, AutomowerControlEntity):
    """Base entity for work areas with control function."""
