"""Platform for the cover integration."""

from abc import ABC
import logging
from typing import Any, Callable, List

from wyzeapy import Wyzeapy, CameraService
from wyzeapy.services.camera_service import Camera
from wyzeapy.exceptions import AccessTokenError, ParameterError, UnknownApiError
from wyzeapy.types import DeviceTypes

import homeassistant.components.cover
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ATTRIBUTION
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers import device_registry as dr
from homeassistant.exceptions import HomeAssistantError
from homeassistant.components.cover import CoverDeviceClass, CoverEntityFeature


from .const import CAMERA_UPDATED, CONF_CLIENT, DOMAIN
from .token_manager import token_exception_handler

_LOGGER = logging.getLogger(__name__)
ATTRIBUTION = "Data provided by Wyze"


@token_exception_handler
async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: Callable[[List[Any], bool], None],
) -> None:
    """
    This function sets up the config_entry

    :param hass: Home Assistant instance
    :param config_entry: The current config_entry
    :param async_add_entities: This function adds entities to the config_entry
    :return:
    """

    _LOGGER.debug("""Creating new WyzeApi cover component""")
    client: Wyzeapy = hass.data[DOMAIN][config_entry.entry_id][CONF_CLIENT]
    camera_service = await client.camera_service
    cameras: List[Camera] = await camera_service.get_cameras()
    garages = []
    for camera in cameras:
        if camera.device_params["dongle_product_model"] == "HL_CGDC":
            garages.append(WyzeGarageDoor(camera_service, camera))

    async_add_entities(garages, True)


class WyzeGarageDoor(homeassistant.components.cover.CoverEntity, ABC):
    """Representation of a Wyze Garage Door."""

    _attr_device_class = CoverDeviceClass.GARAGE
    _attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
    _attr_has_entity_name = True

    def __init__(self, camera_service: CameraService, camera: Camera):
        """Initialize a Wyze garage door."""
        self._camera = camera
        if self._camera.type not in [DeviceTypes.CAMERA]:
            raise HomeAssistantError(f"Invalid device type: {self._camera.type}")

        self._camera_service = camera_service
        self._available = self._camera.available

    @property
    def device_info(self):
        """Return device information about this entity."""
        return {
            "identifiers": {(DOMAIN, self._camera.mac)},
            "name": f"{self._camera.nickname}",
            "connections": {(dr.CONNECTION_NETWORK_MAC, self._camera.mac)},
        }

    @property
    def extra_state_attributes(self):
        """Return device attributes of the entity."""
        return {
            ATTR_ATTRIBUTION: ATTRIBUTION,
            "device model": f"{self._camera.product_model}.{self._camera.device_params['dongle_product_model']}",
        }

    @property
    def should_poll(self) -> bool:
        return False

    @token_exception_handler
    async def async_open_cover(self, **kwargs):
        """Open the cover."""
        try:
            await self._camera_service.garage_door_open(self._camera)
        except (AccessTokenError, ParameterError, UnknownApiError) as err:
            raise HomeAssistantError(f"Wyze returned an error: {err.args}") from err
        except Exception as err:
            raise HomeAssistantError(err) from err
        else:
            self._camera.garage = True
            self.async_write_ha_state()

    @token_exception_handler
    async def async_close_cover(self, **kwargs):
        """Close the cover."""
        try:
            await self._camera_service.garage_door_close(self._camera)
        except (AccessTokenError, ParameterError, UnknownApiError) as err:
            raise HomeAssistantError(f"Wyze returned an error: {err.args}") from err
        except Exception as err:
            raise HomeAssistantError(err) from err
        else:
            self._camera.garage = False
            self.async_write_ha_state()

    @property
    def is_closed(self):
        """Return if the cover is closed."""
        return not self._camera.garage

    @property
    def available(self):
        """Return the connection status of this cover."""
        return self._camera.available

    @property
    def unique_id(self):
        """Define a unique id for this entity."""
        return f"{self._camera.mac}_GarageDoor"

    @property
    def name(self):
        """Return the name of the garage door."""
        return "Garage Door"

    async def async_added_to_hass(self) -> None:
        self.async_on_remove(
            async_dispatcher_connect(
                self.hass,
                f"{CAMERA_UPDATED}-{self._camera.mac}",
                self.handle_camera_update,
            )
        )

    @callback
    def handle_camera_update(self, camera: Camera) -> None:
        """Update the cover whenever there is an update"""
        self._camera = camera
        self.async_write_ha_state()
