"""Governance decision types.

InterventionAction defines what the system does when verification flags fire.
GovernanceDecision is the full decision record — action, evidence, and audit fields.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum, auto

from aurora_lens.verify.flags import Flag


class InterventionAction(Enum):
    """What the governance engine does with a flagged response."""
    PASS = auto()            # Clean — no flags, or flags below threshold
    SOFT_CORRECT = auto()    # Response delivered; correction in metadata only
    FORCE_REVISE = auto()    # Re-prompt LLM with flag context + PEF ground truth
    CONTAIN = auto()         # No determinations; acknowledge + ask intent (EXTRACTION_EMPTY)
    HARD_STOP = auto()       # Block response entirely


_ACTION_TO_ESCALATION = {
    InterventionAction.PASS: 0,         # ADMIT
    InterventionAction.CONTAIN: 1,       # CLARIFY
    InterventionAction.SOFT_CORRECT: 2,  # REFUSE
    InterventionAction.FORCE_REVISE: 2,  # REFUSE (deprecated, maps to 2)
    InterventionAction.HARD_STOP: 3,     # STOP
}


@dataclass
class GovernanceDecision:
    """Full governance decision record.

    Carries everything needed for audit: what happened, why, and context.
    escalation_level is derived from action — for compatibility/telemetry only.
    When canonical Governor fields are present (pathway_id is not None), the
    execution layer must branch on pathway_id, not on escalation_level.

    Canonical pathway fields (set by the Governor bridge, ``CanonicalScannerGateBridge.decide()``):
        pathway_id:           ContinuationPathway value string (e.g. "P_STOP_TERMINAL")
        output_mode:          OutputMode value string (e.g. "terminal_stop")
        commitment_closed:    True when no determination may be issued or recovered
        interaction_open:     True when the user may continue the conversation
        forensic_obligations: List of ForensicObligation value strings
        resolution_mode:      ResolutionMode value string (for audit provenance)

    forensic_event: canonical envelope for non-ADMIT outcomes; set by bridge when logging.
    """
    action: InterventionAction
    flags: list[Flag]
    rationale: str
    policy: str = "strict"                     # Policy that produced this decision
    rule_id: str | None = None                 # PolicyRule.rule_id that produced this action (audit)
    attempt: int = 0                           # Revision attempt (0 = first pass)
    original_response: str | None = None       # Pre-intervention response
    corrected_response: str | None = None      # Post-intervention response
    governed_response: str | None = None       # Exact text returned to caller after pathway rendering
    governance_note: str | None = None         # Human-readable note (for SOFT_CORRECT)
    cid: str | None = None                     # Content identifier (if available)
    safe_alt: str | None = None                # Phase 11: optional alternative for refuse template
    resource: str | None = None                # Phase 11/12: escalation resource (e.g. crisis support)
    forensic_event: dict | None = None         # Canonical forensic envelope (non-ADMIT only)

    # Canonical Governor continuation fields.
    # Populated by ``CanonicalScannerGateBridge.decide()`` from RuntimeDecisionProjection.
    # When pathway_id is present, enforce() branches on these rather than escalation_level.
    pathway_id: str | None = None
    output_mode: str | None = None
    commitment_closed: bool = True
    interaction_open: bool = False
    forensic_obligations: list[str] = field(default_factory=list)
    resolution_mode: str | None = None

    # Error-containment audit fields.
    # Set by enforce() when a Tier 3/4 fallback fires — either an unmapped hostile
    # flag type or a malformed decision reaching the bare stop.
    # These are not product features; they signal unexpected states that require
    # operator attention. When True, unexpected_unclassified_termination = True
    # and fallback_reason names the classification of the gap.
    unexpected_unclassified_termination: bool = False
    fallback_reason: str | None = None

    # Structural governor verdict (Phase 1).
    # Set by lens.py after StructuralGovernor.evaluate(). The verdict is the
    # authoritative decision; synthetic flags are rendering adapters only.
    governor_verdict: object | None = None

    @property
    def escalation_level(self) -> int:
        """0=ADMIT, 1=CLARIFY, 2=REFUSE, 3=STOP. Derived from action.

        Retained for compatibility and telemetry. Do NOT use as the execution
        branch when pathway_id is present — use pathway_id instead.
        """
        return _ACTION_TO_ESCALATION.get(self.action, 0)
