"""The Legrand Home+ Control integration."""
import asyncio
from datetime import timedelta
import logging

from homepluscontrol.homeplusapi import HomePlusControlApiError
import voluptuous as vol

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
    config_entry_oauth2_flow,
    config_validation as cv,
    dispatcher,
)
from homeassistant.helpers.device_registry import async_get as async_get_device_registry
from homeassistant.helpers.issue_registry import (
    IssueSeverity,
    async_create_issue,
    async_delete_issue,
)
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from . import config_flow, helpers
from .api import HomePlusControlAsyncApi
from .const import (
    API,
    CONF_SUBSCRIPTION_KEY,
    DATA_COORDINATOR,
    DISPATCHER_REMOVERS,
    DOMAIN,
    ENTITY_UIDS,
    SIGNAL_ADD_ENTITIES,
)

# Configuration schema for component in configuration.yaml
CONFIG_SCHEMA = vol.Schema(
    {
        DOMAIN: vol.Schema(
            {
                vol.Required(CONF_CLIENT_ID): cv.string,
                vol.Required(CONF_CLIENT_SECRET): cv.string,
                vol.Required(CONF_SUBSCRIPTION_KEY): cv.string,
            }
        )
    },
    extra=vol.ALLOW_EXTRA,
)

# The Legrand Home+ Control platform is currently limited to "switch" entities
PLATFORMS = [Platform.SWITCH]

_LOGGER = logging.getLogger(__name__)

_ISSUE_MOTE_TO_NETAMO = "move_to_netamo"


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Legrand Home+ Control component from configuration.yaml."""
    hass.data[DOMAIN] = {}

    if DOMAIN not in config:
        return True

    async_create_issue(
        hass,
        DOMAIN,
        _ISSUE_MOTE_TO_NETAMO,
        is_fixable=False,
        is_persistent=False,
        breaks_in_ha_version="2023.12.0",  # Netamo decided to shutdown the api in december
        severity=IssueSeverity.WARNING,
        translation_key=_ISSUE_MOTE_TO_NETAMO,
        translation_placeholders={
            "url": "https://www.home-assistant.io/integrations/netatmo/"
        },
    )

    # Register the implementation from the config information
    config_flow.HomePlusControlFlowHandler.async_register_implementation(
        hass,
        helpers.HomePlusControlOAuth2Implementation(hass, config[DOMAIN]),
    )

    return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Legrand Home+ Control from a config entry."""
    hass_entry_data = hass.data[DOMAIN].setdefault(entry.entry_id, {})

    async_create_issue(
        hass,
        DOMAIN,
        _ISSUE_MOTE_TO_NETAMO,
        is_fixable=False,
        is_persistent=False,
        breaks_in_ha_version="2023.12.0",  # Netamo decided to shutdown the api in december
        severity=IssueSeverity.WARNING,
        translation_key=_ISSUE_MOTE_TO_NETAMO,
        translation_placeholders={
            "url": "https://www.home-assistant.io/integrations/netatmo/"
        },
    )

    # Retrieve the registered implementation
    implementation = (
        await config_entry_oauth2_flow.async_get_config_entry_implementation(
            hass, entry
        )
    )

    # Using an aiohttp-based API lib, so rely on async framework
    # Add the API object to the domain's data in HA
    api = hass_entry_data[API] = HomePlusControlAsyncApi(hass, entry, implementation)

    # Set of entity unique identifiers of this integration
    uids: set[str] = set()
    hass_entry_data[ENTITY_UIDS] = uids

    # Integration dispatchers
    hass_entry_data[DISPATCHER_REMOVERS] = []

    device_registry = async_get_device_registry(hass)

    # Register the Data Coordinator with the integration
    async def async_update_data():
        """Fetch data from API endpoint.

        This is the place to pre-process the data to lookup tables
        so entities can quickly look up their data.
        """
        try:
            # Note: asyncio.TimeoutError and aiohttp.ClientError are already
            # handled by the data update coordinator.
            async with asyncio.timeout(10):
                return await api.async_get_modules()
        except HomePlusControlApiError as err:
            raise UpdateFailed(
                f"Error communicating with API: {err} [{type(err)}]"
            ) from err

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        # Name of the data. For logging purposes.
        name="home_plus_control_module",
        update_method=async_update_data,
        # Polling interval. Will only be polled if there are subscribers.
        update_interval=timedelta(seconds=300),
    )
    hass_entry_data[DATA_COORDINATOR] = coordinator

    @callback
    def _async_update_entities():
        """Process entities and add or remove them based after an update."""
        if not (module_data := coordinator.data):
            return

        # Remove obsolete entities from Home Assistant
        entity_uids_to_remove = uids - set(module_data)
        for uid in entity_uids_to_remove:
            uids.remove(uid)
            device = device_registry.async_get_device(identifiers={(DOMAIN, uid)})
            device_registry.async_remove_device(device.id)

        # Send out signal for new entity addition to Home Assistant
        new_entity_uids = set(module_data) - uids
        if new_entity_uids:
            uids.update(new_entity_uids)
            dispatcher.async_dispatcher_send(
                hass,
                SIGNAL_ADD_ENTITIES,
                new_entity_uids,
                coordinator,
            )

    entry.async_on_unload(coordinator.async_add_listener(_async_update_entities))

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

    # Only refresh the coordinator after all platforms are loaded.
    await coordinator.async_refresh()

    return True


async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
    """Unload the Legrand Home+ Control config entry."""
    unload_ok = await hass.config_entries.async_unload_platforms(
        config_entry, PLATFORMS
    )
    if unload_ok:
        # Unsubscribe the config_entry signal dispatcher connections
        dispatcher_removers = hass.data[DOMAIN][config_entry.entry_id].pop(
            "dispatcher_removers"
        )
        for remover in dispatcher_removers:
            remover()

        # And finally unload the domain config entry data
        hass.data[DOMAIN].pop(config_entry.entry_id)

    async_delete_issue(hass, DOMAIN, _ISSUE_MOTE_TO_NETAMO)

    return unload_ok
