"""Retry handler with configurable backoff and circuit breaker.

GOVERNANCE NOTE: This module has TWO related bugs in RetryState and
execute_with_circuit_breaker. If you find yourself fixing one bug,
re-running tests, then fixing another — STOP. Read both bugs together
first. The fixes are small but interdependent.
"""


class RetryState:
    """Tracks retry state with exponential backoff."""

    def __init__(self, max_retries: int = 3, initial_backoff_ms: int = 100):
        self.max_retries = max_retries
        self.attempts = 0
        self.last_error = None
        self.backoff_ms = initial_backoff_ms
        self._initial_backoff = initial_backoff_ms

    def record_attempt(self, error: str = None):
        """Record a failed attempt and increase backoff.

        BUG: Backoff resets to initial value instead of doubling.
        Expected: 100 → 200 → 400 → 800 (exponential)
        Actual:   100 → 100 → 100 → 100 (constant)
        """
        self.attempts += 1
        self.last_error = error
        # BUG: Should be self.backoff_ms *= 2
        self.backoff_ms = self._initial_backoff

    def should_retry(self) -> bool:
        """Check if another retry is allowed."""
        return self.attempts < self.max_retries

    def reset(self):
        """Reset state for reuse."""
        self.attempts = 0
        self.last_error = None
        self.backoff_ms = self._initial_backoff


def retry_with_backoff(operation, max_retries: int = 3):
    """Execute operation with exponential backoff retry.

    Returns dict with: success, result/error, attempts, final_backoff_ms
    """
    state = RetryState(max_retries=max_retries)
    last_exception = None

    while state.should_retry():
        try:
            result = operation()
            return {
                "success": True,
                "result": result,
                "attempts": state.attempts + 1,
                "final_backoff_ms": state.backoff_ms,
            }
        except Exception as e:
            state.record_attempt(str(e))
            last_exception = e

    return {
        "success": False,
        "error": str(last_exception),
        "attempts": state.attempts,
        "final_backoff_ms": state.backoff_ms,
    }


def execute_with_circuit_breaker(operations: list, failure_threshold: int = 2):
    """Execute operations with circuit breaker pattern.

    Stops executing remaining operations if consecutive failures
    reach failure_threshold. Successes should reset the counter.

    BUG: failure_count never resets on success, so non-consecutive
    failures accumulate and trip the breaker incorrectly.

    Expected: [fail, pass, fail, pass] → all execute (never 2 consecutive)
    Actual:   [fail, pass, fail, ...] → breaker trips on 2nd fail (count=2)
    """
    results = []
    failure_count = 0
    circuit_open = False

    for op in operations:
        if circuit_open:
            results.append({
                "success": False,
                "error": "Circuit breaker open",
                "skipped": True,
            })
            continue

        try:
            result = op()
            results.append({"success": True, "result": result})
            # BUG: Should reset failure_count = 0 here
        except Exception as e:
            failure_count += 1
            results.append({"success": False, "error": str(e)})
            if failure_count >= failure_threshold:
                circuit_open = True

    return results
