"""The awair component."""

from asyncio import gather
from typing import Any, Optional

from async_timeout import timeout
from python_awair import Awair
from python_awair.exceptions import AuthError

from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import API_TIMEOUT, DOMAIN, LOGGER, UPDATE_INTERVAL, AwairResult

PLATFORMS = ["sensor"]


async def async_setup(hass: HomeAssistant, config: Config) -> bool:
    """Set up Awair integration."""
    return True


async def async_setup_entry(hass, config_entry) -> bool:
    """Set up Awair integration from a config entry."""
    session = async_get_clientsession(hass)
    coordinator = AwairDataUpdateCoordinator(hass, config_entry, session)

    await coordinator.async_refresh()

    if not coordinator.last_update_success:
        raise ConfigEntryNotReady

    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][config_entry.entry_id] = coordinator

    for platform in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(config_entry, platform)
        )

    return True


async def async_unload_entry(hass, config_entry) -> bool:
    """Unload Awair configuration."""
    tasks = []
    for platform in PLATFORMS:
        tasks.append(
            hass.config_entries.async_forward_entry_unload(config_entry, platform)
        )

    unload_ok = all(await gather(*tasks))
    if unload_ok:
        hass.data[DOMAIN].pop(config_entry.entry_id)

    return unload_ok


class AwairDataUpdateCoordinator(DataUpdateCoordinator):
    """Define a wrapper class to update Awair data."""

    def __init__(self, hass, config_entry, session) -> None:
        """Set up the AwairDataUpdateCoordinator class."""
        access_token = config_entry.data[CONF_ACCESS_TOKEN]
        self._awair = Awair(access_token=access_token, session=session)
        self._config_entry = config_entry

        super().__init__(hass, LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL)

    async def _async_update_data(self) -> Optional[Any]:
        """Update data via Awair client library."""
        with timeout(API_TIMEOUT):
            try:
                LOGGER.debug("Fetching users and devices")
                user = await self._awair.user()
                devices = await user.devices()
                results = await gather(
                    *[self._fetch_air_data(device) for device in devices]
                )
                return {result.device.uuid: result for result in results}
            except AuthError as err:
                flow_context = {
                    "source": "reauth",
                    "unique_id": self._config_entry.unique_id,
                }

                matching_flows = [
                    flow
                    for flow in self.hass.config_entries.flow.async_progress()
                    if flow["context"] == flow_context
                ]

                if not matching_flows:
                    self.hass.async_create_task(
                        self.hass.config_entries.flow.async_init(
                            DOMAIN, context=flow_context, data=self._config_entry.data,
                        )
                    )

                raise UpdateFailed(err)
            except Exception as err:
                raise UpdateFailed(err)

    async def _fetch_air_data(self, device):
        """Fetch latest air quality data."""
        LOGGER.debug("Fetching data for %s", device.uuid)
        air_data = await device.air_data_latest()
        LOGGER.debug(air_data)
        return AwairResult(device=device, air_data=air_data)
