"""Support for 1-Wire environment sensors."""
from __future__ import annotations

from collections.abc import Callable, Mapping
import copy
from dataclasses import dataclass
import logging
import os
from types import MappingProxyType
from typing import Any

from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorEntity,
    SensorEntityDescription,
    SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    ELECTRIC_POTENTIAL_VOLT,
    LIGHT_LUX,
    PERCENTAGE,
    PRESSURE_CBAR,
    PRESSURE_MBAR,
    TEMP_CELSIUS,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType

from .const import (
    DEVICE_KEYS_0_3,
    DEVICE_KEYS_A_B,
    DOMAIN,
    OPTION_ENTRY_DEVICE_OPTIONS,
    OPTION_ENTRY_SENSOR_PRECISION,
    PRECISION_MAPPING_FAMILY_28,
    READ_MODE_FLOAT,
    READ_MODE_INT,
)
from .onewire_entities import OneWireEntity, OneWireEntityDescription
from .onewirehub import OneWireHub


@dataclass
class OneWireSensorEntityDescription(OneWireEntityDescription, SensorEntityDescription):
    """Class describing OneWire sensor entities."""

    override_key: Callable[[str, Mapping[str, Any]], str] | None = None


def _get_sensor_precision_family_28(device_id: str, options: Mapping[str, Any]) -> str:
    """Get precision form config flow options."""
    precision: str = (
        options.get(OPTION_ENTRY_DEVICE_OPTIONS, {})
        .get(device_id, {})
        .get(OPTION_ENTRY_SENSOR_PRECISION, "temperature")
    )
    if precision in PRECISION_MAPPING_FAMILY_28:
        return precision
    _LOGGER.warning(
        "Invalid sensor precision `%s` for device `%s`: reverting to default",
        precision,
        device_id,
    )
    return "temperature"


SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION = OneWireSensorEntityDescription(
    key="temperature",
    device_class=SensorDeviceClass.TEMPERATURE,
    name="Temperature",
    native_unit_of_measurement=TEMP_CELSIUS,
    read_mode=READ_MODE_FLOAT,
    state_class=SensorStateClass.MEASUREMENT,
)

_LOGGER = logging.getLogger(__name__)


DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
    "10": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
    "12": (
        OneWireSensorEntityDescription(
            key="TAI8570/temperature",
            device_class=SensorDeviceClass.TEMPERATURE,
            entity_registry_enabled_default=False,
            name="Temperature",
            native_unit_of_measurement=TEMP_CELSIUS,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="TAI8570/pressure",
            device_class=SensorDeviceClass.PRESSURE,
            entity_registry_enabled_default=False,
            name="Pressure",
            native_unit_of_measurement=PRESSURE_MBAR,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
    ),
    "22": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
    "26": (
        SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,
        OneWireSensorEntityDescription(
            key="humidity",
            device_class=SensorDeviceClass.HUMIDITY,
            entity_registry_enabled_default=False,
            name="Humidity",
            native_unit_of_measurement=PERCENTAGE,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="HIH3600/humidity",
            device_class=SensorDeviceClass.HUMIDITY,
            entity_registry_enabled_default=False,
            name="Humidity HIH3600",
            native_unit_of_measurement=PERCENTAGE,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="HIH4000/humidity",
            device_class=SensorDeviceClass.HUMIDITY,
            entity_registry_enabled_default=False,
            name="Humidity HIH4000",
            native_unit_of_measurement=PERCENTAGE,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="HIH5030/humidity",
            device_class=SensorDeviceClass.HUMIDITY,
            entity_registry_enabled_default=False,
            name="Humidity HIH5030",
            native_unit_of_measurement=PERCENTAGE,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="HTM1735/humidity",
            device_class=SensorDeviceClass.HUMIDITY,
            entity_registry_enabled_default=False,
            name="Humidity HTM1735",
            native_unit_of_measurement=PERCENTAGE,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="B1-R1-A/pressure",
            device_class=SensorDeviceClass.PRESSURE,
            entity_registry_enabled_default=False,
            name="Pressure",
            native_unit_of_measurement=PRESSURE_MBAR,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="S3-R1-A/illuminance",
            device_class=SensorDeviceClass.ILLUMINANCE,
            entity_registry_enabled_default=False,
            name="Illuminance",
            native_unit_of_measurement=LIGHT_LUX,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="VAD",
            device_class=SensorDeviceClass.VOLTAGE,
            entity_registry_enabled_default=False,
            name="Voltage VAD",
            native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="VDD",
            device_class=SensorDeviceClass.VOLTAGE,
            entity_registry_enabled_default=False,
            name="Voltage VDD",
            native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="vis",
            device_class=SensorDeviceClass.VOLTAGE,
            entity_registry_enabled_default=False,
            name="vis",
            native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
    ),
    "28": (
        OneWireSensorEntityDescription(
            key="temperature",
            device_class=SensorDeviceClass.TEMPERATURE,
            name="Temperature",
            native_unit_of_measurement=TEMP_CELSIUS,
            override_key=_get_sensor_precision_family_28,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
    ),
    "30": (
        SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,
        OneWireSensorEntityDescription(
            key="typeX/temperature",
            device_class=SensorDeviceClass.TEMPERATURE,
            entity_registry_enabled_default=False,
            name="Thermocouple temperature",
            native_unit_of_measurement=TEMP_CELSIUS,
            read_mode=READ_MODE_FLOAT,
            override_key=lambda d, o: "typeK/temperature",
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="volt",
            device_class=SensorDeviceClass.VOLTAGE,
            entity_registry_enabled_default=False,
            name="Voltage",
            native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="vis",
            device_class=SensorDeviceClass.VOLTAGE,
            entity_registry_enabled_default=False,
            name="vis",
            native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
    ),
    "3B": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
    "42": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
    "1D": tuple(
        OneWireSensorEntityDescription(
            key=f"counter.{id}",
            name=f"Counter {id}",
            native_unit_of_measurement="count",
            read_mode=READ_MODE_INT,
            state_class=SensorStateClass.TOTAL_INCREASING,
        )
        for id in DEVICE_KEYS_A_B
    ),
}

# EF sensors are usually hobbyboards specialized sensors.

HOBBYBOARD_EF: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
    "HobbyBoards_EF": (
        OneWireSensorEntityDescription(
            key="humidity/humidity_corrected",
            device_class=SensorDeviceClass.HUMIDITY,
            name="Humidity",
            native_unit_of_measurement=PERCENTAGE,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="humidity/humidity_raw",
            device_class=SensorDeviceClass.HUMIDITY,
            name="Humidity Raw",
            native_unit_of_measurement=PERCENTAGE,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="humidity/temperature",
            device_class=SensorDeviceClass.TEMPERATURE,
            name="Temperature",
            native_unit_of_measurement=TEMP_CELSIUS,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
    ),
    "HB_MOISTURE_METER": tuple(
        OneWireSensorEntityDescription(
            key=f"moisture/sensor.{id}",
            device_class=SensorDeviceClass.PRESSURE,
            name=f"Moisture {id}",
            native_unit_of_measurement=PRESSURE_CBAR,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        )
        for id in DEVICE_KEYS_0_3
    ),
}

# 7E sensors are special sensors by Embedded Data Systems

EDS_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
    "EDS0066": (
        OneWireSensorEntityDescription(
            key="EDS0066/temperature",
            device_class=SensorDeviceClass.TEMPERATURE,
            name="Temperature",
            native_unit_of_measurement=TEMP_CELSIUS,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="EDS0066/pressure",
            device_class=SensorDeviceClass.PRESSURE,
            name="Pressure",
            native_unit_of_measurement=PRESSURE_MBAR,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
    ),
    "EDS0068": (
        OneWireSensorEntityDescription(
            key="EDS0068/temperature",
            device_class=SensorDeviceClass.TEMPERATURE,
            name="Temperature",
            native_unit_of_measurement=TEMP_CELSIUS,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="EDS0068/pressure",
            device_class=SensorDeviceClass.PRESSURE,
            name="Pressure",
            native_unit_of_measurement=PRESSURE_MBAR,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="EDS0068/light",
            device_class=SensorDeviceClass.ILLUMINANCE,
            name="Illuminance",
            native_unit_of_measurement=LIGHT_LUX,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
        OneWireSensorEntityDescription(
            key="EDS0068/humidity",
            device_class=SensorDeviceClass.HUMIDITY,
            name="Humidity",
            native_unit_of_measurement=PERCENTAGE,
            read_mode=READ_MODE_FLOAT,
            state_class=SensorStateClass.MEASUREMENT,
        ),
    ),
}


def get_sensor_types(
    device_sub_type: str,
) -> dict[str, tuple[OneWireSensorEntityDescription, ...]]:
    """Return the proper info array for the device type."""
    if "HobbyBoard" in device_sub_type:
        return HOBBYBOARD_EF
    if "EDS" in device_sub_type:
        return EDS_SENSORS
    return DEVICE_SENSORS


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up 1-Wire platform."""
    onewirehub = hass.data[DOMAIN][config_entry.entry_id]
    entities = await hass.async_add_executor_job(
        get_entities, onewirehub, config_entry.options
    )
    async_add_entities(entities, True)


def get_entities(
    onewirehub: OneWireHub, options: MappingProxyType[str, Any]
) -> list[OneWireSensor]:
    """Get a list of entities."""
    if not onewirehub.devices:
        return []

    entities: list[OneWireSensor] = []
    assert onewirehub.owproxy
    for device in onewirehub.devices:
        family = device.family
        device_type = device.type
        device_id = device.id
        device_info = device.device_info
        device_sub_type = "std"
        device_path = device.path
        if "EF" in family:
            device_sub_type = "HobbyBoard"
            family = device_type
        elif "7E" in family:
            device_sub_type = "EDS"
            family = device_type

        if family not in get_sensor_types(device_sub_type):
            continue
        for description in get_sensor_types(device_sub_type)[family]:
            if description.key.startswith("moisture/"):
                s_id = description.key.split(".")[1]
                is_leaf = int(
                    onewirehub.owproxy.read(
                        f"{device_path}moisture/is_leaf.{s_id}"
                    ).decode()
                )
                if is_leaf:
                    description = copy.deepcopy(description)
                    description.device_class = SensorDeviceClass.HUMIDITY
                    description.native_unit_of_measurement = PERCENTAGE
                    description.name = f"Wetness {s_id}"
            override_key = None
            if description.override_key:
                override_key = description.override_key(device_id, options)
            device_file = os.path.join(
                os.path.split(device.path)[0],
                override_key or description.key,
            )
            name = f"{device_id} {description.name}"
            entities.append(
                OneWireSensor(
                    description=description,
                    device_id=device_id,
                    device_file=device_file,
                    device_info=device_info,
                    name=name,
                    owproxy=onewirehub.owproxy,
                )
            )
    return entities


class OneWireSensor(OneWireEntity, SensorEntity):
    """Implementation of a 1-Wire sensor."""

    entity_description: OneWireSensorEntityDescription

    @property
    def native_value(self) -> StateType:
        """Return the state of the entity."""
        return self._state
