"""Escalation — knowing when to stop and ask the human.

Escalation isn't failure. It's the agent recognizing: "This decision
is bigger than me alone." The best agents escalate early, not late.

The escalation engine looks at a Projection and decides:
- Can I proceed on my own?
- Should I ask first?
- Must I stop and wait?
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional

from .types import (
    AlignmentConfig,
    Lens,
    Projection,
    Severity,
    SelfState,
    SEVERITY_ORDER,
)


class EscalationLevel:
    """Named escalation levels with descriptions."""

    NONE = "none"  # No escalation needed — proceed
    INFORM = "inform"  # Proceed but tell the owner what you did
    ASK = "ask"  # Ask the owner before proceeding
    HALT = "halt"  # Stop everything and wait for the owner


@dataclass
class EscalationDecision:
    """The result of the escalation engine's analysis."""

    level: str  # EscalationLevel value
    reason: str  # Why this level
    blocking_lenses: list[str]  # Which lenses triggered the escalation
    message_to_owner: str  # What to say when escalating (empty if NONE)
    can_timeout: bool  # Can the agent auto-proceed after a timeout?
    timeout_seconds: float = 0.0  # How long to wait before auto-proceeding (if can_timeout)

    def to_dict(self) -> dict:
        return {
            "level": self.level,
            "reason": self.reason,
            "blocking_lenses": self.blocking_lenses,
            "message_to_owner": self.message_to_owner,
            "can_timeout": self.can_timeout,
            "timeout_seconds": self.timeout_seconds,
        }


def decide_escalation(
    projection: Projection,
    self_state: SelfState,
    config: AlignmentConfig,
) -> EscalationDecision:
    """Given a projection, decide whether and how to escalate.

    This is the final gate before action. The projection tells us what
    the lenses see. This function decides what to do about it.
    """

    # STOP severity always halts — no exceptions, no timeouts
    if projection.overall_severity == Severity.STOP:
        blocking = [lr.lens.value for lr in projection.blocking_lenses]
        concerns = "; ".join(
            f"{lr.lens.value}: {lr.concern}"
            for lr in projection.blocking_lenses
            if lr.concern
        )
        return EscalationDecision(
            level=EscalationLevel.HALT,
            reason=f"STOP severity triggered by: {', '.join(blocking)}",
            blocking_lenses=blocking,
            message_to_owner=(
                f"I need your decision before proceeding.\n\n"
                f"Action: {projection.action_description}\n"
                f"Concerns: {concerns}\n\n"
                f"Should I proceed?"
            ),
            can_timeout=False,
        )

    # YIELD with escalation enabled
    if projection.overall_severity == Severity.YIELD and config.escalate_on_yield:
        blocking = [lr.lens.value for lr in projection.blocking_lenses]
        concerns = "; ".join(
            f"{lr.lens.value}: {lr.concern}"
            for lr in projection.lens_results
            if lr.severity == Severity.YIELD and lr.concern
        )
        return EscalationDecision(
            level=EscalationLevel.ASK,
            reason=f"YIELD severity — asking before proceeding: {', '.join(blocking)}",
            blocking_lenses=blocking,
            message_to_owner=(
                f"I'd like your input on this.\n\n"
                f"Action: {projection.action_description}\n"
                f"Considerations: {concerns}\n\n"
                f"Should I proceed, or take a different approach?"
            ),
            can_timeout=True,
            timeout_seconds=3600.0,  # 1 hour before auto-proceeding on YIELD
        )

    # Agent is in a state where it should defer most decisions
    if self_state.should_defer():
        return EscalationDecision(
            level=EscalationLevel.HALT,
            reason="Agent self-state indicates possible compromise or conflicting signals.",
            blocking_lenses=["self_state"],
            message_to_owner=(
                f"My self-diagnostics indicate I may not be in a trustworthy state.\n\n"
                f"Status: {self_state.degradation_summary}\n"
                f"Action I was about to take: {projection.action_description}\n\n"
                f"I'm holding all non-essential actions until you confirm."
            ),
            can_timeout=False,
        )

    # CAUTION — proceed but inform
    if projection.overall_severity == Severity.CAUTION:
        caution_lenses = [
            lr.lens.value for lr in projection.lens_results
            if lr.severity == Severity.CAUTION
        ]
        return EscalationDecision(
            level=EscalationLevel.INFORM,
            reason=f"Caution noted from: {', '.join(caution_lenses)}",
            blocking_lenses=[],
            message_to_owner="",  # Will be logged, not sent
            can_timeout=True,
        )

    # All clear
    return EscalationDecision(
        level=EscalationLevel.NONE,
        reason="All lenses clear. Proceeding normally.",
        blocking_lenses=[],
        message_to_owner="",
        can_timeout=True,
    )
