"""
Device management for Loxone MCP Server.

Handles device discovery, registration, state management, and querying.
Provides a centralized registry for all Loxone devices and their states.
"""

import logging
from dataclasses import dataclass, field
from threading import RLock
from typing import Dict, List, Optional, Any, Callable

logger = logging.getLogger(__name__)

# Configure enhanced logging for device management
logger.setLevel(logging.DEBUG)  # Allow debug messages for device discovery troubleshooting

# Device type constants for all supported Loxone device types (Complete HA Component Migration)

# Lighting Controls
DEVICE_TYPE_SWITCH = "Switch"
DEVICE_TYPE_TIMED_SWITCH = "TimedSwitch"
DEVICE_TYPE_PUSHBUTTON = "Pushbutton"
DEVICE_TYPE_DIMMER = "Dimmer"
DEVICE_TYPE_LIGHT_CONTROLLER_V2 = "LightControllerV2"
DEVICE_TYPE_COLOR_PICKER_V2 = "ColorPickerV2"

# Cover Controls
DEVICE_TYPE_JALOUSIE = "Jalousie"
DEVICE_TYPE_WINDOW = "Window"
DEVICE_TYPE_GATE = "Gate"

# Climate Controls
DEVICE_TYPE_IROOM_CONTROLLER_V2 = "IRoomControllerV2"
DEVICE_TYPE_VENTILATION = "Ventilation"

# Sensors and Monitoring
DEVICE_TYPE_INFO_ONLY_ANALOG = "InfoOnlyAnalog"
DEVICE_TYPE_INFO_ONLY_DIGITAL = "InfoOnlyDigital"
DEVICE_TYPE_PRESENCE = "Presence"
DEVICE_TYPE_SMOKE_ALARM = "SmokeAlarm"

# Media and Audio
DEVICE_TYPE_AUDIO_ZONE_V2 = "AudioZoneV2"
DEVICE_TYPE_MEDIA_PLAYER = "MediaPlayer"

# Security and Safety
DEVICE_TYPE_ALARM = "Alarm"
DEVICE_TYPE_CENTRAL_ALARM = "CentralAlarm"
DEVICE_TYPE_INTERCOM = "Intercom"

# Input and Control
DEVICE_TYPE_SLIDER = "Slider"
DEVICE_TYPE_TEXT_INPUT = "TextInput"
DEVICE_TYPE_UP_DOWN_ANALOG = "UpDownAnalog"

# Automation
DEVICE_TYPE_MOOD = "Mood"
DEVICE_TYPE_TRACKER = "Tracker"

# System Integration
DEVICE_TYPE_WEATHER_SERVER = "WeatherServer"

# Scene type constant
SCENE_TYPE = "Scene"

# Set of all supported device types for filtering (Complete HA Component Migration)
SUPPORTED_DEVICE_TYPES = {
    # Lighting Controls
    DEVICE_TYPE_SWITCH,
    DEVICE_TYPE_TIMED_SWITCH,
    DEVICE_TYPE_PUSHBUTTON,
    DEVICE_TYPE_DIMMER,
    DEVICE_TYPE_LIGHT_CONTROLLER_V2,
    DEVICE_TYPE_COLOR_PICKER_V2,
    # Cover Controls
    DEVICE_TYPE_JALOUSIE,
    DEVICE_TYPE_WINDOW,
    DEVICE_TYPE_GATE,
    # Climate Controls
    DEVICE_TYPE_IROOM_CONTROLLER_V2,
    DEVICE_TYPE_VENTILATION,
    # Sensors and Monitoring
    DEVICE_TYPE_INFO_ONLY_ANALOG,
    DEVICE_TYPE_INFO_ONLY_DIGITAL,
    DEVICE_TYPE_PRESENCE,
    DEVICE_TYPE_SMOKE_ALARM,
    # Media and Audio
    DEVICE_TYPE_AUDIO_ZONE_V2,
    DEVICE_TYPE_MEDIA_PLAYER,
    # Security and Safety
    DEVICE_TYPE_ALARM,
    DEVICE_TYPE_CENTRAL_ALARM,
    DEVICE_TYPE_INTERCOM,
    # Input and Control
    DEVICE_TYPE_SLIDER,
    DEVICE_TYPE_TEXT_INPUT,
    DEVICE_TYPE_UP_DOWN_ANALOG,
    # Automation
    DEVICE_TYPE_MOOD,
    DEVICE_TYPE_TRACKER,
    # System Integration
    DEVICE_TYPE_WEATHER_SERVER,
    # Scenes
    SCENE_TYPE,
}


@dataclass
class LoxoneDevice:
    """
    Data model for a Loxone device.

    Represents a single device from the Loxone Miniserver with all its
    metadata, current state, available controls, and device capabilities.
    Enhanced with comprehensive state tracking and metadata support.
    """

    uuid: str
    name: str
    type: str
    room: str
    category: str
    states: Dict[str, Any] = field(default_factory=dict)
    details: Dict[str, Any] = field(default_factory=dict)
    controls: Dict[str, Any] = field(default_factory=dict)
    capabilities: Dict[str, Any] = field(default_factory=dict)
    metadata: Dict[str, Any] = field(default_factory=dict)

    def __post_init__(self):
        """Validate device data after initialization and set up enhanced state tracking."""
        import time

        if not self.uuid:
            raise ValueError("Device UUID cannot be empty")
        if not self.name:
            raise ValueError("Device name cannot be empty")
        if not self.type:
            raise ValueError("Device type cannot be empty")

        # Auto-detect capabilities based on device type if not provided
        if not self.capabilities:
            self.capabilities = self._detect_capabilities()

        # Initialize metadata with state parameter descriptions and tracking
        if not self.metadata:
            self.metadata = self._initialize_metadata()

        # Add state tracking timestamps
        if "_created_at" not in self.states:
            self.states["_created_at"] = time.time()

        if "_last_updated" not in self.states:
            self.states["_last_updated"] = time.time()

        if "_reachable" not in self.states:
            self.states["_reachable"] = True

    def _detect_capabilities(self) -> Dict[str, Any]:
        """
        Detect device capabilities based on device type and details.

        Returns:
            Dictionary containing device capabilities
        """
        capabilities = {}

        # Lighting capabilities
        if self.type in [DEVICE_TYPE_DIMMER, DEVICE_TYPE_LIGHT_CONTROLLER_V2]:
            capabilities.update(
                {"brightness": True, "brightness_range": [0, 100], "supports_on_off": True}
            )

        if self.type == DEVICE_TYPE_COLOR_PICKER_V2:
            capabilities.update(
                {
                    "brightness": True,
                    "brightness_range": [0, 100],
                    "supports_on_off": True,
                    "color": True,
                    "color_modes": ["rgb", "hsv"],
                    "supports_white_value": True,
                }
            )

        # Switch capabilities
        if self.type in [DEVICE_TYPE_SWITCH, DEVICE_TYPE_TIMED_SWITCH, DEVICE_TYPE_PUSHBUTTON]:
            capabilities.update({"supports_on_off": True})

        # Cover capabilities
        if self.type in [DEVICE_TYPE_JALOUSIE, DEVICE_TYPE_WINDOW, DEVICE_TYPE_GATE]:
            capabilities.update(
                {
                    "position": True,
                    "position_range": [0, 100],
                    "supports_open_close": True,
                    "supports_stop": True,
                }
            )

            if self.type == DEVICE_TYPE_JALOUSIE:
                capabilities.update({"tilt": True, "tilt_range": [0, 100]})

        # Climate capabilities
        if self.type == DEVICE_TYPE_IROOM_CONTROLLER_V2:
            capabilities.update(
                {
                    "temperature": True,
                    "temperature_range": [5.0, 35.0],
                    "temperature_unit": "°C",
                    "hvac_modes": ["off", "heat", "cool", "auto"],
                    "supports_target_temperature": True,
                    "supports_current_temperature": True,
                }
            )

        # Fan capabilities
        if self.type == DEVICE_TYPE_VENTILATION:
            capabilities.update(
                {
                    "fan_speed": True,
                    "fan_speed_range": [0, 100],
                    "supports_on_off": True,
                    "fan_modes": ["auto", "low", "medium", "high"],
                }
            )

        # Media player capabilities
        if self.type in [DEVICE_TYPE_AUDIO_ZONE_V2, DEVICE_TYPE_MEDIA_PLAYER]:
            capabilities.update(
                {
                    "volume": True,
                    "volume_range": [0, 100],
                    "supports_play_pause": True,
                    "supports_stop": True,
                    "supports_next_previous": True,
                    "supports_seek": True,
                    "media_content_types": ["music", "radio", "playlist"],
                }
            )

        # Alarm capabilities
        if self.type in [DEVICE_TYPE_ALARM, DEVICE_TYPE_CENTRAL_ALARM]:
            capabilities.update(
                {
                    "supports_arm_disarm": True,
                    "requires_code": True,
                    "alarm_states": ["disarmed", "armed_home", "armed_away", "triggered"],
                }
            )

        # Input control capabilities
        if self.type == DEVICE_TYPE_SLIDER:
            min_val = self.details.get("min", 0)
            max_val = self.details.get("max", 100)
            step = self.details.get("step", 1)
            capabilities.update(
                {
                    "numeric_input": True,
                    "value_range": [min_val, max_val],
                    "step": step,
                    "unit": self.details.get("unit", ""),
                }
            )

        if self.type == DEVICE_TYPE_TEXT_INPUT:
            capabilities.update(
                {"text_input": True, "max_length": self.details.get("maxLength", 255)}
            )

        if self.type == DEVICE_TYPE_UP_DOWN_ANALOG:
            capabilities.update({"up_down_control": True, "supports_increment_decrement": True})

        # Sensor capabilities
        if self.type in [DEVICE_TYPE_INFO_ONLY_ANALOG, DEVICE_TYPE_INFO_ONLY_DIGITAL]:
            capabilities.update(
                {
                    "read_only": True,
                    "sensor_type": (
                        "analog" if self.type == DEVICE_TYPE_INFO_ONLY_ANALOG else "digital"
                    ),
                }
            )

        if self.type == DEVICE_TYPE_PRESENCE:
            capabilities.update(
                {"read_only": True, "sensor_type": "presence", "binary_sensor": True}
            )

        if self.type == DEVICE_TYPE_SMOKE_ALARM:
            capabilities.update(
                {
                    "read_only": True,
                    "sensor_type": "smoke",
                    "binary_sensor": True,
                    "safety_device": True,
                }
            )

        # Weather capabilities
        if self.type == DEVICE_TYPE_WEATHER_SERVER:
            capabilities.update(
                {
                    "read_only": True,
                    "weather_data": True,
                    "provides_temperature": True,
                    "provides_humidity": True,
                    "provides_wind": True,
                    "provides_precipitation": True,
                }
            )

        # Scene capabilities
        if self.type == SCENE_TYPE:
            capabilities.update({"triggerable": True, "scene_control": True})

        # Add security flag for secured devices
        if self.details.get("isSecured", False):
            capabilities["requires_pin"] = True

        return capabilities

    def _initialize_metadata(self) -> Dict[str, Any]:
        """
        Initialize metadata with state parameter descriptions and units.

        Returns:
            Dictionary containing metadata for the device
        """
        import time

        metadata = {
            "created_at": time.time(),
            "state_parameters": {},
            "parameter_units": {},
            "parameter_ranges": {},
            "parameter_descriptions": {},
            "validation_rules": {},
            "last_state_update": None,
            "state_update_count": 0,
        }

        # Add type-specific metadata
        if self.type in [DEVICE_TYPE_DIMMER, DEVICE_TYPE_LIGHT_CONTROLLER_V2]:
            metadata["state_parameters"] = {
                "value": "Brightness level (0-100%)",
                "active": "Light is currently on/off",
            }
            metadata["parameter_units"] = {"value": "%"}
            metadata["parameter_ranges"] = {"value": [0, 100]}
            metadata["validation_rules"] = {"value": {"type": "float", "min": 0, "max": 100}}

        elif self.type == DEVICE_TYPE_COLOR_PICKER_V2:
            metadata["state_parameters"] = {
                "value": "Brightness level (0-100%)",
                "color": "Color value (RGB hex or numeric)",
                "active": "Light is currently on/off",
            }
            metadata["parameter_units"] = {"value": "%", "color": "hex"}
            metadata["parameter_ranges"] = {
                "value": [0, 100],
                "color": [0, 16777215],  # 0x000000 to 0xFFFFFF
            }
            metadata["validation_rules"] = {
                "value": {"type": "float", "min": 0, "max": 100},
                "color": {"type": "int", "min": 0, "max": 16777215},
            }

        elif self.type in [DEVICE_TYPE_JALOUSIE, DEVICE_TYPE_WINDOW, DEVICE_TYPE_GATE]:
            metadata["state_parameters"] = {
                "position": "Cover position (0=closed, 100=open)",
                "active": "Cover is currently moving",
            }
            metadata["parameter_units"] = {"position": "%"}
            metadata["parameter_ranges"] = {"position": [0, 100]}
            metadata["validation_rules"] = {"position": {"type": "float", "min": 0, "max": 100}}

            if self.type == DEVICE_TYPE_JALOUSIE:
                metadata["state_parameters"]["tilt"] = "Tilt angle (0-100%)"
                metadata["parameter_units"]["tilt"] = "%"
                metadata["parameter_ranges"]["tilt"] = [0, 100]
                metadata["validation_rules"]["tilt"] = {"type": "float", "min": 0, "max": 100}

        elif self.type == DEVICE_TYPE_IROOM_CONTROLLER_V2:
            metadata["state_parameters"] = {
                "temperature": "Current temperature",
                "targetTemperature": "Target temperature setting",
                "mode": "HVAC mode (0=off, 1=heat, 2=cool, 3=auto)",
                "active": "Heating/cooling active status",
            }
            metadata["parameter_units"] = {"temperature": "°C", "targetTemperature": "°C"}
            metadata["parameter_ranges"] = {
                "temperature": [-50, 100],
                "targetTemperature": [5, 35],
                "mode": [0, 3],
            }
            metadata["validation_rules"] = {
                "temperature": {"type": "float", "min": -50, "max": 100},
                "targetTemperature": {"type": "float", "min": 5, "max": 35},
                "mode": {"type": "int", "min": 0, "max": 3},
            }

        elif self.type in [DEVICE_TYPE_SWITCH, DEVICE_TYPE_TIMED_SWITCH, DEVICE_TYPE_PUSHBUTTON]:
            metadata["state_parameters"] = {
                "value": "Switch state (0=off, 1=on)",
                "active": "Switch is currently active",
            }
            metadata["parameter_ranges"] = {"value": [0, 1], "active": [0, 1]}
            metadata["validation_rules"] = {
                "value": {"type": "int", "min": 0, "max": 1},
                "active": {"type": "int", "min": 0, "max": 1},
            }

        elif self.type == DEVICE_TYPE_VENTILATION:
            metadata["state_parameters"] = {
                "value": "Fan speed (0-100%)",
                "active": "Fan is currently running",
            }
            metadata["parameter_units"] = {"value": "%"}
            metadata["parameter_ranges"] = {"value": [0, 100]}
            metadata["validation_rules"] = {"value": {"type": "float", "min": 0, "max": 100}}

        elif self.type in [DEVICE_TYPE_AUDIO_ZONE_V2, DEVICE_TYPE_MEDIA_PLAYER]:
            metadata["state_parameters"] = {
                "volume": "Volume level (0-100%)",
                "mode": "Playback mode (0=idle, 1=playing, 2=paused)",
                "active": "Media player is active",
            }
            metadata["parameter_units"] = {"volume": "%"}
            metadata["parameter_ranges"] = {"volume": [0, 100], "mode": [0, 2]}
            metadata["validation_rules"] = {
                "volume": {"type": "float", "min": 0, "max": 100},
                "mode": {"type": "int", "min": 0, "max": 2},
            }

        elif self.type in [DEVICE_TYPE_ALARM, DEVICE_TYPE_CENTRAL_ALARM]:
            metadata["state_parameters"] = {
                "active": "Alarm is triggered",
                "armed": "Alarm is armed",
            }
            metadata["parameter_ranges"] = {"active": [0, 1], "armed": [0, 1]}
            metadata["validation_rules"] = {
                "active": {"type": "int", "min": 0, "max": 1},
                "armed": {"type": "int", "min": 0, "max": 1},
            }

        elif self.type == DEVICE_TYPE_SLIDER:
            min_val = self.details.get("min", 0)
            max_val = self.details.get("max", 100)
            unit = self.details.get("unit", "")

            metadata["state_parameters"] = {"value": f"Slider value ({min_val}-{max_val})"}
            metadata["parameter_units"] = {"value": unit}
            metadata["parameter_ranges"] = {"value": [min_val, max_val]}
            metadata["validation_rules"] = {
                "value": {"type": "float", "min": min_val, "max": max_val}
            }

        elif self.type == DEVICE_TYPE_TEXT_INPUT:
            max_length = self.details.get("maxLength", 255)

            metadata["state_parameters"] = {"text": f"Text input value (max {max_length} chars)"}
            metadata["parameter_ranges"] = {"text": [0, max_length]}
            metadata["validation_rules"] = {"text": {"type": "str", "max_length": max_length}}

        elif self.type in [DEVICE_TYPE_INFO_ONLY_ANALOG, DEVICE_TYPE_INFO_ONLY_DIGITAL]:
            unit = self.details.get("unit", "")
            sensor_type = "digital" if self.type == DEVICE_TYPE_INFO_ONLY_DIGITAL else "analog"

            metadata["state_parameters"] = {"value": f"Sensor reading ({sensor_type})"}
            metadata["parameter_units"] = {"value": unit}
            metadata["validation_rules"] = {
                "value": {"type": "float" if sensor_type == "analog" else "int"}
            }

        elif self.type == SCENE_TYPE:
            metadata["state_parameters"] = {"active": "Scene is currently active"}
            metadata["parameter_ranges"] = {"active": [0, 1]}
            metadata["validation_rules"] = {"active": {"type": "int", "min": 0, "max": 1}}

        return metadata

    def validate_state_parameter(self, parameter: str, value: Any) -> bool:
        """
        Validate a state parameter value against the device's validation rules.

        Args:
            parameter: The parameter name to validate
            value: The value to validate

        Returns:
            True if the value is valid, False otherwise
        """
        if not self.metadata or "validation_rules" not in self.metadata:
            return True  # No validation rules defined

        rules = self.metadata["validation_rules"].get(parameter)
        if not rules:
            return True  # No rules for this parameter

        try:
            # Type validation
            expected_type = rules.get("type")
            if expected_type == "float":
                value = float(value)
            elif expected_type == "int":
                value = int(value)
            elif expected_type == "str":
                value = str(value)

            # Range validation
            if "min" in rules and value < rules["min"]:
                return False
            if "max" in rules and value > rules["max"]:
                return False

            # String length validation
            if expected_type == "str" and "max_length" in rules:
                if len(value) > rules["max_length"]:
                    return False

            return True

        except (ValueError, TypeError):
            return False

    def get_default_state_value(self, parameter: str) -> Any:
        """
        Get the default value for a state parameter.

        Args:
            parameter: The parameter name

        Returns:
            Default value for the parameter
        """
        if not self.metadata or "validation_rules" not in self.metadata:
            return None

        rules = self.metadata["validation_rules"].get(parameter)
        if not rules:
            return None

        expected_type = rules.get("type")
        if expected_type == "float":
            return rules.get("min", 0.0)
        elif expected_type == "int":
            return rules.get("min", 0)
        elif expected_type == "str":
            return ""

        return None

    def update_state_tracking(self) -> None:
        """Update state tracking metadata when states are modified."""
        import time

        if not self.metadata:
            self.metadata = {}

        self.states["_last_updated"] = time.time()
        self.metadata["last_state_update"] = time.time()
        self.metadata["state_update_count"] = self.metadata.get("state_update_count", 0) + 1


class DeviceManager:
    """
    Central device management for Loxone devices.

    Maintains a registry of all discovered devices, manages their states,
    and provides methods for querying and filtering devices.
    """

    def __init__(self):
        """Initialize the device manager."""
        self._devices: Dict[str, LoxoneDevice] = {}
        self._scenes: Dict[str, LoxoneDevice] = {}  # Separate registry for scenes
        self._state_lock = RLock()  # Thread-safe state updates
        self._state_callbacks: List[Callable[[str, Dict[str, Any]], None]] = []

        logger.info("DeviceManager initialized")

    @property
    def devices(self) -> Dict[str, LoxoneDevice]:
        """Get read-only access to the device registry."""
        return self._devices.copy()

    def load_devices(self, loxapp_json: Dict[str, Any]) -> None:
        """
        Parse LoxAPP3.json structure and register supported devices.

        Extracts device information from the Loxone structure file,
        filters for supported device types, and creates LoxoneDevice
        instances for each discovered device.

        Args:
            loxapp_json: The parsed LoxAPP3.json structure from Miniserver
        """
        if not loxapp_json:
            logger.warning("Empty LoxAPP3.json structure provided - no devices will be loaded")
            return

        if not isinstance(loxapp_json, dict):
            logger.error(f"Invalid LoxAPP3.json structure type: {type(loxapp_json)}")
            logger.error("Expected dictionary structure, cannot load devices")
            return

        logger.info("Loading devices from LoxAPP3.json structure")
        logger.debug(f"Structure contains top-level keys: {list(loxapp_json.keys())}")

        # Clear existing devices and scenes
        with self._state_lock:
            self._devices.clear()
            self._scenes.clear()

        # Extract rooms for device categorization
        rooms = {}
        if "rooms" in loxapp_json:
            for room_uuid, room_data in loxapp_json["rooms"].items():
                rooms[room_uuid] = room_data.get("name", "Unknown Room")

        # Extract categories
        categories = {}
        if "cats" in loxapp_json:
            for cat_uuid, cat_data in loxapp_json["cats"].items():
                categories[cat_uuid] = cat_data.get("name", "Unknown Category")

        # Process controls (main device definitions)
        controls_processed = 0
        devices_registered = 0
        unsupported_types = set()

        if "controls" in loxapp_json:
            for control_uuid, control_data in loxapp_json["controls"].items():
                controls_processed += 1

                # Extract basic device information
                device_type = control_data.get("type", "Unknown")
                device_name = control_data.get("name", f"Device {control_uuid}")

                # Check if device type is supported
                if device_type not in SUPPORTED_DEVICE_TYPES:
                    unsupported_types.add(device_type)
                    logger.debug(f"Skipping unsupported device type: {device_type} ({device_name})")
                    continue

                # Get room name
                room_uuid = control_data.get("room", "")
                room_name = rooms.get(room_uuid, "Unknown Room")

                # Get category name
                cat_uuid = control_data.get("cat", "")
                category_name = categories.get(cat_uuid, "Unknown Category")

                # Extract device details and controls
                details = {
                    "uuidAction": control_data.get("uuidAction", ""),
                    "defaultRating": control_data.get("defaultRating", 0),
                    "isFavorite": control_data.get("isFavorite", False),
                    "isSecured": control_data.get("isSecured", False),
                }

                # Add type-specific details
                if "details" in control_data:
                    details.update(control_data["details"])

                # Extract available controls/states
                controls = {}
                if "states" in control_data:
                    for state_name, state_uuid in control_data["states"].items():
                        controls[state_name] = state_uuid

                # Create device instance with enhanced error handling
                try:
                    device = LoxoneDevice(
                        uuid=control_uuid,
                        name=device_name,
                        type=device_type,
                        room=room_name,
                        category=category_name,
                        states={},  # Will be populated by state updates
                        details=details,
                        controls=controls,
                        capabilities={},  # Will be auto-detected in __post_init__
                    )

                    # Register device
                    with self._state_lock:
                        self._devices[control_uuid] = device

                    devices_registered += 1
                    logger.debug(f"Registered device: {device_name} ({device_type}) in {room_name}")

                except ValueError as e:
                    logger.error(
                        f"Failed to create device {control_uuid} ({device_name}): {e}",
                        extra={
                            "device_uuid": control_uuid,
                            "device_name": device_name,
                            "device_type": device_type,
                            "room": room_name,
                        },
                    )
                    continue
                except Exception as e:
                    logger.error(
                        f"Unexpected error creating device {control_uuid} ({device_name}): {e}",
                        exc_info=True,
                        extra={
                            "device_uuid": control_uuid,
                            "device_name": device_name,
                            "device_type": device_type,
                        },
                    )
                    continue

        # Process scenes (automation scenarios)
        scenes_processed = 0
        scenes_registered = 0

        if "autopilot" in loxapp_json:
            autopilot_data = loxapp_json["autopilot"]
            if "states" in autopilot_data:
                for scene_uuid, scene_data in autopilot_data["states"].items():
                    scenes_processed += 1

                    # Extract scene information
                    scene_name = scene_data.get("name", f"Scene {scene_uuid}")

                    # Get room name if available
                    room_uuid = scene_data.get("room", "")
                    room_name = rooms.get(room_uuid, "Unknown Room")

                    # Get category name if available
                    cat_uuid = scene_data.get("cat", "")
                    category_name = categories.get(cat_uuid, "Scenes")

                    # Extract scene details
                    details = {
                        "description": scene_data.get("description", ""),
                        "isFavorite": scene_data.get("isFavorite", False),
                        "isSecured": scene_data.get("isSecured", False),
                    }

                    # Create scene device instance
                    try:
                        scene = LoxoneDevice(
                            uuid=scene_uuid,
                            name=scene_name,
                            type=SCENE_TYPE,
                            room=room_name,
                            category=category_name,
                            states={},  # Scenes don't have states like devices
                            details=details,
                            controls={},  # Scenes are triggered, not controlled
                        )

                        # Register scene
                        with self._state_lock:
                            self._scenes[scene_uuid] = scene

                        scenes_registered += 1
                        logger.debug(f"Registered scene: {scene_name} in {room_name}")

                    except ValueError as e:
                        logger.error(f"Failed to create scene {scene_uuid}: {e}")
                        continue

        # Log summary
        logger.info(
            f"Device loading complete: {devices_registered} devices and {scenes_registered} scenes registered "
            f"from {controls_processed} controls and {scenes_processed} scenes processed"
        )

        if unsupported_types:
            logger.info(
                f"Unsupported device types encountered: {', '.join(sorted(unsupported_types))}"
            )

    def get_device(self, uuid: str) -> Optional[LoxoneDevice]:
        """
        Get a device by its UUID.

        Args:
            uuid: The UUID of the device to retrieve

        Returns:
            LoxoneDevice instance if found, None otherwise
        """
        if not uuid:
            return None

        with self._state_lock:
            return self._devices.get(uuid)

    def get_device_state(self, uuid: str) -> Optional[Dict[str, Any]]:
        """
        Get the current state of a device by UUID.

        Args:
            uuid: The UUID of the device

        Returns:
            Dictionary containing current device state, None if device not found
        """
        device = self.get_device(uuid)
        if device:
            with self._state_lock:
                return device.states.copy()
        return None

    def list_devices(
        self, device_type: Optional[str] = None, room: Optional[str] = None
    ) -> List[LoxoneDevice]:
        """
        List devices with optional filtering.

        Args:
            device_type: Filter by device type (e.g., "Switch", "Dimmer")
            room: Filter by room name

        Returns:
            List of LoxoneDevice instances matching the filters
        """
        with self._state_lock:
            devices = list(self._devices.values())

        # Apply device type filter
        if device_type:
            devices = [d for d in devices if d.type == device_type]

        # Apply room filter
        if room:
            devices = [d for d in devices if d.room == room]

        # Sort by name for consistent ordering
        devices.sort(key=lambda d: d.name)

        logger.debug(f"Listed {len(devices)} devices " f"(type={device_type}, room={room})")

        return devices

    def update_state(self, uuid: str, state_data: Dict[str, Any]) -> None:
        """
        Update the state of a device with enhanced validation and tracking.

        Thread-safe method to update device state cache when state
        updates are received from the Miniserver. Now includes state
        validation and enhanced tracking.

        Args:
            uuid: The UUID of the device to update
            state_data: Dictionary containing new state values
        """
        if not uuid or not state_data:
            return

        with self._state_lock:
            device = self._devices.get(uuid)
            if device:
                # Validate state parameters before updating
                validated_data = {}
                for param, value in state_data.items():
                    if device.validate_state_parameter(param, value):
                        validated_data[param] = value
                    else:
                        logger.warning(
                            f"Invalid state value for device {device.name} ({uuid}): {param}={value}",
                            extra={
                                "device_uuid": uuid,
                                "device_name": device.name,
                                "parameter": param,
                                "invalid_value": value,
                            },
                        )
                        # Use default value instead
                        default_value = device.get_default_state_value(param)
                        if default_value is not None:
                            validated_data[param] = default_value

                # Update device state with validated data
                device.states.update(validated_data)

                # Update state tracking
                device.update_state_tracking()

                logger.debug(f"Updated state for device {device.name} ({uuid}): {validated_data}")

                # Notify callbacks with enhanced error handling
                for callback in self._state_callbacks:
                    try:
                        callback(uuid, validated_data)
                    except Exception as e:
                        logger.error(
                            f"Error in state update callback: {e}",
                            exc_info=True,
                            extra={
                                "device_uuid": uuid,
                                "callback_name": getattr(callback, "__name__", "unknown"),
                                "state_data_keys": (
                                    list(validated_data.keys())
                                    if isinstance(validated_data, dict)
                                    else "non-dict"
                                ),
                            },
                        )
            else:
                logger.debug(
                    f"Received state update for unknown device: {uuid}",
                    extra={
                        "unknown_uuid": uuid,
                        "state_data": state_data,
                        "registered_device_count": len(self._devices),
                    },
                )

    def register_state_callback(self, callback: Callable[[str, Dict[str, Any]], None]) -> None:
        """
        Register a callback for state updates.

        Args:
            callback: Function to call when device states are updated.
                     Signature: callback(uuid: str, state_data: Dict[str, Any])
        """
        if callback not in self._state_callbacks:
            self._state_callbacks.append(callback)
            logger.debug("Registered state update callback")

    def unregister_state_callback(self, callback: Callable[[str, Dict[str, Any]], None]) -> None:
        """
        Unregister a state update callback.

        Args:
            callback: The callback function to remove
        """
        if callback in self._state_callbacks:
            self._state_callbacks.remove(callback)
            logger.debug("Unregistered state update callback")

    def get_device_count(self) -> int:
        """Get the total number of registered devices."""
        with self._state_lock:
            return len(self._devices)

    def get_device_types(self) -> List[str]:
        """Get a list of all device types currently registered."""
        with self._state_lock:
            types = {device.type for device in self._devices.values()}
        return sorted(list(types))

    def get_rooms(self) -> List[str]:
        """Get a list of all rooms with registered devices."""
        with self._state_lock:
            rooms = {device.room for device in self._devices.values()}
        return sorted(list(rooms))

    def clear_devices(self) -> None:
        """Clear all registered devices and scenes (useful for testing)."""
        with self._state_lock:
            self._devices.clear()
            self._scenes.clear()
        logger.info("All devices and scenes cleared from registry")

    def get_scene(self, uuid: str) -> Optional[LoxoneDevice]:
        """
        Get a scene by its UUID.

        Args:
            uuid: The UUID of the scene to retrieve

        Returns:
            LoxoneDevice instance representing the scene if found, None otherwise
        """
        if not uuid:
            return None

        with self._state_lock:
            return self._scenes.get(uuid)

    def list_scenes(self, room: Optional[str] = None) -> List[LoxoneDevice]:
        """
        List all available scenes with optional room filtering.

        Args:
            room: Filter by room name (optional)

        Returns:
            List of LoxoneDevice instances representing scenes
        """
        with self._state_lock:
            scenes = list(self._scenes.values())

        # Apply room filter if specified
        if room:
            scenes = [s for s in scenes if s.room == room]

        # Sort by name for consistent ordering
        scenes.sort(key=lambda s: s.name)

        logger.debug(f"Listed {len(scenes)} scenes (room={room})")

        return scenes

    def get_scene_count(self) -> int:
        """Get the total number of registered scenes."""
        with self._state_lock:
            return len(self._scenes)

    def list_rooms(self) -> List[Dict[str, Any]]:
        """
        List all rooms with device counts.

        Returns:
            List of dictionaries containing room information and device counts
        """
        with self._state_lock:
            room_data = {}

            # Count devices per room
            for device in self._devices.values():
                room_name = device.room
                if room_name not in room_data:
                    room_data[room_name] = {
                        "name": room_name,
                        "device_count": 0,
                        "device_types": set(),
                    }
                room_data[room_name]["device_count"] += 1
                room_data[room_name]["device_types"].add(device.type)

            # Count scenes per room
            for scene in self._scenes.values():
                room_name = scene.room
                if room_name not in room_data:
                    room_data[room_name] = {
                        "name": room_name,
                        "device_count": 0,
                        "device_types": set(),
                    }
                # Scenes are counted separately
                if "scene_count" not in room_data[room_name]:
                    room_data[room_name]["scene_count"] = 0
                room_data[room_name]["scene_count"] += 1

        # Convert to list format
        rooms = []
        for room_info in room_data.values():
            room_info["device_types"] = sorted(list(room_info["device_types"]))
            room_info["scene_count"] = room_info.get("scene_count", 0)
            rooms.append(room_info)

        # Sort by room name
        rooms.sort(key=lambda r: r["name"])

        logger.debug(f"Listed {len(rooms)} rooms")
        return rooms

    def get_room_devices(self, room: str) -> List[Dict[str, Any]]:
        """
        Get all devices in a specific room with their current states.

        Args:
            room: The room name to filter by

        Returns:
            List of dictionaries containing device information and current states
        """
        if not room:
            return []

        with self._state_lock:
            room_devices = []

            # Get devices in the room
            for device in self._devices.values():
                if device.room == room:
                    device_info = {
                        "uuid": device.uuid,
                        "name": device.name,
                        "type": device.type,
                        "category": device.category,
                        "states": device.states.copy(),
                        "capabilities": device.capabilities.copy(),
                        "is_secured": device.details.get("isSecured", False),
                    }
                    room_devices.append(device_info)

            # Get scenes in the room
            for scene in self._scenes.values():
                if scene.room == room:
                    scene_info = {
                        "uuid": scene.uuid,
                        "name": scene.name,
                        "type": scene.type,
                        "category": scene.category,
                        "states": {},
                        "capabilities": scene.capabilities.copy(),
                        "is_secured": scene.details.get("isSecured", False),
                    }
                    room_devices.append(scene_info)

        # Sort by name
        room_devices.sort(key=lambda d: d["name"])

        logger.debug(f"Found {len(room_devices)} devices/scenes in room '{room}'")
        return room_devices

    async def control_room(self, room: str, action: str) -> Dict[str, Any]:
        """
        Control all compatible devices in a room.

        Args:
            room: The room name
            action: The action to perform ('lights_off', 'all_off', 'lights_on', etc.)

        Returns:
            Dictionary with results of the room control operation
        """
        if not room or not action:
            return {
                "success": False,
                "error": "Room name and action are required",
                "affected_devices": [],
            }

        with self._state_lock:
            room_devices = [d for d in self._devices.values() if d.room == room]

        if not room_devices:
            return {
                "success": False,
                "error": f"No devices found in room '{room}'",
                "affected_devices": [],
            }

        affected_devices = []
        errors: list[str] = []

        # Determine which devices to control based on action
        target_devices = []

        if action == "lights_off":
            target_devices = [
                d
                for d in room_devices
                if d.type
                in [
                    DEVICE_TYPE_SWITCH,
                    DEVICE_TYPE_DIMMER,
                    DEVICE_TYPE_LIGHT_CONTROLLER_V2,
                    DEVICE_TYPE_COLOR_PICKER_V2,
                ]
                and d.capabilities.get("supports_on_off", False)
            ]
        elif action == "lights_on":
            target_devices = [
                d
                for d in room_devices
                if d.type
                in [
                    DEVICE_TYPE_SWITCH,
                    DEVICE_TYPE_DIMMER,
                    DEVICE_TYPE_LIGHT_CONTROLLER_V2,
                    DEVICE_TYPE_COLOR_PICKER_V2,
                ]
                and d.capabilities.get("supports_on_off", False)
            ]
        elif action == "all_off":
            target_devices = [
                d for d in room_devices if d.capabilities.get("supports_on_off", False)
            ]
        elif action == "covers_open":
            target_devices = [
                d
                for d in room_devices
                if d.type in [DEVICE_TYPE_JALOUSIE, DEVICE_TYPE_WINDOW, DEVICE_TYPE_GATE]
                and d.capabilities.get("supports_open_close", False)
            ]
        elif action == "covers_close":
            target_devices = [
                d
                for d in room_devices
                if d.type in [DEVICE_TYPE_JALOUSIE, DEVICE_TYPE_WINDOW, DEVICE_TYPE_GATE]
                and d.capabilities.get("supports_open_close", False)
            ]
        else:
            return {
                "success": False,
                "error": f"Unknown action '{action}'. Supported actions: lights_off, lights_on, all_off, covers_open, covers_close",
                "affected_devices": [],
            }

        if not target_devices:
            return {
                "success": True,
                "message": f"No compatible devices found for action '{action}' in room '{room}'",
                "affected_devices": [],
            }

        # Note: Actual command sending would be handled by the ClientSession
        # This method just identifies the devices and prepares the response
        for device in target_devices:
            device_info = {
                "uuid": device.uuid,
                "name": device.name,
                "type": device.type,
                "action_planned": action,
            }
            affected_devices.append(device_info)

        result = {
            "success": True,
            "message": f"Room control '{action}' prepared for {len(affected_devices)} devices in '{room}'",
            "affected_devices": affected_devices,
            "room": room,
            "action": action,
        }

        if errors:
            result["errors"] = errors

        logger.info(
            f"Room control '{action}' prepared for {len(affected_devices)} devices in room '{room}'"
        )
        return result

    def get_enhanced_device_state(self, uuid: str) -> Dict[str, Any]:
        """
        Get comprehensive device state with all available parameters.

        This method addresses the issue where device state queries return empty objects
        by providing detailed state information including capabilities, metadata, and
        comprehensive state structures based on device type.

        Args:
            uuid: The UUID of the device

        Returns:
            Dictionary containing comprehensive device state information
        """
        device = self.get_device(uuid)
        if not device:
            return {"error": "Device not found", "uuid": uuid, "success": False}

        # Build comprehensive state structure based on device type
        enhanced_state = self.build_state_structure(device)

        # Check if state cache is empty and add status information
        with self._state_lock:
            if not device.states or len(device.states) == 0:
                logger.warning(
                    f"Empty state cache for device {uuid} ({device.name}), may need state update"
                )
                enhanced_state["metadata"]["cache_status"] = "empty_requesting_update"
                enhanced_state["metadata"]["cache_empty"] = True
            else:
                enhanced_state["metadata"]["cache_status"] = "populated"
                enhanced_state["metadata"]["cache_empty"] = False

        enhanced_state["success"] = True
        return enhanced_state

    def build_state_structure(self, device: LoxoneDevice) -> Dict[str, Any]:
        """
        Build device-specific state structure with all relevant parameters.

        Creates a comprehensive state structure that includes device information,
        current state values, capabilities, and metadata tailored to the specific
        device type.

        Args:
            device: The LoxoneDevice instance

        Returns:
            Dictionary containing the complete state structure
        """
        import time

        base_structure = {
            "uuid": device.uuid,
            "name": device.name,
            "type": device.type,
            "room": device.room,
            "category": device.category,
            "state": {},
            "capabilities": device.capabilities.copy() if device.capabilities else {},
            "metadata": {
                "state_parameters": [],
                "last_updated": device.states.get("_last_updated"),
                "reachable": device.states.get("_reachable", True),
                "timestamp": time.time(),
                "device_details": device.details.copy() if device.details else {},
            },
        }

        # Device-type specific state building
        if device.type in [DEVICE_TYPE_DIMMER, DEVICE_TYPE_LIGHT_CONTROLLER_V2]:
            return self._build_light_state(device, base_structure)
        elif device.type == DEVICE_TYPE_COLOR_PICKER_V2:
            return self._build_color_light_state(device, base_structure)
        elif device.type in [DEVICE_TYPE_JALOUSIE, DEVICE_TYPE_WINDOW, DEVICE_TYPE_GATE]:
            return self._build_cover_state(device, base_structure)
        elif device.type == DEVICE_TYPE_IROOM_CONTROLLER_V2:
            return self._build_climate_state(device, base_structure)
        elif device.type in [DEVICE_TYPE_SWITCH, DEVICE_TYPE_TIMED_SWITCH, DEVICE_TYPE_PUSHBUTTON]:
            return self._build_switch_state(device, base_structure)
        elif device.type == DEVICE_TYPE_VENTILATION:
            return self._build_fan_state(device, base_structure)
        elif device.type in [DEVICE_TYPE_AUDIO_ZONE_V2, DEVICE_TYPE_MEDIA_PLAYER]:
            return self._build_media_player_state(device, base_structure)
        elif device.type in [DEVICE_TYPE_ALARM, DEVICE_TYPE_CENTRAL_ALARM]:
            return self._build_alarm_state(device, base_structure)
        elif device.type == DEVICE_TYPE_SLIDER:
            return self._build_slider_state(device, base_structure)
        elif device.type == DEVICE_TYPE_TEXT_INPUT:
            return self._build_text_input_state(device, base_structure)
        elif device.type in [DEVICE_TYPE_INFO_ONLY_ANALOG, DEVICE_TYPE_INFO_ONLY_DIGITAL]:
            return self._build_sensor_state(device, base_structure)
        elif device.type == SCENE_TYPE:
            return self._build_scene_state(device, base_structure)
        else:
            return self._build_generic_state(device, base_structure)

    def extract_device_capabilities(self, device_config: Dict[str, Any]) -> Dict[str, Any]:
        """
        Extract device capabilities from LoxAPP3.json device configuration.

        Analyzes the device configuration from the Loxone structure to determine
        what capabilities and features the device supports.

        Args:
            device_config: Device configuration dictionary from LoxAPP3.json

        Returns:
            Dictionary containing detected device capabilities
        """
        capabilities = {}
        device_type = device_config.get("type", "")
        details = device_config.get("details", {})
        states = device_config.get("states", {})

        # Extract capabilities based on available states and details
        if "value" in states:
            capabilities["has_value_state"] = True

        if "position" in states:
            capabilities["has_position_state"] = True
            capabilities["position_range"] = [0, 100]  # Standard Loxone range

        if "tilt" in states:
            capabilities["has_tilt_state"] = True
            capabilities["tilt_range"] = [0, 100]

        if "temperature" in states:
            capabilities["has_temperature_state"] = True
            capabilities["temperature_unit"] = "°C"

        if "targetTemperature" in states:
            capabilities["supports_target_temperature"] = True

        if "mode" in states:
            capabilities["has_mode_state"] = True

        if "volume" in states:
            capabilities["has_volume_state"] = True
            capabilities["volume_range"] = [0, 100]

        if "color" in states:
            capabilities["supports_color"] = True
            capabilities["color_modes"] = ["rgb", "hsv"]

        # Extract ranges from details
        if "min" in details and "max" in details:
            capabilities["value_range"] = [details["min"], details["max"]]

        if "step" in details:
            capabilities["step"] = details["step"]

        if "unit" in details:
            capabilities["unit"] = details["unit"]

        # Security capabilities
        if device_config.get("isSecured", False):
            capabilities["requires_pin"] = True

        # Type-specific capability detection
        if device_type in [
            DEVICE_TYPE_DIMMER,
            DEVICE_TYPE_LIGHT_CONTROLLER_V2,
            DEVICE_TYPE_COLOR_PICKER_V2,
        ]:
            capabilities["supports_brightness"] = True
            capabilities["supports_on_off"] = True

        if device_type in [DEVICE_TYPE_JALOUSIE, DEVICE_TYPE_WINDOW, DEVICE_TYPE_GATE]:
            capabilities["supports_position"] = True
            capabilities["supports_open_close"] = True
            capabilities["supports_stop"] = True

        if device_type == DEVICE_TYPE_JALOUSIE:
            capabilities["supports_tilt"] = True

        return capabilities

    def _build_light_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for light devices (Dimmer, LightControllerV2)."""
        value = device.states.get("value", 0.0)

        # Convert value to appropriate format
        if isinstance(value, str):
            try:
                value = float(value)
            except (ValueError, TypeError):
                value = 0.0

        base["state"] = {
            "value": value,
            "on": value > 0,
            "brightness": value,
            "brightness_pct": value,  # Loxone uses 0-100 scale
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        # Add additional states if available
        if "active" in device.states:
            base["state"]["active"] = device.states["active"]

        base["capabilities"].update(
            {
                "min_brightness": 0,
                "max_brightness": 100,
                "step": 1,
                "supports_dimming": True,
                "supports_color": False,
                "supports_on_off": True,
            }
        )

        base["metadata"].update(
            {
                "units": "%",
                "format": "%.1f%%",
                "state_parameters": ["value", "on", "brightness", "brightness_pct"],
                "control_type": "dimmer",
            }
        )

        return base

    def _build_color_light_state(
        self, device: LoxoneDevice, base: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Build enhanced state for color light devices (ColorPickerV2)."""
        value = device.states.get("value", 0.0)
        color = device.states.get("color", 0)

        # Convert value to appropriate format
        if isinstance(value, str):
            try:
                value = float(value)
            except (ValueError, TypeError):
                value = 0.0

        # Convert color to hex format if it's a number
        color_hex = "#000000"
        if isinstance(color, (int, float)):
            color_hex = f"#{int(color):06x}"
        elif isinstance(color, str) and color.startswith("#"):
            color_hex = color

        base["state"] = {
            "value": value,
            "on": value > 0,
            "brightness": value,
            "brightness_pct": value,
            "color": color,
            "color_hex": color_hex,
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        base["capabilities"].update(
            {
                "min_brightness": 0,
                "max_brightness": 100,
                "step": 1,
                "supports_dimming": True,
                "supports_color": True,
                "supports_on_off": True,
                "color_modes": ["rgb", "hsv"],
                "supports_white_value": True,
            }
        )

        base["metadata"].update(
            {
                "units": "%",
                "format": "%.1f%%",
                "state_parameters": [
                    "value",
                    "on",
                    "brightness",
                    "brightness_pct",
                    "color",
                    "color_hex",
                ],
                "control_type": "color_light",
            }
        )

        return base

    def _build_cover_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for cover devices (Jalousie, Window, Gate)."""
        position = device.states.get("position", 0.0)
        tilt = device.states.get("tilt", 0.0) if device.type == DEVICE_TYPE_JALOUSIE else None

        # Convert values to appropriate format
        if isinstance(position, str):
            try:
                position = float(position)
            except (ValueError, TypeError):
                position = 0.0

        base["state"] = {
            "position": position,
            "position_pct": position,
            "closed": position == 0,
            "open": position == 100,
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        # Add tilt for Jalousie devices
        if tilt is not None:
            if isinstance(tilt, str):
                try:
                    tilt = float(tilt)
                except (ValueError, TypeError):
                    tilt = 0.0
            base["state"]["tilt"] = tilt
            base["state"]["tilt_pct"] = tilt

        # Add movement state if available
        if "active" in device.states:
            base["state"]["moving"] = device.states["active"]

        capabilities = {
            "min_position": 0,
            "max_position": 100,
            "step": 1,
            "supports_position": True,
            "supports_open_close": True,
            "supports_stop": True,
        }

        if device.type == DEVICE_TYPE_JALOUSIE:
            capabilities.update({"supports_tilt": True, "min_tilt": 0, "max_tilt": 100})

        base["capabilities"].update(capabilities)

        state_params = ["position", "position_pct", "closed", "open"]
        if tilt is not None:
            state_params.extend(["tilt", "tilt_pct"])

        base["metadata"].update(
            {
                "units": "%",
                "format": "%.1f%%",
                "state_parameters": state_params,
                "control_type": "cover",
            }
        )

        return base

    def _build_climate_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for climate devices (IRoomControllerV2)."""
        temperature = device.states.get("temperature", 20.0)
        target_temp = device.states.get("targetTemperature", 20.0)
        mode = device.states.get("mode", 0)

        # Convert values to appropriate format
        if isinstance(temperature, str):
            try:
                temperature = float(temperature)
            except (ValueError, TypeError):
                temperature = 20.0

        if isinstance(target_temp, str):
            try:
                target_temp = float(target_temp)
            except (ValueError, TypeError):
                target_temp = 20.0

        # Map mode number to string
        mode_map = {0: "off", 1: "heat", 2: "cool", 3: "auto"}
        mode_str = mode_map.get(mode, "off")

        base["state"] = {
            "temperature": temperature,
            "current_temperature": temperature,
            "target_temperature": target_temp,
            "mode": mode,
            "mode_str": mode_str,
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        # Add additional climate states if available
        if "humidity" in device.states:
            base["state"]["humidity"] = device.states["humidity"]

        if "active" in device.states:
            base["state"]["heating"] = device.states["active"] == 1
            base["state"]["cooling"] = device.states["active"] == 2

        base["capabilities"].update(
            {
                "min_temperature": 5.0,
                "max_temperature": 35.0,
                "temperature_step": 0.5,
                "temperature_unit": "°C",
                "hvac_modes": ["off", "heat", "cool", "auto"],
                "supports_target_temperature": True,
                "supports_current_temperature": True,
            }
        )

        base["metadata"].update(
            {
                "units": "°C",
                "format": "%.1f°C",
                "state_parameters": [
                    "temperature",
                    "current_temperature",
                    "target_temperature",
                    "mode",
                    "mode_str",
                ],
                "control_type": "climate",
            }
        )

        return base

    def _build_switch_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for switch devices."""
        value = device.states.get("value", 0)
        active = device.states.get("active", 0)

        # Convert to boolean
        is_on = bool(value) or bool(active)

        base["state"] = {
            "value": value,
            "active": active,
            "on": is_on,
            "state": "on" if is_on else "off",
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        base["capabilities"].update({"supports_on_off": True})

        base["metadata"].update(
            {"state_parameters": ["value", "active", "on", "state"], "control_type": "switch"}
        )

        return base

    def _build_fan_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for fan/ventilation devices."""
        value = device.states.get("value", 0.0)
        active = device.states.get("active", 0)

        if isinstance(value, str):
            try:
                value = float(value)
            except (ValueError, TypeError):
                value = 0.0

        base["state"] = {
            "value": value,
            "active": active,
            "on": value > 0 or bool(active),
            "speed": value,
            "speed_pct": value,
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        base["capabilities"].update(
            {
                "min_speed": 0,
                "max_speed": 100,
                "step": 1,
                "supports_on_off": True,
                "supports_speed": True,
                "fan_modes": ["auto", "low", "medium", "high"],
            }
        )

        base["metadata"].update(
            {
                "units": "%",
                "format": "%.1f%%",
                "state_parameters": ["value", "active", "on", "speed", "speed_pct"],
                "control_type": "fan",
            }
        )

        return base

    def _build_media_player_state(
        self, device: LoxoneDevice, base: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Build enhanced state for media player devices."""
        volume = device.states.get("volume", 0.0)
        active = device.states.get("active", 0)
        mode = device.states.get("mode", 0)

        if isinstance(volume, str):
            try:
                volume = float(volume)
            except (ValueError, TypeError):
                volume = 0.0

        # Map mode to playback state
        state_map = {0: "idle", 1: "playing", 2: "paused"}
        playback_state = state_map.get(mode, "idle")

        base["state"] = {
            "volume": volume,
            "volume_pct": volume,
            "active": active,
            "mode": mode,
            "state": playback_state,
            "is_playing": mode == 1,
            "is_paused": mode == 2,
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        base["capabilities"].update(
            {
                "min_volume": 0,
                "max_volume": 100,
                "volume_step": 1,
                "supports_play_pause": True,
                "supports_stop": True,
                "supports_volume": True,
                "media_content_types": ["music", "radio", "playlist"],
            }
        )

        base["metadata"].update(
            {
                "units": "%",
                "format": "%.1f%%",
                "state_parameters": [
                    "volume",
                    "volume_pct",
                    "active",
                    "mode",
                    "state",
                    "is_playing",
                    "is_paused",
                ],
                "control_type": "media_player",
            }
        )

        return base

    def _build_alarm_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for alarm devices."""
        active = device.states.get("active", 0)
        armed = device.states.get("armed", 0)

        # Map alarm states
        if armed:
            alarm_state = "armed"
        elif active:
            alarm_state = "triggered"
        else:
            alarm_state = "disarmed"

        base["state"] = {
            "active": active,
            "armed": armed,
            "state": alarm_state,
            "is_armed": bool(armed),
            "is_triggered": bool(active),
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        base["capabilities"].update(
            {
                "supports_arm_disarm": True,
                "requires_code": True,
                "alarm_states": ["disarmed", "armed", "triggered"],
            }
        )

        base["metadata"].update(
            {
                "state_parameters": ["active", "armed", "state", "is_armed", "is_triggered"],
                "control_type": "alarm",
            }
        )

        return base

    def _build_slider_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for slider input devices."""
        value = device.states.get("value", 0.0)

        if isinstance(value, str):
            try:
                value = float(value)
            except (ValueError, TypeError):
                value = 0.0

        min_val = device.details.get("min", 0)
        max_val = device.details.get("max", 100)
        step = device.details.get("step", 1)
        unit = device.details.get("unit", "")

        base["state"] = {
            "value": value,
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        base["capabilities"].update(
            {
                "min_value": min_val,
                "max_value": max_val,
                "step": step,
                "unit": unit,
                "supports_numeric_input": True,
            }
        )

        base["metadata"].update(
            {
                "units": unit,
                "format": f"%.{len(str(step).split('.')[-1]) if '.' in str(step) else 0}f{unit}",
                "state_parameters": ["value"],
                "control_type": "slider",
            }
        )

        return base

    def _build_text_input_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for text input devices."""
        text = device.states.get("text", "")

        base["state"] = {
            "text": text,
            "value": text,
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        max_length = device.details.get("maxLength", 255)

        base["capabilities"].update({"supports_text_input": True, "max_length": max_length})

        base["metadata"].update(
            {"state_parameters": ["text", "value"], "control_type": "text_input"}
        )

        return base

    def _build_sensor_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for sensor devices."""
        value = device.states.get("value", 0)

        # Determine if it's a binary or analog sensor
        is_binary = device.type == DEVICE_TYPE_INFO_ONLY_DIGITAL

        base["state"] = {
            "value": value,
            "last_changed": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        if is_binary:
            base["state"]["on"] = bool(value)
            base["state"]["state"] = "on" if bool(value) else "off"

        unit = device.details.get("unit", "")

        base["capabilities"].update(
            {
                "read_only": True,
                "sensor_type": "digital" if is_binary else "analog",
                "binary_sensor": is_binary,
                "unit": unit,
            }
        )

        state_params = ["value"]
        if is_binary:
            state_params.extend(["on", "state"])

        base["metadata"].update(
            {"units": unit, "state_parameters": state_params, "control_type": "sensor"}
        )

        return base

    def _build_scene_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build enhanced state for scene devices."""
        active = device.states.get("active", 0)

        base["state"] = {
            "active": active,
            "is_active": bool(active),
            "last_triggered": device.states.get("_last_changed"),
            "reachable": device.states.get("_reachable", True),
        }

        base["capabilities"].update({"triggerable": True, "scene_control": True})

        base["metadata"].update(
            {"state_parameters": ["active", "is_active", "last_triggered"], "control_type": "scene"}
        )

        return base

    def _build_generic_state(self, device: LoxoneDevice, base: Dict[str, Any]) -> Dict[str, Any]:
        """Build generic state for unknown or unsupported device types."""
        # Include all available states as-is
        base["state"] = device.states.copy()

        # Add basic reachability
        if "_reachable" not in base["state"]:
            base["state"]["_reachable"] = True

        base["metadata"].update(
            {"state_parameters": list(device.states.keys()), "control_type": "generic"}
        )

        return base
