"""Sensor that can display the current Home Assistant versions."""
from datetime import timedelta
import logging

from pyhaversion import (
    HaVersion,
    HaVersionChannel,
    HaVersionSource,
    exceptions as pyhaversionexceptions,
)
import voluptuous as vol

from homeassistant.components.sensor import (
    PLATFORM_SCHEMA,
    SensorEntity,
    SensorEntityDescription,
)
from homeassistant.const import CONF_NAME, CONF_SOURCE
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle

ALL_IMAGES = [
    "default",
    "intel-nuc",
    "odroid-c2",
    "odroid-n2",
    "odroid-xu",
    "qemuarm-64",
    "qemuarm",
    "qemux86-64",
    "qemux86",
    "raspberrypi",
    "raspberrypi2",
    "raspberrypi3-64",
    "raspberrypi3",
    "raspberrypi4-64",
    "raspberrypi4",
    "tinker",
]

HA_VERSION_SOURCES = [source.value for source in HaVersionSource]

ALL_SOURCES = HA_VERSION_SOURCES + [
    "hassio",  # Kept to not break existing configurations
    "docker",  # Kept to not break existing configurations
]

CONF_BETA = "beta"
CONF_IMAGE = "image"

DEFAULT_IMAGE = "default"
DEFAULT_NAME_LATEST = "Latest Version"
DEFAULT_NAME_LOCAL = "Current Version"
DEFAULT_SOURCE = "local"

TIME_BETWEEN_UPDATES = timedelta(minutes=5)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Optional(CONF_BETA, default=False): cv.boolean,
        vol.Optional(CONF_IMAGE, default=DEFAULT_IMAGE): vol.In(ALL_IMAGES),
        vol.Optional(CONF_NAME, default=""): cv.string,
        vol.Optional(CONF_SOURCE, default=DEFAULT_SOURCE): vol.In(ALL_SOURCES),
    }
)

_LOGGER: logging.Logger = logging.getLogger(__name__)


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    """Set up the Version sensor platform."""

    beta = config.get(CONF_BETA)
    image = config.get(CONF_IMAGE)
    name = config.get(CONF_NAME)
    source = config.get(CONF_SOURCE)

    channel = HaVersionChannel.BETA if beta else HaVersionChannel.STABLE
    session = async_get_clientsession(hass)

    if source in HA_VERSION_SOURCES:
        source = HaVersionSource(source)
    elif source == "hassio":
        source = HaVersionSource.SUPERVISOR
    elif source == "docker":
        source = HaVersionSource.CONTAINER

    if (
        source == HaVersionSource.CONTAINER
        and image is not None
        and image != DEFAULT_IMAGE
    ):
        image = f"{image}-homeassistant"

    if not (name := config.get(CONF_NAME)):
        if source == HaVersionSource.LOCAL:
            name = DEFAULT_NAME_LOCAL
        else:
            name = DEFAULT_NAME_LATEST

    async_add_entities(
        [
            VersionSensor(
                VersionData(
                    HaVersion(
                        session=session, source=source, image=image, channel=channel
                    )
                ),
                SensorEntityDescription(key=source, name=name),
            )
        ],
        True,
    )


class VersionData:
    """Get the latest data and update the states."""

    def __init__(self, api: HaVersion) -> None:
        """Initialize the data object."""
        self.api = api

    @Throttle(TIME_BETWEEN_UPDATES)
    async def async_update(self):
        """Get the latest version information."""
        try:
            await self.api.get_version()
        except pyhaversionexceptions.HaVersionFetchException as exception:
            _LOGGER.warning(exception)
        except pyhaversionexceptions.HaVersionParseException as exception:
            _LOGGER.warning(
                "Could not parse data received for %s - %s", self.api.source, exception
            )


class VersionSensor(SensorEntity):
    """Representation of a Home Assistant version sensor."""

    _attr_icon = "mdi:package-up"

    def __init__(
        self,
        data: VersionData,
        description: SensorEntityDescription,
    ) -> None:
        """Initialize the Version sensor."""
        self.data = data
        self.entity_description = description

    async def async_update(self):
        """Get the latest version information."""
        await self.data.async_update()
        self._attr_native_value = self.data.api.version
        self._attr_extra_state_attributes = self.data.api.version_data