"""
Phase 104: Circuit Breaker

Prevents the autonomous Swarm from causing cascading failures. If agents fail 
multiple times consecutively (e.g., repeatedly breaking tests or writing syntax errors),
the circuit breaker trips into the OPEN state, halting autonomous loops until a human
intervenes or a timeout occurs.
"""

import json
import logging
from pathlib import Path
from datetime import datetime, timezone

logger = logging.getLogger("brynq.circuit_breaker")

BREAKER_FILE = Path("state/broker/evolution/circuit_breaker.json")

def _ensure_dir():
    BREAKER_FILE.parent.mkdir(parents=True, exist_ok=True)

def _load_state() -> dict:
    if BREAKER_FILE.exists():
        try:
            return json.loads(BREAKER_FILE.read_text("utf-8"))
        except:
            pass
    return {
        "state": "CLOSED", # CLOSED, OPEN, HALF_OPEN
        "consecutive_failures": 0,
        "last_failure_at": None,
        "trip_reason": None
    }

def _save_state(state: dict):
    _ensure_dir()
    BREAKER_FILE.write_text(json.dumps(state, indent=2))

def record_success():
    """Reset the failure counter and close the breaker on a successful run."""
    state = _load_state()
    if state["state"] == "HALF_OPEN":
        logger.info("Circuit Breaker: Success during HALF_OPEN. Resetting to CLOSED.")
    
    state["state"] = "CLOSED"
    state["consecutive_failures"] = 0
    state["trip_reason"] = None
    _save_state(state)

def record_failure(reason: str, threshold: int = 3):
    """Record a failure and trip the breaker if the threshold is reached."""
    state = _load_state()
    
    if state["state"] == "OPEN":
        return # Already open
        
    state["consecutive_failures"] += 1
    state["last_failure_at"] = datetime.now(timezone.utc).isoformat()
    
    logger.warning(f"Circuit Breaker: Recorded failure {state['consecutive_failures']}/{threshold}: {reason}")
    
    if state["consecutive_failures"] >= threshold:
        state["state"] = "OPEN"
        state["trip_reason"] = f"Exceeded {threshold} consecutive failures. Last reason: {reason}"
        logger.error(f"CIRCUIT BREAKER TRIPPED: {state['trip_reason']}")
        
    _save_state(state)

def is_execution_allowed(cooldown_minutes: int = 60) -> bool:
    """Check if the system is allowed to execute autonomous changes."""
    state = _load_state()
    
    if state["state"] == "CLOSED":
        return True
        
    if state["state"] == "OPEN":
        # Check if we should transition to HALF_OPEN based on cooldown
        if not state["last_failure_at"]:
            return False
            
        try:
            last_fail = datetime.fromisoformat(state["last_failure_at"])
            age_minutes = (datetime.now(timezone.utc) - last_fail).total_seconds() / 60
            
            if age_minutes >= cooldown_minutes:
                logger.info(f"Circuit Breaker: Cooldown of {cooldown_minutes}m elapsed. Transitioning to HALF_OPEN.")
                state["state"] = "HALF_OPEN"
                _save_state(state)
                return True
        except:
            pass
            
        return False
        
    if state["state"] == "HALF_OPEN":
        # Only allow 1 execution in half-open state to test the waters
        return True
        
    return False

def get_status() -> dict:
    return _load_state()
