#!/usr/bin/env python
# -*- coding: utf-8 -*-
#  SPDX-License-Identifier: Apache-2.0
"""
Alexa Devices Alarm Control Panel using Guard Mode.

For more details about this platform, please refer to the documentation at
https://community.home-assistant.io/t/echo-devices-alexa-as-media-player-testers-needed/58639
"""
import logging
from typing import Dict, List, Text  # noqa pylint: disable=unused-import

from homeassistant import util
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.const import STATE_ALARM_ARMED_AWAY, STATE_ALARM_DISARMED
from homeassistant.helpers.event import async_call_later

from . import (
    CONF_EMAIL,
    CONF_EXCLUDE_DEVICES,
    CONF_INCLUDE_DEVICES,
    DATA_ALEXAMEDIA,
    DOMAIN as ALEXA_DOMAIN,
    MIN_TIME_BETWEEN_FORCED_SCANS,
    MIN_TIME_BETWEEN_SCANS,
    hide_email,
)
from .helpers import _catch_login_errors, add_devices, retry_async

_LOGGER = logging.getLogger(__name__)

DEPENDENCIES = [ALEXA_DOMAIN]


@retry_async(limit=5, delay=2, catch_exceptions=True)
async def async_setup_platform(
    hass, config, add_devices_callback, discovery_info=None
) -> bool:
    """Set up the Alexa alarm control panel platform."""
    devices = []  # type: List[AlexaAlarmControlPanel]
    account = config[CONF_EMAIL]
    include_filter = config.get(CONF_INCLUDE_DEVICES, [])
    exclude_filter = config.get(CONF_EXCLUDE_DEVICES, [])
    account_dict = hass.data[DATA_ALEXAMEDIA]["accounts"][account]
    if "alarm_control_panel" not in (account_dict["entities"]):
        (
            hass.data[DATA_ALEXAMEDIA]["accounts"][account]["entities"][
                "alarm_control_panel"
            ]
        ) = {}
    alexa_client: AlexaAlarmControlPanel = AlexaAlarmControlPanel(
        account_dict["login_obj"]
    )
    await alexa_client.init()
    if not (alexa_client and alexa_client.unique_id):
        _LOGGER.debug(
            "%s: Skipping creation of uninitialized device: %s",
            hide_email(account),
            alexa_client,
        )
    elif alexa_client.unique_id not in (
        account_dict["entities"]["alarm_control_panel"]
    ):
        devices.append(alexa_client)
        (
            hass.data[DATA_ALEXAMEDIA]["accounts"][account]["entities"][
                "alarm_control_panel"
            ][alexa_client.unique_id]
        ) = alexa_client
    else:
        _LOGGER.debug(
            "%s: Skipping already added device: %s", hide_email(account), alexa_client
        )
    return await add_devices(
        hide_email(account),
        devices,
        add_devices_callback,
        include_filter,
        exclude_filter,
    )


async def async_setup_entry(hass, config_entry, async_add_devices):
    """Set up the Alexa alarm control panel platform by config_entry."""
    return await async_setup_platform(
        hass, config_entry.data, async_add_devices, discovery_info=None
    )


async def async_unload_entry(hass, entry) -> bool:
    """Unload a config entry."""
    account = entry.data[CONF_EMAIL]
    account_dict = hass.data[DATA_ALEXAMEDIA]["accounts"][account]
    for device in account_dict["entities"]["alarm_control_panel"].values():
        await device.async_remove()
    return True


class AlexaAlarmControlPanel(AlarmControlPanel):
    """Implementation of Alexa Media Player alarm control panel."""

    def __init__(self, login) -> None:
        # pylint: disable=unexpected-keyword-arg
        """Initialize the Alexa device."""
        from alexapy import AlexaAPI

        # Class info
        self._login = login
        self.alexa_api = AlexaAPI(self, login)
        self.alexa_api_session = login.session
        self.account = hide_email(login.email)

        # Guard info
        self._appliance_id = None
        self._guard_entity_id = None
        self._friendly_name = "Alexa Guard"
        self._state = None
        self._should_poll = False
        self._attrs: Dict[Text, Text] = {}

    async def init(self):
        """Initialize."""
        try:
            from simplejson import JSONDecodeError

            data = await self.alexa_api.get_guard_details(self._login)
            guard_dict = data["locationDetails"]["locationDetails"]["Default_Location"][
                "amazonBridgeDetails"
            ]["amazonBridgeDetails"]["LambdaBridge_AAA/OnGuardSmartHomeBridgeService"][
                "applianceDetails"
            ][
                "applianceDetails"
            ]
        except (KeyError, TypeError, JSONDecodeError):
            guard_dict = {}
        for _, value in guard_dict.items():
            if value["modelName"] == "REDROCK_GUARD_PANEL":
                self._appliance_id = value["applianceId"]
                self._guard_entity_id = value["entityId"]
                self._friendly_name += " " + self._appliance_id[-5:]
                _LOGGER.debug(
                    "%s: Discovered %s: %s %s",
                    self.account,
                    self._friendly_name,
                    self._appliance_id,
                    self._guard_entity_id,
                )
        if not self._appliance_id:
            _LOGGER.debug("%s: No Alexa Guard entity found", self.account)

    async def async_added_to_hass(self):
        """Store register state change callback."""
        try:
            if not self.enabled:
                return
        except AttributeError:
            pass
        # Register event handler on bus
        self._listener = self.hass.bus.async_listen(
            f"{ALEXA_DOMAIN}_{hide_email(self._login.email)}"[0:32], self._handle_event
        )
        await self.async_update()

    async def async_will_remove_from_hass(self):
        """Prepare to remove entity."""
        # Register event handler on bus
        self._listener()

    def _handle_event(self, event):
        """Handle websocket events.

        Used instead of polling.
        """
        try:
            if not self.enabled:
                return
        except AttributeError:
            pass
        if "push_activity" in event.data:
            async_call_later(
                self.hass,
                2,
                lambda _: self.hass.async_create_task(
                    self.async_update(no_throttle=True)
                ),
            )

    @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
    @_catch_login_errors
    async def async_update(self):
        """Update Guard state."""
        try:
            if not self.enabled:
                return
        except AttributeError:
            pass
        import json

        _LOGGER.debug("%s: Refreshing %s", self.account, self.name)
        state = None
        state_json = await self.alexa_api.get_guard_state(
            self._login, self._appliance_id
        )
        # _LOGGER.debug("%s: state_json %s", self.account, state_json)
        if state_json and "deviceStates" in state_json and state_json["deviceStates"]:
            cap = state_json["deviceStates"][0]["capabilityStates"]
            # _LOGGER.debug("%s: cap %s", self.account, cap)
            for item_json in cap:
                item = json.loads(item_json)
                # _LOGGER.debug("%s: item %s", self.account, item)
                if item["name"] == "armState":
                    state = item["value"]
                    # _LOGGER.debug("%s: state %s", self.account, state)
        elif state_json["errors"]:
            _LOGGER.debug(
                "%s: Error refreshing alarm_control_panel %s: %s",
                self.account,
                self.name,
                json.dumps(state_json["errors"]) if state_json else None,
            )
        if state is None:
            return
        if state == "ARMED_AWAY":
            self._state = STATE_ALARM_ARMED_AWAY
        elif state == "ARMED_STAY":
            self._state = STATE_ALARM_DISARMED
        else:
            self._state = STATE_ALARM_DISARMED
        _LOGGER.debug("%s: Alarm State: %s", self.account, self.state)
        self.async_schedule_update_ha_state()

    @_catch_login_errors
    async def async_alarm_disarm(self, code=None) -> None:
        # pylint: disable=unexpected-keyword-arg
        """Send disarm command."""
        try:
            if not self.enabled:
                return
        except AttributeError:
            pass
        await self.alexa_api.set_guard_state(
            self._login, self._guard_entity_id, "ARMED_STAY"
        )
        await self.async_update(no_throttle=True)
        self.async_schedule_update_ha_state()

    @_catch_login_errors
    async def async_alarm_arm_away(self, code=None) -> None:
        """Send arm away command."""
        # pylint: disable=unexpected-keyword-arg
        try:
            if not self.enabled:
                return
        except AttributeError:
            pass
        await self.alexa_api.set_guard_state(
            self._login, self._guard_entity_id, "ARMED_AWAY"
        )
        await self.async_update(no_throttle=True)
        self.async_schedule_update_ha_state()

    @property
    def unique_id(self):
        """Return the unique ID."""
        return self._guard_entity_id

    @property
    def name(self):
        """Return the name of the device."""
        return self._friendly_name

    @property
    def state(self):
        """Return the state of the device."""
        return self._state

    @property
    def device_state_attributes(self):
        """Return the state attributes."""
        return self._attrs

    @property
    def should_poll(self):
        """Return the polling state."""
        return self._should_poll or not (
            self.hass.data[DATA_ALEXAMEDIA]["accounts"][self._login.email]["websocket"]
        )

    @property
    def supported_features(self) -> int:
        """Return the list of supported features."""
        try:
            from homeassistant.components.alarm_control_panel import (
                SUPPORT_ALARM_ARM_AWAY,
            )
        except ImportError:
            return 0
        return SUPPORT_ALARM_ARM_AWAY
