"""An abstract class for entities."""
from abc import ABC
import asyncio
from datetime import datetime, timedelta
import functools as ft
import logging
from timeit import default_timer as timer
from typing import Any, Dict, Iterable, List, Optional, Union

from homeassistant.config import DATA_CUSTOMIZE
from homeassistant.const import (
    ATTR_ASSUMED_STATE,
    ATTR_DEVICE_CLASS,
    ATTR_ENTITY_PICTURE,
    ATTR_FRIENDLY_NAME,
    ATTR_HIDDEN,
    ATTR_ICON,
    ATTR_SUPPORTED_FEATURES,
    ATTR_UNIT_OF_MEASUREMENT,
    DEVICE_DEFAULT_NAME,
    STATE_OFF,
    STATE_ON,
    STATE_UNAVAILABLE,
    STATE_UNKNOWN,
    TEMP_CELSIUS,
    TEMP_FAHRENHEIT,
)
from homeassistant.core import CALLBACK_TYPE, Context, HomeAssistant, callback
from homeassistant.exceptions import NoEntitySpecifiedError
from homeassistant.helpers.entity_platform import EntityPlatform
from homeassistant.helpers.entity_registry import (
    EVENT_ENTITY_REGISTRY_UPDATED,
    RegistryEntry,
)
from homeassistant.util import dt as dt_util, ensure_unique_string, slugify
from homeassistant.util.async_ import run_callback_threadsafe

# mypy: allow-untyped-defs, no-check-untyped-defs, no-warn-return-any

_LOGGER = logging.getLogger(__name__)
SLOW_UPDATE_WARNING = 10


def generate_entity_id(
    entity_id_format: str,
    name: Optional[str],
    current_ids: Optional[List[str]] = None,
    hass: Optional[HomeAssistant] = None,
) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    if current_ids is None:
        if hass is None:
            raise ValueError("Missing required parameter currentids or hass")
        return run_callback_threadsafe(
            hass.loop,
            async_generate_entity_id,
            entity_id_format,
            name,
            current_ids,
            hass,
        ).result()

    name = (slugify(name or "") or slugify(DEVICE_DEFAULT_NAME)).lower()

    return ensure_unique_string(entity_id_format.format(name), current_ids)


@callback
def async_generate_entity_id(
    entity_id_format: str,
    name: Optional[str],
    current_ids: Optional[Iterable[str]] = None,
    hass: Optional[HomeAssistant] = None,
) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    if current_ids is None:
        if hass is None:
            raise ValueError("Missing required parameter currentids or hass")

        current_ids = hass.states.async_entity_ids()
    name = (name or DEVICE_DEFAULT_NAME).lower()

    return ensure_unique_string(entity_id_format.format(slugify(name)), current_ids)


class Entity(ABC):
    """An abstract class for Home Assistant entities."""

    # SAFE TO OVERWRITE
    # The properties and methods here are safe to overwrite when inheriting
    # this class. These may be used to customize the behavior of the entity.
    entity_id = None  # type: str

    # Owning hass instance. Will be set by EntityPlatform
    hass: Optional[HomeAssistant] = None

    # Owning platform instance. Will be set by EntityPlatform
    platform: Optional[EntityPlatform] = None

    # If we reported if this entity was slow
    _slow_reported = False

    # If we reported this entity is updated while disabled
    _disabled_reported = False

    # Protect for multiple updates
    _update_staged = False

    # Process updates in parallel
    parallel_updates: Optional[asyncio.Semaphore] = None

    # Entry in the entity registry
    registry_entry: Optional[RegistryEntry] = None

    # Hold list for functions to call on remove.
    _on_remove: Optional[List[CALLBACK_TYPE]] = None

    # Context
    _context: Optional[Context] = None
    _context_set: Optional[datetime] = None

    @property
    def should_poll(self) -> bool:
        """Return True if entity has to be polled for state.

        False if entity pushes its state to HA.
        """
        return True

    @property
    def unique_id(self) -> Optional[str]:
        """Return a unique ID."""
        return None

    @property
    def name(self) -> Optional[str]:
        """Return the name of the entity."""
        return None

    @property
    def state(self) -> Union[None, str, int, float]:
        """Return the state of the entity."""
        return STATE_UNKNOWN

    @property
    def capability_attributes(self) -> Optional[Dict[str, Any]]:
        """Return the capability attributes.