"""Heating decision value object."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timedelta
from enum import Enum


class HeatingAction(Enum):
    """Types of heating actions that can be taken."""

    START_HEATING = "start_heating"
    STOP_HEATING = "stop_heating"
    SET_TEMPERATURE = "set_temperature"
    NO_ACTION = "no_action"


@dataclass(frozen=True)
class HeatingDecision:
    """Represents a decision about heating control.

    This value object encapsulates what action should be taken and why.

    Attributes:
        action: The type of action to take
        target_temp: Target temperature if starting heating (None otherwise)
        reason: Human-readable explanation for the decision
    """

    action: HeatingAction
    target_temp: float | None = None
    reason: str = ""

    def __post_init__(self) -> None:
        """Validate the heating decision data."""
        if self.action == HeatingAction.START_HEATING and self.target_temp is None:
            raise ValueError("START_HEATING action requires a target temperature")

        if self.action == HeatingAction.SET_TEMPERATURE and self.target_temp is None:
            raise ValueError("SET_TEMPERATURE action requires a target temperature")


@dataclass(frozen=True)
class TariffPeriodDetail:
    """Represents energy consumption and cost details for a specific tariff period."""

    tariff_price_eur_per_kwh: float
    energy_kwh: float
    heating_duration_minutes: float
    cost_euro: float


@dataclass(frozen=True)
class HeatingCycle:
    """Represents a single heating cycle, encapsulating all its relevant data.

    This value object provides a complete and immutable snapshot of a heating period,
    including its duration, temperature changes, and energy consumption details.

    Attributes:
        start_time: The exact datetime when the heating cycle started.
        end_time: The exact datetime when the heating cycle ended.
        target_temp: The target temperature set for this heating cycle.
        end_temp: The actual temperature reached at the end of the heating cycle.
        start_temp: The temperature at the beginning of the heating cycle.
        tariff_details: A list of TariffDetail objects, breaking down energy, duration,
                        and cost by specific TariffPeriodDetail periods within the cycle.
        dead_time_cycle_minutes: Dead time for this specific cycle in minutes. Time from
                                 cycle start to first measurable temperature change.
                                 None if cannot be determined.
        min_effective_duration_minutes: Minimum effective heating duration (in minutes) required
                                        to compute a valid slope. Effective duration is
                                        total_duration − dead_time. Cycles whose effective window
                                        is shorter than this threshold return 0.0 for
                                        ``avg_heating_slope`` to prevent aberrant values caused by
                                        near-zero denominators. Defaults to 5.0 minutes.
    """

    device_id: str
    start_time: datetime
    end_time: datetime
    target_temp: float
    end_temp: float
    start_temp: float
    tariff_details: list[TariffPeriodDetail] | None = None
    dead_time_cycle_minutes: float | None = None
    min_effective_duration_minutes: float = 5.0

    @property
    def avg_heating_slope(self) -> float:
        """Calculates the average heating slope in °C/hour for the heating cycle.

        Excludes the dead_time_cycle period to get the true heating slope once
        the system is actively heating (without initial inertia).

        Returns 0.0 when the effective heating duration (after subtracting dead_time) is shorter
        than ``min_effective_duration_minutes``. This guards against aberrant slope values that
        arise when dead_time ≈ total_duration, leaving an effective duration of only a few
        microseconds and producing slopes in the range of 100 000–200 000 °C/h.
        """
        # Calculate effective start time (after dead_time_cycle)
        if self.dead_time_cycle_minutes and self.dead_time_cycle_minutes > 0:
            effective_start_time = self.start_time + timedelta(minutes=self.dead_time_cycle_minutes)
            duration_hours = (self.end_time - effective_start_time).total_seconds() / 3600
        else:
            duration_hours = (self.end_time - self.start_time).total_seconds() / 3600

        if duration_hours <= 0:
            return 0.0

        # Guard: reject cycles whose effective heating window is too narrow.
        # When dead_time ≈ total_duration the slope formula amplifies noise by orders of magnitude.
        effective_duration_minutes = duration_hours * 60.0
        if effective_duration_minutes < self.min_effective_duration_minutes:
            return 0.0

        temp_increase = self.end_temp - self.start_temp
        return temp_increase / duration_hours

    @property
    def duration_minutes(self) -> float:
        """Calculates the total duration of the heating cycle in minutes."""
        return (self.end_time - self.start_time).total_seconds() / 60

    @property
    def temp_delta(self) -> float:
        """Calculates the difference between the target temperature and the end temperature."""
        return self.target_temp - self.end_temp

    @property
    def start_hour(self) -> int:
        """Returns the hour (0-23) when the heating cycle started."""
        return self.start_time.hour

    @property
    def end_hour(self) -> int:
        """Returns the hour (0-23) when the heating cycle ended."""
        return self.end_time.hour

    @property
    def start_weekday(self) -> int:
        """Returns the weekday (0=Monday, 6=Sunday) when the heating cycle started."""
        return self.start_time.weekday()

    @property
    def end_weekday(self) -> int:
        """Returns the weekday (0=Monday, 6=Sunday) when the heating cycle ended."""
        return self.end_time.weekday()

    @property
    def total_energy_kwh(self) -> float:
        """Calculates the total energy consumed during the cycle in kWh from tariff details."""
        return sum(detail.energy_kwh for detail in (self.tariff_details or []))

    @property
    def total_heating_duration_minutes(self) -> float:
        """Calculates the total heating duration in minutes from tariff details."""
        return sum(detail.heating_duration_minutes for detail in (self.tariff_details or []))

    @property
    def total_cost_euro(self) -> float:
        """Calculates the total cost in euros from tariff details."""
        return sum(detail.cost_euro for detail in (self.tariff_details or []))

    def __post_init__(self) -> None:
        """Validate the heating cycle data."""
        if self.start_time >= self.end_time:
            raise ValueError("Start time must be before end time for a heating cycle.")
