"""Self-state monitoring — the agent's awareness of its own condition.

"Have I eaten? Have I slept? Everything else fails if this machine fails."

This module detects when the agent is operating in a degraded state
and adjusts behavior accordingly. It's not introspection for its own sake —
it's the practical reality that a compromised machine makes compromised decisions.
"""

from __future__ import annotations

import time
from typing import Optional

from .types import (
    AlignmentConfig,
    SelfState,
    SelfStateFlag,
)


class SelfMonitor:
    """Monitors agent health and maintains self-state awareness.

    Call check() periodically (before major decisions, or on a timer).
    The monitor updates flags based on observable conditions.
    """

    def __init__(self, config: AlignmentConfig) -> None:
        self._config = config
        self._state = SelfState()
        self._decision_timestamps: list[float] = []  # Rolling window of decision times
        self._last_check: float = 0.0
        self._forced_flags: set[SelfStateFlag] = set()  # Manually set flags that survive check()
        self._flag_change_log: list[tuple[float, str, SelfStateFlag]] = []  # (timestamp, action, flag)
        self._min_flag_interval: float = 60.0  # Minimum seconds between force/clear of same flag

    @property
    def state(self) -> SelfState:
        return self._state

    def record_owner_interaction(self) -> None:
        """Call this whenever the owner sends a message or provides input."""
        self._state.last_owner_interaction = time.time()

    def record_decision(self, confidence: float) -> None:
        """Call this after every alignment decision."""
        now = time.time()
        self._decision_timestamps.append(now)
        self._state.confidence_trend.append(confidence)

        # Keep rolling windows manageable
        cutoff = now - 300  # 5 minutes for rapid decision detection
        self._decision_timestamps = [t for t in self._decision_timestamps if t > cutoff]
        self._state.confidence_trend = self._state.confidence_trend[-100:]
        self._state.recent_decision_count = len(self._decision_timestamps)

    def report_tool_health(self, tool_name: str, is_working: bool) -> None:
        """Report the health status of an external tool or service."""
        self._state.tool_health[tool_name] = is_working

    def update_context_age(self, age_seconds: float) -> None:
        """Update how old the agent's freshest information is."""
        self._state.context_age_seconds = age_seconds

    def check(self) -> SelfState:
        """Run a full self-state check. Returns the updated state.

        This is the agent asking itself: "Am I okay? Can I trust my own decisions?"
        """
        now = time.time()
        self._last_check = now
        flags: list[SelfStateFlag] = []

        # Check context freshness
        if self._state.context_age_seconds > self._config.stale_context_seconds:
            flags.append(SelfStateFlag.STALE_CONTEXT)

        # Check tool health
        unhealthy_tools = [
            name for name, ok in self._state.tool_health.items() if not ok
        ]
        if unhealthy_tools:
            flags.append(SelfStateFlag.TOOL_DEGRADED)
            self._state.notes = f"Degraded tools: {', '.join(unhealthy_tools)}"

        # Check decision rate
        if self._state.recent_decision_count > self._config.max_decisions_per_minute:
            flags.append(SelfStateFlag.RAPID_DECISIONS)

        # Check confidence trend
        avg = self._state.average_confidence()
        if avg < self._config.confidence_floor and len(self._state.confidence_trend) >= 3:
            flags.append(SelfStateFlag.HIGH_UNCERTAINTY)

        # Check owner presence
        if self._state.last_owner_interaction > 0:
            hours = (now - self._state.last_owner_interaction) / 3600
            if hours > self._config.owner_absent_hours:
                flags.append(SelfStateFlag.OWNER_ABSENT)

        # Include any manually forced flags (these survive check cycles)
        for forced in self._forced_flags:
            if forced not in flags:
                flags.append(forced)

        # If no issues found, we're healthy
        if not flags:
            flags = [SelfStateFlag.HEALTHY]

        self._state.flags = flags
        return self._state

    def force_flag(self, flag: SelfStateFlag) -> None:
        """Manually set a flag with cooldown enforcement."""
        now = time.time()
        # Check cooldown — prevent rapid cycling
        recent_changes = [
            t for t, action, f in self._flag_change_log
            if f == flag and now - t < self._min_flag_interval
        ]
        if len(recent_changes) >= 2:
            raise RuntimeError(
                f"Flag {flag.value} changed too rapidly. "
                f"Minimum interval: {self._min_flag_interval}s. "
                "This may indicate manipulation."
            )
        self._flag_change_log.append((now, "force", flag))
        self._forced_flags.add(flag)
        if SelfStateFlag.HEALTHY in self._state.flags:
            self._state.flags.remove(SelfStateFlag.HEALTHY)
        if flag not in self._state.flags:
            self._state.flags.append(flag)

    def clear_flag(self, flag: SelfStateFlag) -> None:
        """Manually clear a flag with cooldown enforcement."""
        now = time.time()
        recent_changes = [
            t for t, action, f in self._flag_change_log
            if f == flag and now - t < self._min_flag_interval
        ]
        if len(recent_changes) >= 2:
            raise RuntimeError(
                f"Flag {flag.value} changed too rapidly. "
                f"Minimum interval: {self._min_flag_interval}s."
            )
        self._flag_change_log.append((now, "clear", flag))
        self._forced_flags.discard(flag)
        if flag in self._state.flags:
            self._state.flags.remove(flag)
        if not self._state.flags:
            self._state.flags = [SelfStateFlag.HEALTHY]

    def snapshot(self) -> dict:
        """Export current state for logging / build cache."""
        return {
            "timestamp": time.time(),
            "flags": [f.value for f in self._state.flags],
            "is_healthy": self._state.is_healthy,
            "context_age_s": self._state.context_age_seconds,
            "recent_decisions": self._state.recent_decision_count,
            "avg_confidence": round(self._state.average_confidence(), 3),
            "hours_since_owner": round(self._state.hours_since_owner, 1),
            "tool_health": self._state.tool_health,
            "notes": self._state.notes,
        }
