"""Home Assistant Switcher Component."""

from asyncio import QueueEmpty, TimeoutError as Asyncio_TimeoutError, wait_for
from datetime import datetime, timedelta
from logging import getLogger
from typing import Dict, Optional

import voluptuous as vol

from homeassistant.auth.permissions.const import POLICY_EDIT
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import CONF_ENTITY_ID, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import callback, split_entity_id
from homeassistant.exceptions import Unauthorized, UnknownUser
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.discovery import (async_listen_platform,
                                             async_load_platform)
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.typing import (ContextType, EventType,
                                          HomeAssistantType, ServiceCallType)
from homeassistant.loader import bind_hass

_LOGGER = getLogger(__name__)

DOMAIN = 'switcher_kis'

CONF_AUTO_OFF = 'auto_off'
CONF_DEVICE_ID = 'device_id'
CONF_DEVICE_PASSWORD = 'device_password'
CONF_PHONE_ID = 'phone_id'

DATA_DEVICE = 'device'

SIGNAL_SWITCHER_DEVICE_UPDATE = 'switcher_device_update'

ATTR_AUTO_OFF_SET = 'auto_off_set'
ATTR_ELECTRIC_CURRENT = 'electric_current'
ATTR_REMAINING_TIME = 'remaining_time'

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Required(CONF_PHONE_ID): cv.string,
        vol.Required(CONF_DEVICE_ID): cv.string,
        vol.Required(CONF_DEVICE_PASSWORD): cv.string
    })
}, extra=vol.ALLOW_EXTRA)

SERVICE_SET_AUTO_OFF_NAME = 'set_auto_off'
SERVICE_SET_AUTO_OFF_SCHEMA = vol.Schema({
    vol.Required(CONF_ENTITY_ID): cv.entity_id,
    vol.Required(CONF_AUTO_OFF): cv.time_period_str
})


@bind_hass
async def _validate_edit_permission(
        hass: HomeAssistantType, context: ContextType,
        entity_id: str) -> None:
    """Use for validating user control permissions."""
    splited = split_entity_id(entity_id)
    if splited[0] != SWITCH_DOMAIN or not splited[1].startswith(DOMAIN):
        raise Unauthorized(
            context=context, entity_id=entity_id, permission=(POLICY_EDIT, ))

    user = await hass.auth.async_get_user(context.user_id)
    if user is None:
        raise UnknownUser(
            context=context, entity_id=entity_id, permission=(POLICY_EDIT, ))

    if not user.permissions.check_entity(entity_id, POLICY_EDIT):
        raise Unauthorized(
            context=context, entity_id=entity_id, permission=(POLICY_EDIT, ))


async def async_setup(hass: HomeAssistantType, config: Dict) -> bool:
    """Set up the switcher component."""
    from aioswitcher.bridge import SwitcherV2Bridge

    phone_id = config[DOMAIN][CONF_PHONE_ID]
    device_id = config[DOMAIN][CONF_DEVICE_ID]
    device_password = config[DOMAIN][CONF_DEVICE_PASSWORD]

    v2bridge = SwitcherV2Bridge(
        hass.loop, phone_id, device_id, device_password)

    await v2bridge.start()

    async def async_stop_bridge(event: EventType) -> None:
        """On homeassistant stop, gracefully stop the bridge if running."""
        await v2bridge.stop()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_stop_bridge)

    try:
        device_data = await wait_for(v2bridge.queue.get(), timeout=10.0)
    except (Asyncio_TimeoutError, RuntimeError):
        _LOGGER.exception("Failed to get response from device")
        await v2bridge.stop()
        return False

    hass.data[DOMAIN] = {
        DATA_DEVICE: device_data
    }

    async def async_switch_platform_discovered(
            platform: str, discovery_info: Optional[Dict]) -> None:
        """Use for registering services after switch platform is discoverd."""
        if platform != DOMAIN:
            return

        async def async_set_auto_off_service(service: ServiceCallType) -> None:
            """Use for handling setting device auto-off service calls."""
            from aioswitcher.api import SwitcherV2Api

            await _validate_edit_permission(
                hass, service.context, service.data[CONF_ENTITY_ID])

            async with SwitcherV2Api(hass.loop, device_data.ip_addr, phone_id,
                                     device_id, device_password) as swapi:
                await swapi.set_auto_shutdown(service.data[CONF_AUTO_OFF])

        hass.services.async_register(
            DOMAIN, SERVICE_SET_AUTO_OFF_NAME,
            async_set_auto_off_service,
            schema=SERVICE_SET_AUTO_OFF_SCHEMA)

    async_listen_platform(
        hass, SWITCH_DOMAIN, async_switch_platform_discovered)

    hass.async_create_task(async_load_platform(
        hass, SWITCH_DOMAIN, DOMAIN, {}, config))

    @callback
    def device_updates(timestamp: Optional[datetime]) -> None:
        """Use for updating the device data from the queue."""
        if v2bridge.running:
            try:
                device_new_data = v2bridge.queue.get_nowait()
                if device_new_data:
                    async_dispatcher_send(
                        hass, SIGNAL_SWITCHER_DEVICE_UPDATE, device_new_data)
            except QueueEmpty:
                pass

    async_track_time_interval(hass, device_updates, timedelta(seconds=4))

    return True
