"""Python library to connect deCONZ and Home Assistant to work together."""

import logging

from .deconzdevice import DeconzDevice

_LOGGER = logging.getLogger(__name__)


class DeconzSensor(DeconzDevice):
    """deCONZ sensor representation.

    Dresden Elektroniks documentation of sensors in deCONZ
    http://dresden-elektronik.github.io/deconz-rest-doc/sensors/
    """

    DECONZ_TYPE = '/sensors/'

    BINARY = None
    ZHATYPE = set()

    SENSOR_CLASS = None
    SENSOR_ICON = None
    SENSOR_UNIT = None

    @property
    def battery(self):
        """The battery status of the sensor."""
        return self.raw['config'].get('battery')

    @property
    def ep(self):
        """The Endpoint of the sensor."""
        return self.raw.get('ep')

    @property
    def lowbattery(self):
        """Low battery."""
        return self.raw['state'].get('lowbattery')

    @property
    def on(self):
        """Declare if the sensor is on or off."""
        return self.raw['config'].get('on')

    @property
    def reachable(self):
        """Declare if the sensor is reachable."""
        return self.raw['config'].get('reachable', True)

    @property
    def tampered(self):
        """Tampered."""
        return self.raw['state'].get('tampered')

    @property
    def secondary_temperature(self):
        """Extra temperature available on some Xiaomi devices."""
        if 'temperature' not in self.raw['config']:
            return None

        return Temperature.convert_temperature(
            self.raw['config'].get('temperature'))


class Alarm(DeconzSensor):
    """Alarm sensor."""

    BINARY = False
    ZHATYPE = ('ZHAAlarm',)

    SENSOR_CLASS = 'motion'

    @property
    def state(self):
        """Main state of sensor."""
        return self.alarm

    @property
    def alarm(self):
        """Alarm."""
        return self.raw['state'].get('alarm')


class CarbonMonoxide(DeconzSensor):
    """Carbon monoxide sensor."""

    BINARY = True
    ZHATYPE = ('ZHACarbonMonoxide',)

    SENSOR_CLASS = 'carbon_monoxide'

    @property
    def state(self):
        """Main state of sensor."""
        return self.is_tripped

    @property
    def carbonmonoxide(self):
        """Carbon monoxide detected."""
        return self.raw['state'].get('carbonmonoxide')

    @property
    def is_tripped(self):
        """Sensor is tripped."""
        return self.carbonmonoxide


class Consumption(DeconzSensor):
    """Power consumption sensor."""

    BINARY = False
    ZHATYPE = ('ZHAConsumption',)

    SENSOR_UNIT = 'kWh'

    @property
    def state(self):
        """Main state of sensor."""
        if self.consumption is None:
            return None

        return float(self.consumption/1000)

    @property
    def consumption(self):
        """Consumption."""
        return self.raw['state'].get('consumption')

    @property
    def power(self):
        """Power."""
        return self.raw['state'].get('power')


class Daylight(DeconzSensor):
    """Daylight sensor built into deCONZ software.

    Strings from daylight.h at
    https://github.com/dresden-elektronik/deconz-rest-plugin.
    Also has a 'daylight' boolean.
    Has no 'reachable' config parameter, so set sensor reachable True here.
    """

    BINARY = False
    ZHATYPE = ('Daylight',)

    SENSOR_CLASS = 'daylight'
    SENSOR_ICON = 'mdi:white-balance-sunny'

    @property
    def state(self):
        """Main state of sensor."""
        return self.status

    @property
    def configured(self):
        """Is daylight sensor configured."""
        return self.raw['config'].get('configured')

    @property
    def daylight(self):
        """True if daylight, false if not."""
        return self.raw['state'].get('daylight')

    @property
    def status(self):
        """Return the daylight status string."""
        status = self.raw['state'].get('status')
        if status == 100:
            return 'nadir'
        if status == 110:
            return 'night_end'
        if status == 120:
            return 'nautical_dawn'
        if status == 130:
            return 'dawn'
        if status == 140:
            return 'sunrise_start'
        if status == 150:
            return 'sunrise_end'
        if status == 160:
            return 'golden_hour_1'
        if status == 170:
            return 'solar_noon'
        if status == 180:
            return 'golden_hour_2'
        if status == 190:
            return 'sunset_start'
        if status == 200:
            return 'sunset_end'
        if status == 210:
            return 'dusk'
        if status == 220:
            return 'nautical_dusk'
        if status == 230:
            return 'night_start'
        return 'unknown'

    @property
    def sunriseoffset(self):
        """Sunrise offset."""
        return self.raw['config'].get('sunriseoffset')

    @property
    def sunsetoffset(self):
        """Sunset offset."""
        return self.raw['config'].get('sunsetoffset')


class Fire(DeconzSensor):
    """Fire sensor."""

    BINARY = True
    ZHATYPE = ('ZHAFire',)

    SENSOR_CLASS = 'smoke'

    @property
    def state(self):
        """Main state of sensor."""
        return self.is_tripped

    @property
    def fire(self):
        """Fire detected."""
        return self.raw['state'].get('fire')

    @property
    def is_tripped(self):
        """Sensor is tripped."""
        return self.fire


class GenericFlag(DeconzSensor):
    """Generic flag sensor."""

    BINARY = True
    ZHATYPE = ('CLIPGenericFlag',)

    @property
    def state(self):
        """Main state of sensor."""
        return self.flag

    @property
    def flag(self):
        """Flag status."""
        return self.raw['state'].get('flag')

    @property
    def is_tripped(self):
        """Sensor is tripped."""
        return self.flag


class GenericStatus(DeconzSensor):
    """Generic status sensor."""

    BINARY = False
    ZHATYPE = ('CLIPGenericStatus',)

    @property
    def state(self):
        """Main state of sensor."""
        return self.status

    @property
    def status(self):
        """Status."""
        return self.raw['state'].get('status')


class Humidity(DeconzSensor):
    """Humidity sensor."""

    BINARY = False
    ZHATYPE = ('ZHAHumidity', 'CLIPHumidity')

    SENSOR_CLASS = 'humidity'
    SENSOR_UNIT = '%'

    @property
    def state(self):
        """Main state of sensor."""
        if self.humidity is None:
            return None

        return round(float(self.humidity) / 100, 1)

    @property
    def humidity(self):
        """Humidity level."""
        return self.raw['state'].get('humidity')


class LightLevel(DeconzSensor):
    """Light level sensor."""

    BINARY = False
    ZHATYPE = ('ZHALightLevel', 'CLIPLightLevel')

    SENSOR_CLASS = 'illuminance'
    SENSOR_UNIT = 'lux'

    @property
    def state(self):
        """Main state of sensor."""
        if self.lightlevel is None:
            return None

        return round(10 ** (float(self.lightlevel - 1) / 10000), 1)

    @property
    def dark(self):
        """If the area near the sensor is light or not."""
        return self.raw['state'].get('dark')

    @property
    def daylight(self):
        """Daylight."""
        return self.raw['state'].get('daylight')

    @property
    def lightlevel(self):
        """Light level."""
        return self.raw['state'].get('lightlevel')

    @property
    def lux(self):
        """Lux."""
        return self.raw['state'].get('lux')

    @property
    def tholddark(self):
        """Threshold to hold dark."""
        return self.raw['config'].get('tholddark')

    @property
    def tholdoffset(self):
        """Offset for threshold to hold dark."""
        return self.raw['config'].get('tholdoffset')


class OpenClose(DeconzSensor):
    """Door/Window sensor."""

    BINARY = True
    ZHATYPE = ('ZHAOpenClose', 'CLIPOpenClose')

    SENSOR_CLASS = 'opening'

    @property
    def state(self):
        """Main state of sensor."""
        return self.is_tripped

    @property
    def is_tripped(self):
        """Sensor is tripped."""
        return self.open

    @property
    def open(self):
        """Door open."""
        return self.raw['state'].get('open')


class Power(DeconzSensor):
    """Power sensor."""

    BINARY = False
    ZHATYPE = ('ZHAPower',)

    SENSOR_UNIT = 'W'

    @property
    def state(self):
        """Main state of sensor."""
        return self.power

    @property
    def current(self):
        """Current."""
        return self.raw['state'].get('current')

    @property
    def power(self):
        """Power."""
        return self.raw['state'].get('power')

    @property
    def voltage(self):
        """Voltage."""
        return self.raw['state'].get('voltage')


class Presence(DeconzSensor):
    """Presence detector."""

    BINARY = True
    ZHATYPE = ('ZHAPresence', 'CLIPPresence')

    SENSOR_CLASS = 'motion'

    @property
    def state(self):
        """Main state of sensor."""
        return self.is_tripped

    @property
    def dark(self):
        """If the area near the sensor is light or not."""
        return self.raw['state'].get('dark')

    @property
    def duration(self):
        """Minimum duration which presence will be true."""
        return self.raw['config'].get('duration')

    @property
    def is_tripped(self):
        """Sensor is tripped."""
        return self.presence

    @property
    def presence(self):
        """Motion detected."""
        return self.raw['state'].get('presence')


class Pressure(DeconzSensor):
    """Pressure sensor."""

    BINARY = False
    ZHATYPE = ('ZHAPressure', 'CLIPPressure')

    SENSOR_CLASS = 'pressure'
    SENSOR_ICON = 'mdi:gauge'
    SENSOR_UNIT = 'hPa'

    @property
    def state(self):
        """Main state of sensor."""
        return self.pressure

    @property
    def pressure(self):
        """Pressure."""
        return self.raw['state'].get('pressure')


class Switch(DeconzSensor):
    """Switch sensor."""

    BINARY = False
    ZHATYPE = ('ZHASwitch', 'ZGPSwitch', 'CLIPSwitch')

    @property
    def state(self):
        """Main state of switch."""
        return self.buttonevent

    @property
    def buttonevent(self):
        """Button press."""
        return self.raw['state'].get('buttonevent')


class Temperature(DeconzSensor):
    """Temperature sensor."""

    BINARY = False
    ZHATYPE = ('ZHATemperature', 'CLIPTemperature')

    SENSOR_CLASS = 'temperature'
    SENSOR_ICON = 'mdi:thermometer'
    SENSOR_UNIT = '°C'

    @property
    def state(self):
        """Main state of sensor."""
        return self.temperature

    @property
    def temperature(self):
        """Temperature."""
        return self.convert_temperature(self.raw['state'].get('temperature'))

    @staticmethod
    def convert_temperature(temperature):
        """Convert temperature to celsius"""
        if temperature is None:
            return None

        return round(float(temperature) / 100, 1)


class Thermostat(Temperature):
    """Thermostat "sensor"."""

    BINARY = False
    ZHATYPE = ('ZHAThermostat', 'CLIPThermostat')

    @property
    def heatsetpoint(self):
        """Heating setpoint"""
        return self.convert_temperature(self.raw['config'].get('heatsetpoint'))

    @property
    def locked(self):
        """"""
        return self.raw['config'].get('locked')

    @property
    def mode(self):
        """Thermostat mode; "off", "heat", and "auto"."""
        return self.raw['config'].get('mode')

    @property
    def offset(self):
        """Temperature offset."""
        return self.raw['config'].get('offset')

    @property
    def state_on(self):
        """Declare if the sensor is on or off."""
        return self.raw['state'].get('on')

    @property
    def valve(self):
        """How open the valve is."""
        return self.raw['state'].get('valve')


class Vibration(DeconzSensor):
    """Vibration sensor."""

    BINARY = True
    ZHATYPE = ('ZHAVibration',)

    SENSOR_CLASS = 'motion'

    @property
    def state(self):
        """Main state of sensor."""
        return self.is_tripped

    @property
    def is_tripped(self) -> bool:
        """Sensor is tripped."""
        return self.vibration

    @property
    def orientation(self) -> list:
        """Orientation."""
        return self.raw['state'].get('orientation')

    @property
    def sensitivity(self) -> int:
        """Configured sensitivity"""
        return self.raw['config'].get('sensitivity')

    @property
    def sensitivitymax(self) -> int:
        """Configured max sensitivity."""
        return self.raw['config'].get('sensitivitymax')

    @property
    def tiltangle(self) -> int:
        """Tilt angle."""
        return self.raw['state'].get('tiltangle')

    @property
    def vibration(self) -> bool:
        """Vibration."""
        return self.raw['state'].get('vibration')

    @property
    def vibrationstrength(self) -> int:
        """Strength of vibration."""
        return self.raw['state'].get('vibrationstrength')


class Water(DeconzSensor):
    """Water sensor."""

    BINARY = True
    ZHATYPE = ('ZHAWater',)

    SENSOR_CLASS = 'moisture'

    @property
    def state(self):
        """Main state of sensor."""
        return self.is_tripped

    @property
    def is_tripped(self):
        """Sensor is tripped."""
        return self.water

    @property
    def water(self):
        """Water detected."""
        return self.raw['state'].get('water')


SENSOR_CLASSES = (
    Alarm, CarbonMonoxide, Consumption, Daylight, Fire, GenericFlag,
    GenericStatus, Humidity, LightLevel, OpenClose, Power, Presence, Pressure,
    Switch, Temperature, Thermostat, Vibration, Water)


def create_sensor(sensor_id, raw, loop, async_set_state_callback):
    """Simplify creating sensor by not needing to know type."""
    for sensor_class in SENSOR_CLASSES:
        if raw['type'] in sensor_class.ZHATYPE:
            return sensor_class(
                sensor_id, raw, loop, async_set_state_callback)


def supported_sensor(raw):
    """Check if sensor is supported by pydeconz."""
    for sensor_class in SENSOR_CLASSES:
        if raw['type'] in sensor_class.ZHATYPE:
            return True

    _LOGGER.info('Unsupported sensor type %s (%s)',
                 raw['type'], raw['name'])
    return False
