"""Sensor platform for Climate Scheduler."""
import logging
from datetime import timedelta
from typing import Any

from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorEntity,
    SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.util import dt as dt_util

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

# How many historical samples to keep for derivative calculation
SAMPLE_SIZE = 10


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up Climate Scheduler sensors from a config entry."""
    from homeassistant.helpers import device_registry as dr, entity_registry as er
    
    storage = hass.data[DOMAIN]["storage"]
    coordinator = hass.data[DOMAIN]["coordinator"]
    
    # Get settings to check if derivative sensors are enabled
    settings = storage._data.get("settings", {})
    sensors = []
    
    # Always create coldest and warmest sensors
    sensors.append(ColdestEntitySensor(hass, storage, coordinator))
    sensors.append(WarmestEntitySensor(hass, storage, coordinator))
    
    if not settings.get("create_derivative_sensors", True):
        async_add_entities(sensors, True)
        return
    
    # Get registries
    entity_registry = er.async_get(hass)
    device_registry = dr.async_get(hass)
    
    # Get all entities from groups (both single and multi-entity groups)
    all_entity_ids = await storage.async_get_all_entities()
    
    for entity_id in all_entity_ids:
        # Skip ignored entities
        if await storage.async_is_ignored(entity_id):
            continue
        
        # Find the device for this climate entity
        entity_entry = entity_registry.async_get(entity_id)
        _LOGGER.debug(f"Entity entry for {entity_id}: {entity_entry}")
        
        device_id = None
        if entity_entry and entity_entry.device_id:
            device_id = entity_entry.device_id
            _LOGGER.debug(f"Found device {device_id} for {entity_id}")
        
        # Create main temperature rate sensor
        _LOGGER.debug(f"Creating derivative sensor for {entity_id}")
        sensors.append(ClimateSchedulerRateSensor(hass, entity_id, entity_id, "current_temperature", device_id))
        
        if device_id:
            # Find all sensor entities on the same device
            device_sensors = [
                entry for entry in entity_registry.entities.values()
                if entry.device_id == device_id 
                and entry.domain == "sensor"
                and "floor" in entry.entity_id.lower()  # Look for "floor" in the entity_id
            ]
            
            _LOGGER.debug(f"Found {len(device_sensors)} floor sensors for {entity_id}: {[s.entity_id for s in device_sensors]}")
            
            # Create derivative sensors for floor temperature sensors
            for floor_sensor in device_sensors:
                _LOGGER.debug(f"Creating floor derivative sensor for {entity_id} tracking {floor_sensor.entity_id}")
                sensors.append(ClimateSchedulerRateSensor(
                    hass, 
                    entity_id,  # Associated climate entity
                    floor_sensor.entity_id,  # Floor sensor to track
                    "state",  # Use state instead of attribute
                    device_id  # Link to same device
                ))
        else:
            _LOGGER.warning(f"No device found for {entity_id}, skipping floor sensor detection")
    
    if sensors:
        async_add_entities(sensors, True)


class ClimateSchedulerRateSensor(SensorEntity):
    """Sensor that tracks the rate of temperature change for a climate entity."""

    def __init__(
        self, 
        hass: HomeAssistant, 
        climate_entity_id: str, 
        source_entity_id: str,
        temperature_attribute: str = "current_temperature",
        device_id: str = None
    ) -> None:
        """Initialize the sensor."""
        self.hass = hass
        self._climate_entity_id = climate_entity_id
        self._source_entity_id = source_entity_id
        self._temperature_attribute = temperature_attribute
        self._device_id = device_id  # Store device_id for device_info
        
        # Extract name from entity_id (e.g., climate.bedroom -> bedroom)
        entity_name = climate_entity_id.split(".")[-1]
        friendly_name = entity_name.replace("_", " ").title()
        
        # Determine if this is a floor sensor (tracking a separate sensor entity)
        is_floor = source_entity_id != climate_entity_id
        
        if is_floor:
            # For floor sensors, include the floor sensor name in unique_id to avoid collisions
            floor_sensor_name = source_entity_id.split(".")[-1]
            # Strip any previously-created climate_scheduler prefix to avoid recursive names
            if floor_sensor_name.startswith("climate_scheduler_"):
                floor_sensor_name = floor_sensor_name[len("climate_scheduler_") :]
            # Strip trailing _rate if present
            if floor_sensor_name.endswith("_rate"):
                floor_sensor_name = floor_sensor_name[: -len("_rate")]

            # Prevent duplicated segments like "front_room_front_room_..."
            if floor_sensor_name.startswith(f"{entity_name}_"):
                floor_sensor_suffix = floor_sensor_name[len(entity_name) + 1 :]
            else:
                floor_sensor_suffix = floor_sensor_name

            suffix = f"{floor_sensor_suffix.replace('_', ' ').title()} Rate"
            unique_suffix = f"{floor_sensor_suffix}_rate"
        else:
            suffix = "Rate"
            unique_suffix = "rate"
        
        _LOGGER.debug(f"Creating sensor climate_scheduler_{entity_name}_{unique_suffix} with device_id: {device_id}")
        
        self._attr_name = f"Climate Scheduler {friendly_name} {suffix}"
        self._attr_unique_id = f"climate_scheduler_{entity_name}_{unique_suffix}"
        # Let Home Assistant manage the `entity_id` from the `unique_id`
        self._attr_device_class = None  # No standard device class for rate
        self._attr_state_class = SensorStateClass.MEASUREMENT
        self._attr_native_unit_of_measurement = "°C/h"
        self._attr_icon = "mdi:thermometer-lines" if not is_floor else "mdi:floor-plan"
        
        # Storage for temperature samples (timestamp, temperature)
        self._samples = []
        self._attr_native_value = 0.0  # Start at 0 instead of None
        
        # Set device_info during initialization to ensure proper device linking
        if device_id:
            device_registry = dr.async_get(hass)
            device = device_registry.async_get(device_id)
            
            if device:
                self._attr_device_info = {
                    "identifiers": device.identifiers,
                }
                _LOGGER.debug(f"Linked sensor {self._attr_unique_id} to device {device_id} with identifiers {device.identifiers}")
            else:
                _LOGGER.error(f"Could not find device {device_id} in registry for sensor {self._attr_unique_id}")
        else:
            _LOGGER.warning(f"No device_id provided for sensor {self._attr_unique_id}")
    
    async def async_added_to_hass(self) -> None:
        """Register state listener when entity is added."""
        # Listen to state changes of the source entity
        self.async_on_remove(
            async_track_state_change_event(
                self.hass,
                [self._source_entity_id],
                self._async_source_state_changed,
            )
        )
        
        # Get initial state
        state = self.hass.states.get(self._source_entity_id)
        if state:
            # For separate sensor entities, use the state value
            if self._temperature_attribute == "state":
                try:
                    temp = float(state.state)
                    now = dt_util.utcnow()
                    self._samples.append((now, temp))
                except (ValueError, TypeError):
                    pass
            # For climate entity attributes, use the attribute
            elif state.attributes.get(self._temperature_attribute) is not None:
                temp = float(state.attributes[self._temperature_attribute])
                now = dt_util.utcnow()
                self._samples.append((now, temp))

    @callback
    def _async_source_state_changed(self, event) -> None:
        """Handle source entity state changes."""
        new_state = event.data.get("new_state")
        if not new_state:
            return
        
        try:
            # For separate sensor entities, use the state value
            if self._temperature_attribute == "state":
                temp = float(new_state.state)
            # For climate entity attributes, use the attribute
            else:
                current_temp = new_state.attributes.get(self._temperature_attribute)
                if current_temp is None:
                    return
                temp = float(current_temp)
            now = dt_util.utcnow()
            
            # Add new sample
            self._samples.append((now, temp))
            
            # Keep only last SAMPLE_SIZE samples
            if len(self._samples) > SAMPLE_SIZE:
                self._samples = self._samples[-SAMPLE_SIZE:]
            
            # Calculate derivative if we have enough samples
            if len(self._samples) >= 2:
                self._calculate_rate()
            
            self.async_write_ha_state()
        except (ValueError, TypeError) as e:
            _LOGGER.debug(f"Error processing temperature for {self._climate_entity_id}: {e}")

    def _calculate_rate(self) -> None:
        """Calculate the rate of temperature change."""
        if len(self._samples) < 2:
            self._attr_native_value = 0.0  # Not enough data, rate is 0
            return
        
        # Get oldest and newest samples
        oldest_time, oldest_temp = self._samples[0]
        newest_time, newest_temp = self._samples[-1]
        
        # Calculate time difference in hours
        time_diff = (newest_time - oldest_time).total_seconds() / 3600
        
        if time_diff == 0:
            self._attr_native_value = 0.0
            return
        
        # Calculate temperature change rate (°C per hour)
        temp_diff = newest_temp - oldest_temp
        rate = temp_diff / time_diff
        
        # Round to 2 decimal places
        self._attr_native_value = round(rate, 2)

    @property
    def extra_state_attributes(self) -> dict[str, Any]:
        """Return additional state attributes."""
        return {
            "climate_entity": self._climate_entity_id,
            "source_entity": self._source_entity_id,
            "temperature_attribute": self._temperature_attribute,
            "sample_count": len(self._samples),
            "time_window_minutes": 5,
        }


class ColdestEntitySensor(SensorEntity):
    """Sensor that shows the coldest climate entity."""

    def __init__(
        self, 
        hass: HomeAssistant,
        storage,
        coordinator
    ) -> None:
        """Initialize the sensor."""
        self.hass = hass
        self._storage = storage
        self._coordinator = coordinator
        
        self._attr_name = "Climate Scheduler Coldest Entity"
        self._attr_unique_id = "climate_scheduler_coldest_entity"
        # Let Home Assistant manage the `entity_id` from the `unique_id`
        self._attr_device_class = SensorDeviceClass.TEMPERATURE
        self._attr_state_class = SensorStateClass.MEASUREMENT
        self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
        self._attr_icon = "mdi:snowflake"
        self._attr_native_value = None
        self._coldest_entity_id = None
        self._coldest_friendly_name = None
        self._remove_listener = None
    
    async def async_added_to_hass(self) -> None:
        """Register state listener when entity is added."""
        # Listen to coordinator updates
        self._remove_listener = self._coordinator.async_add_listener(self._handle_coordinator_update)
        # Initial update
        await self._async_update()

    async def async_will_remove_from_hass(self) -> None:
        """Unregister listener when entity is removed."""
        if self._remove_listener:
            self._remove_listener()

    @callback
    def _handle_coordinator_update(self) -> None:
        """Handle updated data from the coordinator."""
        self.hass.async_create_task(self._async_update())

    async def _async_update(self) -> None:
        """Update the sensor value."""
        # Get all entities from groups
        all_entities = await self._storage.async_get_all_entities()
        
        coldest_temp = None
        coldest_entity = None
        coldest_name = None
        
        for entity_id in all_entities:
            # Skip ignored entities
            if await self._storage.async_is_ignored(entity_id):
                continue
            
            state = self.hass.states.get(entity_id)
            if not state:
                continue
            
            # Check if entity has current_temperature attribute
            current_temp = state.attributes.get("current_temperature")
            if current_temp is None:
                continue
            
            try:
                temp = float(current_temp)
                if coldest_temp is None or temp < coldest_temp:
                    coldest_temp = temp
                    coldest_entity = entity_id
                    coldest_name = state.attributes.get("friendly_name", entity_id)
            except (ValueError, TypeError):
                continue
        
        self._attr_native_value = coldest_temp
        self._coldest_entity_id = coldest_entity
        self._coldest_friendly_name = coldest_name
        self.async_write_ha_state()

    @property
    def extra_state_attributes(self) -> dict[str, Any]:
        """Return additional state attributes."""
        return {
            "entity_id": self._coldest_entity_id,
            "friendly_name": self._coldest_friendly_name,
        }


class WarmestEntitySensor(SensorEntity):
    """Sensor that shows the warmest climate entity."""

    def __init__(
        self, 
        hass: HomeAssistant,
        storage,
        coordinator
    ) -> None:
        """Initialize the sensor."""
        self.hass = hass
        self._storage = storage
        self._coordinator = coordinator
        
        self._attr_name = "Climate Scheduler Warmest Entity"
        self._attr_unique_id = "climate_scheduler_warmest_entity"
        # Let Home Assistant manage the `entity_id` from the `unique_id`
        self._attr_device_class = SensorDeviceClass.TEMPERATURE
        self._attr_state_class = SensorStateClass.MEASUREMENT
        self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
        self._attr_icon = "mdi:fire"
        self._attr_native_value = None
        self._warmest_entity_id = None
        self._warmest_friendly_name = None
        self._remove_listener = None
    
    async def async_added_to_hass(self) -> None:
        """Register state listener when entity is added."""
        # Listen to coordinator updates
        self._remove_listener = self._coordinator.async_add_listener(self._handle_coordinator_update)
        # Initial update
        await self._async_update()

    async def async_will_remove_from_hass(self) -> None:
        """Unregister listener when entity is removed."""
        if self._remove_listener:
            self._remove_listener()

    @callback
    def _handle_coordinator_update(self) -> None:
        """Handle updated data from the coordinator."""
        self.hass.async_create_task(self._async_update())

    async def _async_update(self) -> None:
        """Update the sensor value."""
        # Get all entities from groups
        all_entities = await self._storage.async_get_all_entities()
        
        warmest_temp = None
        warmest_entity = None
        warmest_name = None
        
        for entity_id in all_entities:
            # Skip ignored entities
            if await self._storage.async_is_ignored(entity_id):
                continue
            
            state = self.hass.states.get(entity_id)
            if not state:
                continue
            
            # Check if entity has current_temperature attribute
            current_temp = state.attributes.get("current_temperature")
            if current_temp is None:
                continue
            
            try:
                temp = float(current_temp)
                if warmest_temp is None or temp > warmest_temp:
                    warmest_temp = temp
                    warmest_entity = entity_id
                    warmest_name = state.attributes.get("friendly_name", entity_id)
            except (ValueError, TypeError):
                continue
        
        self._attr_native_value = warmest_temp
        self._warmest_entity_id = warmest_entity
        self._warmest_friendly_name = warmest_name
        self.async_write_ha_state()

    @property
    def extra_state_attributes(self) -> dict[str, Any]:
        """Return additional state attributes."""
        return {
            "entity_id": self._warmest_entity_id,
            "friendly_name": self._warmest_friendly_name,
        }
