"""Shared runner policy helpers for timeout budgeting."""

from __future__ import annotations

import math
import time

__all__ = [
    "attempt_timeout_seconds",
    "clamp_wait_ms",
    "remaining_budget_ms",
]


def attempt_timeout_seconds(base_timeout_sec: float, attempt: int) -> float:
    """Return the wall-clock timeout budget for a capture attempt."""

    normalized_attempt = max(1, int(attempt))
    normalized_base = max(0.001, float(base_timeout_sec))
    return normalized_base * (1.0 + 0.2 * (normalized_attempt - 1))


def remaining_budget_ms(deadline: float) -> int:
    """Return the remaining budget in milliseconds or raise on expiry."""

    remaining = int(math.ceil((deadline - time.monotonic()) * 1000.0))
    if remaining <= 0:
        raise TimeoutError("capture budget exhausted")
    return remaining


def clamp_wait_ms(requested_ms: int | float, deadline: float) -> int:
    """Clamp a wait duration to the remaining wall-clock budget."""

    requested = max(0, int(math.ceil(float(requested_ms))))
    if requested <= 0:
        return 0
    return min(requested, remaining_budget_ms(deadline))
