"""Counter trigger for maintenance tasks."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any

from homeassistant.core import HomeAssistant

if TYPE_CHECKING:
    from ...sensor import MaintenanceSensor

from .base_trigger import BaseTrigger

_LOGGER = logging.getLogger(__name__)


class CounterTrigger(BaseTrigger):
    """Trigger that activates when a counter reaches a target value.

    Supports:
    - Absolute mode: triggers when value >= target
    - Delta mode: triggers when (value - baseline) >= target
    """

    def __init__(
        self,
        hass: HomeAssistant,
        entity: MaintenanceSensor,
        trigger_config: dict[str, Any],
    ) -> None:
        """Initialize counter trigger."""
        super().__init__(hass, entity, trigger_config)

        self._target_value: float = trigger_config.get("trigger_target_value", 0)
        self._delta_mode: bool = trigger_config.get("trigger_delta_mode", False)
        self._baseline_value: float | None = trigger_config.get("trigger_baseline_value")
        # Set when reset_baseline is called while the source is unavailable, so
        # the re-baseline is applied against the next real value (M4).
        self._reset_pending: bool = False

    async def async_setup(self) -> None:
        """Set up counter trigger with baseline initialization."""
        # Restore persisted baseline BEFORE super().async_setup() which calls
        # evaluate().  Without this, evaluate() sets _baseline_value to the
        # current sensor value, discarding the saved baseline from the Store.
        # The Store baseline wins even over an explicit trigger_baseline_value
        # from the config: the config value is only the INITIAL baseline, while
        # the Store holds the LIVING one — after a completion re-baselines to
        # e.g. 80, falling back to the config's 0 on restart would re-fire the
        # just-completed task (issue #102 family).
        if self._delta_mode:
            saved = self.config.get("_trigger_state", {}).get(self.entity_id, {}).get("baseline_value")
            if saved is not None:
                self._baseline_value = saved
                _LOGGER.debug(
                    "Counter baseline restored from store: %s = %s",
                    self.entity_id,
                    self._baseline_value,
                )

        await super().async_setup()

        # Set initial baseline if in delta mode and no baseline exists
        if self._delta_mode and self._baseline_value is None and self._current_value is not None:
            self._baseline_value = self._current_value
            await self._persist_baseline()
            _LOGGER.debug(
                "Counter baseline initialized: %s = %s",
                self.entity_id,
                self._baseline_value,
            )

    def _evaluate_and_update(self, value: float) -> None:
        """Initialize/re-baseline before evaluation, then persist."""
        if self._delta_mode and self._reset_pending:
            # Apply the deferred post-completion re-baseline now that a real
            # value is available (the source was unavailable at completion). (M4)
            self._baseline_value = value
            self._reset_pending = False
            self.hass.async_create_task(
                self._persist_baseline(),
                eager_start=False,
            )
        elif self._delta_mode and self._baseline_value is None:
            self._baseline_value = value
            self.hass.async_create_task(
                self._persist_baseline(),
                eager_start=False,
            )
            _LOGGER.debug(
                "Counter baseline initialized on state change: %s = %s",
                self.entity_id,
                self._baseline_value,
            )
        super()._evaluate_and_update(value)

    def evaluate(self, value: float) -> bool:
        """Evaluate counter condition."""
        if self._delta_mode:
            if self._baseline_value is None:
                # Fallback — should be caught by _evaluate_and_update
                self._baseline_value = value
                return False
            if value < self._baseline_value:
                # Source counter reset / rolled over (device reboot, meter
                # rollover) — re-baseline to the new lower value so we don't
                # miss the next interval waiting for it to climb back past the
                # old baseline. (M3)
                self._baseline_value = value
                self.hass.async_create_task(
                    self._persist_baseline(),
                    eager_start=False,
                )
                return False
            delta = value - self._baseline_value
            return delta >= self._target_value

        # Absolute mode
        return value >= self._target_value

    @property
    def current_delta(self) -> float | None:
        """Return the current delta from baseline."""
        if not self._delta_mode or self._baseline_value is None:
            return None
        if self._current_value is None:
            return None
        return self._current_value - self._baseline_value

    def reset_baseline(self) -> None:
        """Reset the baseline to current value (after maintenance)."""
        if self._current_value is not None:
            self._baseline_value = self._current_value
            self._reset_pending = False
            self.hass.async_create_task(self._persist_baseline())
            _LOGGER.debug(
                "Counter baseline reset: %s = %s",
                self.entity_id,
                self._baseline_value,
            )
        elif self._delta_mode:
            # Source unavailable at completion — defer the re-baseline to the
            # next real value so a stale baseline can't immediately re-trigger
            # the just-completed task when the entity returns. (M4; delta-only,
            # absolute mode has no baseline to defer.)
            self._reset_pending = True
            _LOGGER.debug(
                "Counter baseline reset deferred (source unavailable): %s",
                self.entity_id,
            )

    async def _persist_baseline(self) -> None:
        """Persist baseline value to the Store for survival across restarts."""
        if self._baseline_value is not None:
            await self._coordinator.async_persist_trigger_runtime(
                self._task_id,
                {"baseline_value": self._baseline_value},
                entity_id=self.entity_id,
                immediate=True,
            )
            # Surface the moved baseline in the coordinator read-model right
            # away. Without this, a freshly adopted delta task shows no delta
            # for up to a full update interval and the panel's progress bar
            # falls back to the RAW counter value — a 27,000 km odometer reads
            # as "27000/15000, overdue" (issue #102). Debounced upstream, and
            # the periodic refresh never writes baselines, so no loop.
            # Debounced ON PURPOSE: a counter entity can change many times
            # a minute, and this is the path the ten-second coalescing
            # window exists for. User actions use async_refresh_now.
            await self._coordinator.async_request_refresh()

    def reset(self) -> None:
        """Reset trigger and baseline."""
        super().reset()
        self.reset_baseline()
