"""Support for the Philips Hue system."""
import asyncio
import logging

from aiohue.util import normalize_bridge_id
import voluptuous as vol

from homeassistant import config_entries, core
from homeassistant.components import persistent_notification
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.service import verify_domain_control

from .bridge import HueBridge
from .const import (
    ATTR_GROUP_NAME,
    ATTR_SCENE_NAME,
    ATTR_TRANSITION,
    CONF_ALLOW_HUE_GROUPS,
    CONF_ALLOW_UNREACHABLE,
    DEFAULT_ALLOW_HUE_GROUPS,
    DEFAULT_ALLOW_UNREACHABLE,
    DOMAIN,
)

_LOGGER = logging.getLogger(__name__)
SERVICE_HUE_SCENE = "hue_activate_scene"


async def async_setup_entry(
    hass: core.HomeAssistant, entry: config_entries.ConfigEntry
):
    """Set up a bridge from a config entry."""

    # Migrate allow_unreachable from config entry data to config entry options
    if (
        CONF_ALLOW_UNREACHABLE not in entry.options
        and CONF_ALLOW_UNREACHABLE in entry.data
        and entry.data[CONF_ALLOW_UNREACHABLE] != DEFAULT_ALLOW_UNREACHABLE
    ):
        options = {
            **entry.options,
            CONF_ALLOW_UNREACHABLE: entry.data[CONF_ALLOW_UNREACHABLE],
        }
        data = entry.data.copy()
        data.pop(CONF_ALLOW_UNREACHABLE)
        hass.config_entries.async_update_entry(entry, data=data, options=options)

    # Migrate allow_hue_groups from config entry data to config entry options
    if (
        CONF_ALLOW_HUE_GROUPS not in entry.options
        and CONF_ALLOW_HUE_GROUPS in entry.data
        and entry.data[CONF_ALLOW_HUE_GROUPS] != DEFAULT_ALLOW_HUE_GROUPS
    ):
        options = {
            **entry.options,
            CONF_ALLOW_HUE_GROUPS: entry.data[CONF_ALLOW_HUE_GROUPS],
        }
        data = entry.data.copy()
        data.pop(CONF_ALLOW_HUE_GROUPS)
        hass.config_entries.async_update_entry(entry, data=data, options=options)

    bridge = HueBridge(hass, entry)

    if not await bridge.async_setup():
        return False

    _register_services(hass)

    config = bridge.api.config

    # For backwards compat
    unique_id = normalize_bridge_id(config.bridgeid)
    if entry.unique_id is None:
        hass.config_entries.async_update_entry(entry, unique_id=unique_id)

    # For recovering from bug where we incorrectly assumed homekit ID = bridge ID
    elif entry.unique_id != unique_id:
        # Find entries with this unique ID
        other_entry = next(
            (
                entry
                for entry in hass.config_entries.async_entries(DOMAIN)
                if entry.unique_id == unique_id
            ),
            None,
        )

        if other_entry is None:
            # If no other entry, update unique ID of this entry ID.
            hass.config_entries.async_update_entry(entry, unique_id=unique_id)

        elif other_entry.source == config_entries.SOURCE_IGNORE:
            # There is another entry but it is ignored, delete that one and update this one
            hass.async_create_task(
                hass.config_entries.async_remove(other_entry.entry_id)
            )
            hass.config_entries.async_update_entry(entry, unique_id=unique_id)
        else:
            # There is another entry that already has the right unique ID. Delete this entry
            hass.async_create_task(hass.config_entries.async_remove(entry.entry_id))
            return False

    device_registry = await dr.async_get_registry(hass)
    device_registry.async_get_or_create(
        config_entry_id=entry.entry_id,
        connections={(dr.CONNECTION_NETWORK_MAC, config.mac)},
        identifiers={(DOMAIN, config.bridgeid)},
        manufacturer="Signify",
        name=config.name,
        model=config.modelid,
        sw_version=config.swversion,
    )

    if config.modelid == "BSB002" and config.swversion < "1935144040":
        persistent_notification.async_create(
            hass,
            "Your Hue hub has a known security vulnerability ([CVE-2020-6007](https://cve.circl.lu/cve/CVE-2020-6007)). Go to the Hue app and check for software updates.",
            "Signify Hue",
            "hue_hub_firmware",
        )

    elif config.swupdate2_bridge_state == "readytoinstall":
        err = (
            "Please check for software updates of the bridge in the Philips Hue App.",
            "Signify Hue",
            "hue_hub_firmware",
        )
        _LOGGER.warning(err)

    return True


async def async_unload_entry(hass, entry):
    """Unload a config entry."""
    unload_success = await hass.data[DOMAIN][entry.entry_id].async_reset()
    if len(hass.data[DOMAIN]) == 0:
        hass.data.pop(DOMAIN)
        hass.services.async_remove(DOMAIN, SERVICE_HUE_SCENE)
    return unload_success


@core.callback
def _register_services(hass):
    """Register Hue services."""

    async def hue_activate_scene(call, skip_reload=True):
        """Handle activation of Hue scene."""
        # Get parameters
        group_name = call.data[ATTR_GROUP_NAME]
        scene_name = call.data[ATTR_SCENE_NAME]

        # Call the set scene function on each bridge
        tasks = [
            bridge.hue_activate_scene(
                call.data, skip_reload=skip_reload, hide_warnings=skip_reload
            )
            for bridge in hass.data[DOMAIN].values()
            if isinstance(bridge, HueBridge)
        ]
        results = await asyncio.gather(*tasks)

        # Did *any* bridge succeed? If not, refresh / retry
        # Note that we'll get a "None" value for a successful call
        if None not in results:
            if skip_reload:
                await hue_activate_scene(call, skip_reload=False)
                return
            _LOGGER.warning(
                "No bridge was able to activate " "scene %s in group %s",
                scene_name,
                group_name,
            )

    if not hass.services.has_service(DOMAIN, SERVICE_HUE_SCENE):
        # Register a local handler for scene activation
        hass.services.async_register(
            DOMAIN,
            SERVICE_HUE_SCENE,
            verify_domain_control(hass, DOMAIN)(hue_activate_scene),
            schema=vol.Schema(
                {
                    vol.Required(ATTR_GROUP_NAME): cv.string,
                    vol.Required(ATTR_SCENE_NAME): cv.string,
                    vol.Optional(ATTR_TRANSITION): cv.positive_int,
                }
            ),
        )
