"""State container for Versatile Thermostat.

This lightweight data class is intended to group together the minimal
public state we may want to persist, serialize, expose via services
or exchange between managers (for example during algorithms decisions
or for debug tooling).

Attributes:
    hvac_mode: Current HVAC mode of the thermostat (e.g. 'heat', 'off', 'cool', 'sleep').
    target_temperature: The active target temperature (can be None when mode does not use a temperature, e.g. 'off').
    preset: The current preset name (e.g. 'eco', 'comfort', 'boost', 'away') or None if not in a preset.
"""
from __future__ import annotations

from typing import Optional, Any

from .vtherm_hvac_mode import VThermHvacMode
from .vtherm_preset import VThermPreset


class VThermState:
    """Simple state snapshot for a VTherm.

    The object is mutable; use set_state for a bulk update or assign
    attributes individually via properties. to_dict / from_dict help (de)serialization.
    """

    def __init__(self, hvac_mode: Any, target_temperature: Optional[float] = None, preset: Optional[VThermPreset] = None) -> None:
        if preset is not None and not isinstance(preset, str):
            raise ValueError(f"Invalid preset: {preset}. Should be an instance of VThermPreset.")

        self._hvac_mode: VThermHvacMode = hvac_mode if isinstance(hvac_mode, VThermHvacMode) else VThermHvacMode(str(hvac_mode))
        self._target_temperature: Optional[float] = target_temperature
        self._preset: Optional[VThermPreset] = preset
        self._is_hvac_mode_changed: bool = True
        self._is_target_temperature_changed: bool = True
        self._is_preset_changed: bool = True

    def set_state(
        self,
        hvac_mode: Optional[VThermHvacMode] = None,
        target_temperature: Optional[float] = None,
        preset: Optional[VThermPreset] = None,
    ) -> None:
        """Update only the attributes provided (not None).

        Args:
            hvac_mode: New HVAC mode to set (if not None).
            target_temperature: New target temperature (if not None).
            preset: New preset (if not None).
        """
        if hvac_mode is not None:
            self.set_hvac_mode(hvac_mode)
        if target_temperature is not None:
            self.set_target_temperature(target_temperature)
        if preset is not None:
            self.set_preset(preset)

    def set_hvac_mode(self, hvac_mode: VThermHvacMode) -> None:
        """Set the HVAC mode only.

        Args:
            hvac_mode: New HVAC mode to set (e.g. 'heat', 'off', 'cool', 'sleep').
        """
        if not isinstance(hvac_mode, VThermHvacMode):
            hvac_mode = VThermHvacMode(str(hvac_mode))

        self._is_hvac_mode_changed = self._is_hvac_mode_changed or self._hvac_mode != hvac_mode
        self._hvac_mode = hvac_mode

    def set_target_temperature(self, target_temperature: Optional[float]) -> None:
        """Set the target temperature only.

        Args:
            target_temperature: New target temperature to set, or None.
        """
        self._is_target_temperature_changed = self._is_target_temperature_changed or self._target_temperature != target_temperature
        self._target_temperature = target_temperature

    def set_preset(self, preset: Optional[VThermPreset]) -> None:
        """Set the preset only.

        Args:
            preset: New preset name to set, or None.
        """
        if preset is not None and not isinstance(preset, str):
            raise ValueError(f"Invalid preset: {preset}. Should be an instance of VThermPreset.")

        self._is_preset_changed = self._is_preset_changed or self._preset != preset
        self._preset = preset

    def force_changed(self) -> None:
        """Forcefully mark the state as changed."""
        self._is_hvac_mode_changed = True
        self._is_target_temperature_changed = True
        self._is_preset_changed = True

    @property
    def hvac_mode(self) -> VThermHvacMode:
        """Get or set the current HVAC mode."""
        return self._hvac_mode

    @property
    def target_temperature(self) -> Optional[float]:
        """Get or set the current target temperature."""
        return self._target_temperature

    @property
    def preset(self) -> Optional[VThermPreset]:
        """Get or set the current preset."""
        return self._preset

    @property
    def is_changed(self) -> bool:
        """Check if the state has changed."""
        return self._is_hvac_mode_changed or self._is_target_temperature_changed or self._is_preset_changed

    @property
    def is_hvac_mode_changed(self) -> bool:
        """Check if the HVAC mode has changed."""
        return self._is_hvac_mode_changed

    @property
    def is_target_temperature_changed(self) -> bool:
        """Check if the target temperature has changed."""
        return self._is_target_temperature_changed

    @property
    def is_preset_changed(self) -> bool:
        """Check if the preset has changed."""
        return self._is_preset_changed

    def reset_changed(self) -> None:
        """Reset the changed state."""
        self._is_hvac_mode_changed = False
        self._is_target_temperature_changed = False
        self._is_preset_changed = False

    def to_dict(self) -> dict[str, Any]:
        """Convert the state to a dictionary."""
        return {
            "hvac_mode": str(self._hvac_mode),
            "target_temperature": self._target_temperature,
            "preset": str(self._preset),
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "VThermState":
        """Create a VThermState from a dictionary."""
        hvac_mode = VThermHvacMode(data["hvac_mode"])
        target_temperature = data["target_temperature"]
        preset = VThermPreset(data["preset"]) if data["preset"] else None
        state = cls(hvac_mode, target_temperature, preset)
        return state

    def __str__(self) -> str:
        """Return a human readable representation of the state."""
        return f"VThermState(" f"hvac_mode={self._hvac_mode}, " f"target_temperature={self._target_temperature}, " f"preset={self._preset}, " f"is_changed={self.is_changed})"

    __repr__ = __str__

    def __eq__(self, other: object) -> bool:
        """Compare two VThermState instances for equality.

        Args:
            other: Another object to compare with.

        Returns:
            True if both states have the same hvac_mode, target_temperature, and preset.
        """
        if not isinstance(other, VThermState):
            return False

        return self._hvac_mode == other._hvac_mode and self._target_temperature == other._target_temperature and self._preset == other._preset

    def __ne__(self, other: object) -> bool:
        """Compare two VThermState instances for inequality."""
        return not self.__eq__(other)
