"""Support for ESPHome Alarm Control Panel."""

from __future__ import annotations

from functools import partial

from aioesphomeapi import (
    AlarmControlPanelCommand,
    AlarmControlPanelEntityState as ESPHomeAlarmControlPanelEntityState,
    AlarmControlPanelInfo,
    AlarmControlPanelState as ESPHomeAlarmControlPanelState,
    APIIntEnum,
    EntityInfo,
)

from homeassistant.components.alarm_control_panel import (
    AlarmControlPanelEntity,
    AlarmControlPanelEntityFeature,
    AlarmControlPanelState,
    CodeFormat,
)
from homeassistant.core import callback

from .entity import (
    EsphomeEntity,
    convert_api_error_ha_error,
    esphome_state_property,
    platform_async_setup_entry,
)
from .enum_mapper import EsphomeEnumMapper

PARALLEL_UPDATES = 0

_ESPHOME_ACP_STATE_TO_HASS_STATE: EsphomeEnumMapper[
    ESPHomeAlarmControlPanelState, AlarmControlPanelState
] = EsphomeEnumMapper(
    {
        ESPHomeAlarmControlPanelState.DISARMED: AlarmControlPanelState.DISARMED,
        ESPHomeAlarmControlPanelState.ARMED_HOME: AlarmControlPanelState.ARMED_HOME,
        ESPHomeAlarmControlPanelState.ARMED_AWAY: AlarmControlPanelState.ARMED_AWAY,
        ESPHomeAlarmControlPanelState.ARMED_NIGHT: AlarmControlPanelState.ARMED_NIGHT,
        ESPHomeAlarmControlPanelState.ARMED_VACATION: AlarmControlPanelState.ARMED_VACATION,
        ESPHomeAlarmControlPanelState.ARMED_CUSTOM_BYPASS: AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
        ESPHomeAlarmControlPanelState.PENDING: AlarmControlPanelState.PENDING,
        ESPHomeAlarmControlPanelState.ARMING: AlarmControlPanelState.ARMING,
        ESPHomeAlarmControlPanelState.DISARMING: AlarmControlPanelState.DISARMING,
        ESPHomeAlarmControlPanelState.TRIGGERED: AlarmControlPanelState.TRIGGERED,
    }
)


class EspHomeACPFeatures(APIIntEnum):
    """ESPHome AlarmCintolPanel feature numbers."""

    ARM_HOME = 1
    ARM_AWAY = 2
    ARM_NIGHT = 4
    TRIGGER = 8
    ARM_CUSTOM_BYPASS = 16
    ARM_VACATION = 32


class EsphomeAlarmControlPanel(
    EsphomeEntity[AlarmControlPanelInfo, ESPHomeAlarmControlPanelEntityState],
    AlarmControlPanelEntity,
):
    """An Alarm Control Panel implementation for ESPHome."""

    @callback
    def _on_static_info_update(self, static_info: EntityInfo) -> None:
        """Set attrs from static info."""
        super()._on_static_info_update(static_info)
        static_info = self._static_info
        feature = 0
        if static_info.supported_features & EspHomeACPFeatures.ARM_HOME:
            feature |= AlarmControlPanelEntityFeature.ARM_HOME
        if static_info.supported_features & EspHomeACPFeatures.ARM_AWAY:
            feature |= AlarmControlPanelEntityFeature.ARM_AWAY
        if static_info.supported_features & EspHomeACPFeatures.ARM_NIGHT:
            feature |= AlarmControlPanelEntityFeature.ARM_NIGHT
        if static_info.supported_features & EspHomeACPFeatures.TRIGGER:
            feature |= AlarmControlPanelEntityFeature.TRIGGER
        if static_info.supported_features & EspHomeACPFeatures.ARM_CUSTOM_BYPASS:
            feature |= AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS
        if static_info.supported_features & EspHomeACPFeatures.ARM_VACATION:
            feature |= AlarmControlPanelEntityFeature.ARM_VACATION
        self._attr_supported_features = AlarmControlPanelEntityFeature(feature)
        self._attr_code_format = (
            CodeFormat.NUMBER if static_info.requires_code else None
        )
        self._attr_code_arm_required = bool(static_info.requires_code_to_arm)

    @property
    @esphome_state_property
    def alarm_state(self) -> AlarmControlPanelState | None:
        """Return the state of the device."""
        return _ESPHOME_ACP_STATE_TO_HASS_STATE.from_esphome(self._state.state)

    @convert_api_error_ha_error
    async def async_alarm_disarm(self, code: str | None = None) -> None:
        """Send disarm command."""
        self._client.alarm_control_panel_command(
            self._key, AlarmControlPanelCommand.DISARM, code
        )

    @convert_api_error_ha_error
    async def async_alarm_arm_home(self, code: str | None = None) -> None:
        """Send arm home command."""
        self._client.alarm_control_panel_command(
            self._key, AlarmControlPanelCommand.ARM_HOME, code
        )

    @convert_api_error_ha_error
    async def async_alarm_arm_away(self, code: str | None = None) -> None:
        """Send arm away command."""
        self._client.alarm_control_panel_command(
            self._key, AlarmControlPanelCommand.ARM_AWAY, code
        )

    @convert_api_error_ha_error
    async def async_alarm_arm_night(self, code: str | None = None) -> None:
        """Send arm away command."""
        self._client.alarm_control_panel_command(
            self._key, AlarmControlPanelCommand.ARM_NIGHT, code
        )

    @convert_api_error_ha_error
    async def async_alarm_arm_custom_bypass(self, code: str | None = None) -> None:
        """Send arm away command."""
        self._client.alarm_control_panel_command(
            self._key, AlarmControlPanelCommand.ARM_CUSTOM_BYPASS, code
        )

    @convert_api_error_ha_error
    async def async_alarm_arm_vacation(self, code: str | None = None) -> None:
        """Send arm away command."""
        self._client.alarm_control_panel_command(
            self._key, AlarmControlPanelCommand.ARM_VACATION, code
        )

    @convert_api_error_ha_error
    async def async_alarm_trigger(self, code: str | None = None) -> None:
        """Send alarm trigger command."""
        self._client.alarm_control_panel_command(
            self._key, AlarmControlPanelCommand.TRIGGER, code
        )


async_setup_entry = partial(
    platform_async_setup_entry,
    info_type=AlarmControlPanelInfo,
    entity_type=EsphomeAlarmControlPanel,
    state_type=ESPHomeAlarmControlPanelEntityState,
)