"""Gate evaluation for architect phases.

Evaluation order (first match wins):
1. Budget gate: total cost >= max_budget → "fail"
2. Failure gate: phase failed → circuit breaker check → "fail" or "pause"
3. Structural gate: test results, git changes → "continue" or "pause"
"""

from __future__ import annotations

import json
import logging
from dataclasses import dataclass

from cleave.architect.config import ArchitectConfig
from cleave.architect.dag import DAGStore, Phase

logger = logging.getLogger(__name__)


@dataclass
class GateDecision:
    """Result of gate evaluation."""

    decision: str  # continue | pause | fail
    gate_type: str  # budget | failure | structural
    reason: str


def evaluate_gate(
    phase: Phase,
    observation: dict,
    config: ArchitectConfig,
    dag: DAGStore,
) -> GateDecision:
    """Evaluate gates for a completed phase.

    Args:
        phase: The phase that just completed.
        observation: Observer output for this phase.
        config: Architect configuration.
        dag: DAG store for aggregate queries.

    Returns:
        GateDecision with the evaluation result.
    """
    # 1. Budget gate
    total_cost = dag.total_cost()
    if total_cost >= config.max_budget_usd:
        return GateDecision(
            decision="fail",
            gate_type="budget",
            reason=f"Budget exhausted: ${total_cost:.2f} spent >= ${config.max_budget_usd:.2f} limit",
        )

    # 2. Failure gate
    if phase.status == "failed":
        consecutive = dag.consecutive_failures()
        if consecutive >= config.circuit_breaker_threshold:
            return GateDecision(
                decision="fail",
                gate_type="failure",
                reason=f"Circuit breaker: {consecutive} consecutive failures "
                       f"(threshold: {config.circuit_breaker_threshold})",
            )
        return GateDecision(
            decision="pause",
            gate_type="failure",
            reason=f"Phase '{phase.id}' failed: {phase.error_message or 'unknown error'}",
        )

    # 3. Structural gate
    test_results = observation.get("test_results", {})
    git_diff = observation.get("git_diff_summary", {})

    # Tests ran and failed → pause
    if test_results.get("ran") and not test_results.get("passed"):
        return GateDecision(
            decision="pause",
            gate_type="structural",
            reason=f"Tests failed after phase '{phase.id}': {test_results.get('summary', '')}",
        )

    # No git changes produced → pause
    if git_diff.get("files_changed", 0) == 0 and not git_diff.get("error"):
        orchestrator = observation.get("orchestrator_result", {})
        if orchestrator.get("success"):
            # Phase "succeeded" but produced no changes — suspicious
            return GateDecision(
                decision="pause",
                gate_type="structural",
                reason=f"Phase '{phase.id}' completed but produced no git changes",
            )

    # Phase succeeded → continue
    return GateDecision(
        decision="continue",
        gate_type="structural",
        reason=f"Phase '{phase.id}' completed successfully",
    )
