"""ANU v3.1 Codex CC Decision Loop (Phase 1 Core).

Reference: ANU v3 master spec section 5.1, 5.2, 5.3.

Pure-logic adapter that converts a Codex CC review/re-review payload into a
structured verdict plus a CHAIR_HOLD / AUTO_EXECUTE recommendation.  This
module does NOT touch the filesystem, network, GitHub, dispatch, or any live
runtime.  Production write / dev bot dispatch / merge / auto-merge are not
performed here (forbidden 1, 3, 4, 5, 6, 7 per task-2662 md).

Used by `utils.anu_codex_micro_refinement_loop` to translate the Codex
verdict-of-record into a refinement loop control signal.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, List, Mapping, Optional

CODEX_PASS = "PASS"
CODEX_PASS_WITH_RECOMMENDATIONS = "PASS_WITH_RECOMMENDATIONS"
CODEX_FAIL = "FAIL"
CODEX_UNKNOWN = "UNKNOWN"

ALLOWED_CODEX_VERDICTS = frozenset(
    {CODEX_PASS, CODEX_PASS_WITH_RECOMMENDATIONS, CODEX_FAIL, CODEX_UNKNOWN}
)


@dataclass(frozen=True)
class CodexDecision:
    """Structured verdict result returned by `evaluate`.

    `auto_execute_allowed` is `True` only when (a) verdict ∈ {PASS,
    PASS_WITH_RECOMMENDATIONS} AND (b) `critical_7` is `False` AND (c) no
    safety flag listed in section 5.3 is set.
    """

    task_id: str
    review_round: int
    codex_final_verdict: str
    critical_7: bool
    auto_execute_allowed: bool
    recommendations: List[Dict[str, Any]] = field(default_factory=list)
    decision_required: bool = False
    recommended_next_action: str = "ASK_CHAIR"

    def as_dict(self) -> Dict[str, Any]:
        return {
            "schema": "anu_v3.codex_cc_decision_loop.v1",
            "task_id": self.task_id,
            "loop_kind": "risk_bounded_decision",
            "review_round": self.review_round,
            "codex_final_verdict": self.codex_final_verdict,
            "critical_7": self.critical_7,
            "auto_execute_allowed": self.auto_execute_allowed,
            "recommendations": list(self.recommendations),
            "decision_required": self.decision_required,
            "recommended_next_action": self.recommended_next_action,
        }


def _normalize_verdict(raw: Any) -> str:
    if not isinstance(raw, str):
        return CODEX_UNKNOWN
    candidate = raw.strip().upper()
    return candidate if candidate in ALLOWED_CODEX_VERDICTS else CODEX_UNKNOWN


def evaluate(
    *,
    task_id: str,
    review_round: int,
    codex_payload: Mapping[str, Any],
    safety_signal: Optional[Mapping[str, bool]] = None,
) -> CodexDecision:
    """Translate a Codex payload into a CodexDecision.

    `codex_payload` is expected to carry at least `codex_final_verdict` and
    `critical_7`.  `safety_signal` is an optional dict containing the live
    safety probe results (e.g. permission_expansion, github_write_required,
    real_write_required, forbidden_write_target, scope_expansion).  When any
    safety probe is true the decision is forced to `auto_execute_allowed=False`
    and `decision_required=True` regardless of the verdict.
    """

    if not isinstance(task_id, str) or not task_id:
        raise ValueError("task_id required")
    if not isinstance(review_round, int) or review_round < 1:
        raise ValueError("review_round must be a positive int")

    verdict = _normalize_verdict(codex_payload.get("codex_final_verdict"))
    critical_7 = bool(codex_payload.get("critical_7", False))
    recommendations = list(codex_payload.get("recommendations", []) or [])

    safety = dict(safety_signal or {})
    safety_blocked = critical_7 or any(bool(v) for v in safety.values())

    verdict_pass = verdict in (CODEX_PASS, CODEX_PASS_WITH_RECOMMENDATIONS)
    auto_execute = verdict_pass and not safety_blocked

    decision_required = safety_blocked or verdict in (CODEX_FAIL, CODEX_UNKNOWN)
    recommended_next_action = (
        "AUTO_EXECUTE_EXISTING_AUTOMATION" if auto_execute else "ASK_CHAIR"
    )

    return CodexDecision(
        task_id=task_id,
        review_round=review_round,
        codex_final_verdict=verdict,
        critical_7=critical_7,
        auto_execute_allowed=auto_execute,
        recommendations=recommendations,
        decision_required=decision_required,
        recommended_next_action=recommended_next_action,
    )
