import logging
from typing import Final

from zigpy.quirks.v2 import QuirkBuilder, BinarySensorDeviceClass

import zigpy.types as t
from zigpy.zcl.foundation import ZCLAttributeDef
from zigpy.zcl.clusters.measurement import (
    IlluminanceMeasurement,
    OccupancySensing,
)
from zigpy.zcl.clusters.security import IasZone
from zigpy.quirks.v2.homeassistant import EntityPlatform, EntityType

from zhaquirks.tuya import (
    TuyaLocalCluster,
    TuyaPowerConfigurationCluster2AAA,
)
from zhaquirks.tuya.mcu import TuyaMCUCluster, DPToAttributeMapping

class PresenceState(t.enum8):
    """Presence State values"""
    none = 0x00
    presence = 0x01
    peaceful = 0x02
    small_movement = 0x03
    large_movement = 0x04

class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster):
    """Tuya local OccupancySensing cluster."""

class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster):
    """Tuya local IlluminanceMeasurement cluster."""

class HumanPresenceSensorManufCluster(TuyaMCUCluster):
    """Human Presence Sensor ZG-205Z (5.8GHz)"""

    class AttributeDefs(TuyaMCUCluster.AttributeDefs):
        """Tuya DataPoints attributes"""

        # Presence state
        presence_state: Final = ZCLAttributeDef(
            id=0x0001, # DP 1
            type=t.uint16_t,
            access="rp",
            is_manufacturer_specific=True,
        )
        # Target distance
        target_distance: Final = ZCLAttributeDef(
            id=0x0101, # DP 101
            type=t.uint16_t,
            access="rp",
            is_manufacturer_specific=True,
        )
        # Illuminance value
        illuminance_lux: Final = ZCLAttributeDef(
            id=0x0102, # DP 102
            type=t.uint16_t,
            access="rp",
            is_manufacturer_specific=True,
        )
        # None delay time (presence keep time)
        none_delay_time: Final = ZCLAttributeDef(
            id=0x0103, # DP 103
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Indicator
        indicator: Final = ZCLAttributeDef(
            id=0x0104, # DP 104
            type=t.Bool,
            is_manufacturer_specific=True,
        )
        # Move detection max distance
        move_detection_max: Final = ZCLAttributeDef(
            id=0x0107, # DP 107
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Move detection min distance
        move_detection_min: Final = ZCLAttributeDef(
            id=0x0108, # DP 108
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Breath detection max distance
        breath_detection_max: Final = ZCLAttributeDef(
            id=0x0109, # DP 109
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Breath detection min distance
        breath_detection_min: Final = ZCLAttributeDef(
            id=0x0110, # DP 110
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Small move detection max distance
        small_move_detection_max: Final = ZCLAttributeDef(
            id=0x0114, # DP 114
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Small move detection min distance
        small_move_detection_min: Final = ZCLAttributeDef(
            id=0x0115, # DP 115
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Move sensitivity (1 is higher, 9 is lower)
        move_sensitivity: Final = ZCLAttributeDef(
            id=0x0116, # DP 116
            type=t.uint8_t,
            is_manufacturer_specific=True,
        )
        # Small move sensitivity
        small_move_sensitivity: Final = ZCLAttributeDef(
            id=0x0117, # DP 117
            type=t.uint8_t,
            is_manufacturer_specific=True,
        )
        # Breath sensitivity
        breath_sensitivity: Final = ZCLAttributeDef(
            id=0x0118, # DP 118
            type=t.uint8_t,
            is_manufacturer_specific=True,
        )

    dp_to_attribute: dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "presence_state",
            converter=PresenceState
        ),
        101: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "target_distance",
            converter=lambda x: x / 100 if x is not None else 0
        ),
        102: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "illuminance_lux",
        ),
        103: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "none_delay_time",
            # Value in Tuya App is 30 after Factory reset
            #converter=lambda x: x if x is not None else 30
        ),
        104: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "indicator",
        ),
        107: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "move_detection_max",
            converter=lambda x: x / 100 if x is not None else 10
        ),
        108: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "move_detection_min",
            converter=lambda x: x / 100 if x is not None else 0
        ),
        109: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "breath_detection_max",
            converter=lambda x: x / 100 if x is not None else 6
        ),
        110: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "breath_detection_min",
            converter=lambda x: x / 100 if x is not None else 0
        ),
        114: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "small_move_detection_max",
            converter=lambda x: x / 100 if x is not None else 6
        ),
        115: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "small_move_detection_min",
            converter=lambda x: x / 100 if x is not None else 0
        ),
        116: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "move_sensitivity",
            converter=lambda x: x if x is not None else 5
        ),
        117: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "small_move_sensitivity",
            converter=lambda x: x if x is not None else 5
        ),
        118: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "breath_sensitivity",
            converter=lambda x: x if x is not None else 5
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        101: "_dp_2_attr_update",
        102: "_dp_2_attr_update",
        103: "_dp_2_attr_update",
        104: "_dp_2_attr_update",
        107: "_dp_2_attr_update",
        108: "_dp_2_attr_update",
        109: "_dp_2_attr_update",
        110: "_dp_2_attr_update",
        114: "_dp_2_attr_update",
        115: "_dp_2_attr_update",
        116: "_dp_2_attr_update",
        117: "_dp_2_attr_update",
        118: "_dp_2_attr_update",
    }

(
    QuirkBuilder("_TZE204_dapwryy7", "TS0601")
    .skip_configuration()
    .removes(IasZone.cluster_id)
    .adds(HumanPresenceSensorManufCluster)
    #.adds(TuyaOccupancySensing)
    .replaces(TuyaPowerConfigurationCluster2AAA)
    .replaces(TuyaIlluminanceMeasurement)
    .binary_sensor(
        "presence_state",
        HumanPresenceSensorManufCluster.cluster_id,
        endpoint_id=1,
        #entity_type=EntityType.STANDARD, # very soon (zigpy channel #dev on github
        device_class=BinarySensorDeviceClass.OCCUPANCY,
        fallback_name="Presence"
    )
    .enum(
        HumanPresenceSensorManufCluster.AttributeDefs.presence_state.name,
        PresenceState,
        HumanPresenceSensorManufCluster.cluster_id,
        entity_platform=EntityPlatform.SENSOR,
        entity_type=EntityType.STANDARD,
        fallback_name="Presence State",
        translation_key="presence_state"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.illuminance_lux.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=2500,
        fallback_name="Illuminance (lx)",
        translation_key="illuminance_lux",
        #entity_type=EntityType.STANDARD # not yet :/
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.target_distance.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=10,
        fallback_name="Target Distance (m)",
        translation_key="target_distance",
        #entity_type=EntityType.STANDARD # not yet :/
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.none_delay_time.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=28800,
        #unit="s",
        fallback_name="Hold Delay Time",
        translation_key="none_delay_time"
    )
    .switch(
        HumanPresenceSensorManufCluster.AttributeDefs.indicator.name,
        HumanPresenceSensorManufCluster.cluster_id,
        fallback_name="LED Indicator",
        translation_key="indicator"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.move_detection_max.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=10,
        fallback_name="Move Detection Max Distance (m)",
        translation_key="move_detection_max"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.move_detection_min.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=10,
        fallback_name="Move Detection Min Distance (m)",
        translation_key="move_detection_min"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.small_move_detection_max.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=6,
        fallback_name="Small Move Detection Max Distance (m)",
        translation_key="small_move_detection_max"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.small_move_detection_min.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=6,
        fallback_name="Small Move Detection Min Distance (m)",
        translation_key="small_move_detection_min"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.breath_detection_max.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=6,
        fallback_name="Breath Detection Max Distance (m)",
        translation_key="breath_detection_max"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.breath_detection_min.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=6,
        fallback_name="Breath Detection Min Distance (m)",
        translation_key="breath_detection_min"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.move_sensitivity.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=10,
        fallback_name="Move Sensitivity",
        translation_key="move_sensitivity"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.small_move_sensitivity.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=10,
        fallback_name="Small Move Sensitivity",
        translation_key="small_move_sensitivity"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.breath_sensitivity.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=10,
        fallback_name="Breath Sensitivity", 
        translation_key="breath_sensitivity"
    )
    .add_to_registry()
)