"""Support for HomematicIP Cloud cover devices."""

from typing import Any, override

from homematicip.base.enums import DoorCommand, DoorState
from homematicip.device import (
    BlindModule,
    DinRailBlind4,
    FullFlushBlind,
    FullFlushShutter,
    GarageDoorModuleTormatic,
    HoermannDrivesModule,
    WiredDinRailBlind4,
)
from homematicip.group import ExtendedLinkedShutterGroup

from homeassistant.components.cover import (
    ATTR_POSITION,
    ATTR_TILT_POSITION,
    CoverDeviceClass,
    CoverEntity,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .entity import HomematicipGenericEntity
from .hap import HomematicIPConfigEntry, HomematicipHAP

HMIP_COVER_OPEN = 0
HMIP_COVER_CLOSED = 1
HMIP_SLATS_OPEN = 0
HMIP_SLATS_CLOSED = 1


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: HomematicIPConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the HomematicIP cover from a config entry."""
    hap = config_entry.runtime_data
    entities: list[HomematicipGenericEntity] = [
        HomematicipCoverShutterGroup(hap, group)
        for group in hap.home.groups
        if isinstance(group, ExtendedLinkedShutterGroup)
    ]
    for device in hap.home.devices:
        if isinstance(device, BlindModule):
            entities.append(HomematicipBlindModule(hap, device))
        elif isinstance(device, (DinRailBlind4, WiredDinRailBlind4)):
            entities.extend(
                HomematicipMultiCoverSlats(hap, device, channel=channel)
                for channel in range(1, 5)
            )
        elif isinstance(device, FullFlushBlind):
            entities.append(HomematicipCoverSlats(hap, device))
        elif isinstance(device, FullFlushShutter):
            entities.append(HomematicipCoverShutter(hap, device))
        elif isinstance(device, (HoermannDrivesModule, GarageDoorModuleTormatic)):
            entities.append(HomematicipGarageDoorModule(hap, device))

    async_add_entities(entities)


class HomematicipBlindModule(HomematicipGenericEntity, CoverEntity):
    """Representation of the HomematicIP blind module."""

    _attr_device_class = CoverDeviceClass.BLIND

    def __init__(self, hap: HomematicipHAP, device) -> None:
        """Initialize the blind module entity."""
        super().__init__(hap, device, feature_id="blind")

    @property
    @override
    def current_cover_position(self) -> int | None:
        """Return current position of cover."""
        if self._device.primaryShadingLevel is not None:
            return int((1 - self._device.primaryShadingLevel) * 100)
        return None

    @property
    @override
    def current_cover_tilt_position(self) -> int | None:
        """Return current tilt position of cover."""
        if self._device.secondaryShadingLevel is not None:
            return int((1 - self._device.secondaryShadingLevel) * 100)
        return None

    @override
    async def async_set_cover_position(self, **kwargs: Any) -> None:
        """Move the cover to a specific position."""
        position = kwargs[ATTR_POSITION]
        # HmIP cover is closed:1 -> open:0
        level = 1 - position / 100.0
        await self._device.set_primary_shading_level_async(primaryShadingLevel=level)

    @override
    async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
        """Move the cover to a specific tilt position."""
        position = kwargs[ATTR_TILT_POSITION]
        # HmIP slats is closed:1 -> open:0
        level = 1 - position / 100.0
        await self._device.set_secondary_shading_level_async(
            primaryShadingLevel=self._device.primaryShadingLevel,
            secondaryShadingLevel=level,
        )

    @property
    @override
    def is_closed(self) -> bool | None:
        """Return if the cover is closed."""
        if self._device.primaryShadingLevel is not None:
            return self._device.primaryShadingLevel == HMIP_COVER_CLOSED
        return None

    @override
    async def async_open_cover(self, **kwargs: Any) -> None:
        """Open the cover."""
        await self._device.set_primary_shading_level_async(
            primaryShadingLevel=HMIP_COVER_OPEN
        )

    @override
    async def async_close_cover(self, **kwargs: Any) -> None:
        """Close the cover."""
        await self._device.set_primary_shading_level_async(
            primaryShadingLevel=HMIP_COVER_CLOSED
        )

    @override
    async def async_stop_cover(self, **kwargs: Any) -> None:
        """Stop the device if in motion."""
        await self._device.stop_async()

    @override
    async def async_open_cover_tilt(self, **kwargs: Any) -> None:
        """Open the slats."""
        await self._device.set_secondary_shading_level_async(
            primaryShadingLevel=self._device.primaryShadingLevel,
            secondaryShadingLevel=HMIP_SLATS_OPEN,
        )

    @override
    async def async_close_cover_tilt(self, **kwargs: Any) -> None:
        """Close the slats."""
        await self._device.set_secondary_shading_level_async(
            primaryShadingLevel=self._device.primaryShadingLevel,
            secondaryShadingLevel=HMIP_SLATS_CLOSED,
        )

    @override
    async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
        """Stop the device if in motion."""
        await self._device.stop_async()


class HomematicipMultiCoverShutter(HomematicipGenericEntity, CoverEntity):
    """Representation of the HomematicIP cover shutter."""

    _attr_device_class = CoverDeviceClass.SHUTTER

    def __init__(
        self,
        hap: HomematicipHAP,
        device,
        channel=1,
        is_multi_channel=True,
        feature_id="shutter",
    ) -> None:
        """Initialize the multi cover entity."""
        super().__init__(
            hap,
            device,
            channel=channel,
            is_multi_channel=is_multi_channel,
            feature_id=feature_id,
        )

    @property
    @override
    def current_cover_position(self) -> int | None:
        """Return current position of cover."""
        if self._device.functionalChannels[self._channel].shutterLevel is not None:
            return int(
                (1 - self._device.functionalChannels[self._channel].shutterLevel) * 100
            )
        return None

    @override
    async def async_set_cover_position(self, **kwargs: Any) -> None:
        """Move the cover to a specific position."""
        position = kwargs[ATTR_POSITION]
        # HmIP cover is closed:1 -> open:0
        level = 1 - position / 100.0
        await self._device.set_shutter_level_async(level, self._channel)

    @property
    @override
    def is_closed(self) -> bool | None:
        """Return if the cover is closed."""
        if self._device.functionalChannels[self._channel].shutterLevel is not None:
            return (
                self._device.functionalChannels[self._channel].shutterLevel
                == HMIP_COVER_CLOSED
            )
        return None

    @override
    async def async_open_cover(self, **kwargs: Any) -> None:
        """Open the cover."""
        await self._device.set_shutter_level_async(HMIP_COVER_OPEN, self._channel)

    @override
    async def async_close_cover(self, **kwargs: Any) -> None:
        """Close the cover."""
        await self._device.set_shutter_level_async(HMIP_COVER_CLOSED, self._channel)

    @override
    async def async_stop_cover(self, **kwargs: Any) -> None:
        """Stop the device if in motion."""
        await self._device.set_shutter_stop_async(self._channel)


class HomematicipCoverShutter(HomematicipMultiCoverShutter, CoverEntity):
    """Representation of the HomematicIP cover shutter."""

    def __init__(self, hap: HomematicipHAP, device) -> None:
        """Initialize the multi cover entity."""
        super().__init__(hap, device, is_multi_channel=False)


class HomematicipMultiCoverSlats(HomematicipMultiCoverShutter, CoverEntity):
    """Representation of the HomematicIP multi cover slats."""

    def __init__(
        self,
        hap: HomematicipHAP,
        device,
        channel=1,
        is_multi_channel=True,
    ) -> None:
        """Initialize the multi slats entity."""
        super().__init__(
            hap,
            device,
            channel=channel,
            is_multi_channel=is_multi_channel,
            feature_id="slats",
        )

    @property
    @override
    def current_cover_tilt_position(self) -> int | None:
        """Return current tilt position of cover."""
        if self._device.functionalChannels[self._channel].slatsLevel is not None:
            return int(
                (1 - self._device.functionalChannels[self._channel].slatsLevel) * 100
            )
        return None

    @override
    async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
        """Move the cover to a specific tilt position."""
        position = kwargs[ATTR_TILT_POSITION]
        # HmIP slats is closed:1 -> open:0
        level = 1 - position / 100.0
        await self._device.set_slats_level_async(
            slatsLevel=level, channelIndex=self._channel
        )

    @override
    async def async_open_cover_tilt(self, **kwargs: Any) -> None:
        """Open the slats."""
        await self._device.set_slats_level_async(
            slatsLevel=HMIP_SLATS_OPEN, channelIndex=self._channel
        )

    @override
    async def async_close_cover_tilt(self, **kwargs: Any) -> None:
        """Close the slats."""
        await self._device.set_slats_level_async(
            slatsLevel=HMIP_SLATS_CLOSED, channelIndex=self._channel
        )

    @override
    async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
        """Stop the device if in motion."""
        await self._device.set_shutter_stop_async(self._channel)


class HomematicipCoverSlats(HomematicipMultiCoverSlats, CoverEntity):
    """Representation of the HomematicIP cover slats."""

    def __init__(self, hap: HomematicipHAP, device) -> None:
        """Initialize the multi slats entity."""
        super().__init__(hap, device, is_multi_channel=False)


class HomematicipGarageDoorModule(HomematicipGenericEntity, CoverEntity):
    """Representation of the HomematicIP Garage Door Module."""

    _attr_device_class = CoverDeviceClass.GARAGE

    def __init__(self, hap: HomematicipHAP, device) -> None:
        """Initialize the garage door module entity."""
        super().__init__(hap, device, feature_id="garage_door")

    @property
    @override
    def current_cover_position(self) -> int | None:
        """Return current position of cover."""
        door_state_to_position = {
            DoorState.CLOSED: 0,
            DoorState.OPEN: 100,
            DoorState.VENTILATION_POSITION: 10,
            DoorState.POSITION_UNKNOWN: None,
        }
        return door_state_to_position.get(self._device.doorState)

    @property
    @override
    def is_closed(self) -> bool | None:
        """Return if the cover is closed."""
        channel = self.get_channel_or_raise()
        return channel.doorState == DoorState.CLOSED

    @override
    async def async_open_cover(self, **kwargs: Any) -> None:
        """Open the cover."""
        channel = self.get_channel_or_raise()
        await channel.async_send_door_command(DoorCommand.OPEN)

    @override
    async def async_close_cover(self, **kwargs: Any) -> None:
        """Close the cover."""
        channel = self.get_channel_or_raise()
        await channel.async_send_door_command(DoorCommand.CLOSE)

    @override
    async def async_stop_cover(self, **kwargs: Any) -> None:
        """Stop the cover."""
        channel = self.get_channel_or_raise()
        await channel.async_send_door_command(DoorCommand.STOP)


class HomematicipCoverShutterGroup(HomematicipGenericEntity, CoverEntity):
    """Representation of the HomematicIP cover shutter group."""

    _attr_has_entity_name = False
    _attr_device_class = CoverDeviceClass.SHUTTER

    def __init__(self, hap: HomematicipHAP, device, post: str = "ShutterGroup") -> None:
        """Initialize switching group."""
        device.modelType = f"HmIP-{post}"
        super().__init__(
            hap, device, post, is_multi_channel=False, feature_id="shutter"
        )

    @property
    @override
    def available(self) -> bool:
        """Cover shutter group available.

        A cover shutter group must be available, and should not be affected by
        the individual availability of group members.
        This allows controlling the shutters even when individual group
        members are not available.
        """
        return True

    @property
    @override
    def current_cover_position(self) -> int | None:
        """Return current position of cover."""
        if self._device.shutterLevel is not None:
            return int((1 - self._device.shutterLevel) * 100)
        return None

    @property
    @override
    def current_cover_tilt_position(self) -> int | None:
        """Return current tilt position of cover."""
        if self._device.slatsLevel is not None:
            return int((1 - self._device.slatsLevel) * 100)
        return None

    @property
    @override
    def is_closed(self) -> bool | None:
        """Return if the cover is closed."""
        if self._device.shutterLevel is not None:
            return self._device.shutterLevel == HMIP_COVER_CLOSED
        return None

    @override
    async def async_set_cover_position(self, **kwargs: Any) -> None:
        """Move the cover to a specific position."""
        position = kwargs[ATTR_POSITION]
        # HmIP cover is closed:1 -> open:0
        level = 1 - position / 100.0
        await self._device.set_shutter_level_async(level)

    @override
    async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
        """Move the cover to a specific tilt position."""
        position = kwargs[ATTR_TILT_POSITION]
        # HmIP slats is closed:1 -> open:0
        level = 1 - position / 100.0
        await self._device.set_slats_level_async(level)

    @override
    async def async_open_cover(self, **kwargs: Any) -> None:
        """Open the cover."""
        await self._device.set_shutter_level_async(HMIP_COVER_OPEN)

    @override
    async def async_close_cover(self, **kwargs: Any) -> None:
        """Close the cover."""
        await self._device.set_shutter_level_async(HMIP_COVER_CLOSED)

    @override
    async def async_stop_cover(self, **kwargs: Any) -> None:
        """Stop the group if in motion."""
        await self._device.set_shutter_stop_async()

    @override
    async def async_open_cover_tilt(self, **kwargs: Any) -> None:
        """Open the slats."""
        await self._device.set_slats_level_async(HMIP_SLATS_OPEN)

    @override
    async def async_close_cover_tilt(self, **kwargs: Any) -> None:
        """Close the slats."""
        await self._device.set_slats_level_async(HMIP_SLATS_CLOSED)

    @override
    async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
        """Stop the group if in motion."""
        await self._device.set_shutter_stop_async()
