import logging

from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME, DEGREE, UnitOfLength
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity, SensorStateClass
from homeassistant.helpers.device_registry import DeviceEntryType

from .const import (
    ATTR_LAT,
    ATTR_LIGHTNING_AZIMUTH,
    ATTR_LIGHTNING_COUNTER,
    ATTR_LON,
    ATTRIBUTION,
    DOMAIN,
    SERVER_STATS,
    SW_VERSION,
)

ATTR_ICON = "icon"
ATTR_LABEL = "label"
ATTR_UNIT = "unit"
ATTR_LIGHTNING_PROPERTY = "lightning_prop"

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass, config_entry, async_add_entities):
    integration_name = config_entry.data[CONF_NAME]

    coordinator = hass.data[DOMAIN][config_entry.entry_id]

    unique_prefix = config_entry.unique_id

    sensors = [
        klass(coordinator, integration_name, unique_prefix)
        for klass in (DistanceSensor, AzimuthSensor, CounterSensor)
    ]

    async_add_entities(sensors, False)

    config = hass.data[DOMAIN].get("config") or {}
    if config.get(SERVER_STATS):
        server_stat_sensors = {}

        def on_message(message):
            if not message.topic.startswith("$SYS/broker/"):
                return
            topic = message.topic.replace("$SYS/broker/", "")
            if topic.startswith("load") and not topic.endswith("/1min"):
                return
            if topic.startswith("clients") and topic != "clients/connected":
                return
            sensor = server_stat_sensors.get(topic)
            if not sensor:
                sensor = ServerStatSensor(
                    topic, coordinator, integration_name, unique_prefix
                )
                server_stat_sensors[topic] = sensor
                async_add_entities([sensor], False)
            sensor.on_message(topic, message)

        coordinator.register_message_receiver(on_message)


class BlitzortungSensor(SensorEntity):
    """Define a Blitzortung sensor."""

    def __init__(self, coordinator, integration_name, unique_prefix):
        """Initialize."""
        self.coordinator = coordinator
        self._integration_name = integration_name
        self.entity_id = f"sensor.{integration_name}-{self.name}"
        self._unique_id = f"{unique_prefix}-{self.kind}"
        self._device_class = None
        self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}

    should_poll = False
    icon = "mdi:flash"
    device_class = None

    @property
    def available(self):
        return self.coordinator.is_connected

    @property
    def label(self):
        return self.kind.capitalize()

    @property
    def name(self):
        """Return the name."""
        return f"Lightning {self.label}"

    @property
    def extra_state_attributes(self):
        """Return the state attributes."""
        return self._attrs

    @property
    def unique_id(self):
        """Return a unique_id for this entity."""
        return self._unique_id

    async def async_added_to_hass(self):
        """Connect to dispatcher listening for entity data notifications."""
        # self.async_on_remove(self.coordinator.async_add_listener(self._update_sensor))
        self.coordinator.register_sensor(self)

    async def async_update(self):
        await self.coordinator.async_request_refresh()

    @property
    def device_info(self):
        return {
            "name": f"{self._integration_name} Lightning Detector",
            "identifiers": {(DOMAIN, self._integration_name)},
            "model": "Lightning Detector",
            "sw_version": SW_VERSION,
            "entry_type": DeviceEntryType.SERVICE,
        }

    def update_lightning(self, lightning):
        pass

    def on_message(self, message):
        pass

    def tick(self):
        pass


class LightningSensor(BlitzortungSensor):
    INITIAL_STATE = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._attr_native_value = self.INITIAL_STATE

    def tick(self):
        if self._attr_native_value != self.INITIAL_STATE and self.coordinator.is_inactive:
            self._attr_native_value = self.INITIAL_STATE
            self.async_write_ha_state()


class DistanceSensor(LightningSensor):
    kind = SensorDeviceClass.DISTANCE
    device_class = SensorDeviceClass.DISTANCE
    state_class = SensorStateClass.MEASUREMENT
    _attr_native_unit_of_measurement = UnitOfLength.KILOMETERS

    def update_lightning(self, lightning):
        self._attr_native_value = lightning["distance"]
        self._attrs[ATTR_LAT] = lightning[ATTR_LAT]
        self._attrs[ATTR_LON] = lightning[ATTR_LON]
        self.async_write_ha_state()


class AzimuthSensor(LightningSensor):
    kind = ATTR_LIGHTNING_AZIMUTH
    _attr_native_unit_of_measurement = DEGREE

    def update_lightning(self, lightning):
        self._attr_native_value = lightning["azimuth"]
        self._attrs[ATTR_LAT] = lightning[ATTR_LAT]
        self._attrs[ATTR_LON] = lightning[ATTR_LON]
        self.async_write_ha_state()


class CounterSensor(LightningSensor):
    kind = ATTR_LIGHTNING_COUNTER
    _attr_native_unit_of_measurement = "↯"
    INITIAL_STATE = 0

    def update_lightning(self, lightning):
        self._attr_native_value = self._attr_native_value + 1
        self.async_write_ha_state()


class ServerStatSensor(BlitzortungSensor):
    def __init__(self, topic, coordinator, integration_name, unique_prefix):
        self._topic = topic

        topic_parts = topic.replace("$SYS/broker/", "").split("/")
        self.kind = "_".join(topic_parts)
        if self.kind.startswith("load"):
            self.data_type = float
        elif self.kind in ("uptime", "version"):
            self.data_type = str
        else:
            self.data_type = int

        if self.kind == "clients_connected":
            self.kind = "server_stats"

        self._name = " ".join(part.capitalize() for part in topic_parts)

        super().__init__(coordinator, integration_name, unique_prefix)

    @property
    def unit_of_measurement(self):
        if self.data_type in (int, float):
            return "." if self.kind == "server_stats" else " "
        else:
            return None

    @classmethod
    def for_topic(cls, topic, coordinator, integration_name, unique_prefix):
        return cls(topic, coordinator, integration_name, unique_prefix)

    def on_message(self, topic, message):
        if topic == self._topic:
            payload = message.payload.decode("utf-8")
            try:
                self._attr_native_value = self.data_type(payload)
            except ValueError:
                self._attr_native_value = str(payload)
            if self.hass:
                self.async_write_ha_state()

    @property
    def label(self):
        return self._name

    @property
    def name(self):
        return self._name
