# src/veritell_langchain/fail_open.py

from __future__ import annotations

from .errors import RateLimitedError, ServiceUnavailableError, VeritellError, VeritellTimeoutError
from .schema import VeritellEvaluationResult


def _reason_for_fail_open(exc: Exception) -> str:
    # Prefer stable sentinel reasons required by tests, even if the server error body is unknown.
    status = getattr(exc, "status", None)
    if status == 429:
        return "rate_limited"
    if status == 503:
        return "service_unavailable"

    if isinstance(exc, VeritellTimeoutError):
        return "timeout"
    if isinstance(exc, RateLimitedError):
        return "rate_limited"
    if isinstance(exc, ServiceUnavailableError):
        return "service_unavailable"

    err = getattr(exc, "error", None)
    if isinstance(err, str) and err:
        return err
    return "unknown_error"


def fail_open_or_raise(*, exc: Exception, fail_open: bool) -> VeritellEvaluationResult:
    if not fail_open:
        raise exc

    if isinstance(exc, (VeritellTimeoutError, RateLimitedError, ServiceUnavailableError)):
        reason = _reason_for_fail_open(exc)
        return VeritellEvaluationResult.default(reason=reason, fail_open=True)

    # Also handle generic VeritellError with status set (e.g., raised by http layer).
    if isinstance(exc, VeritellError):
        status = getattr(exc, "status", None)
        if status in (429, 503):
            reason = _reason_for_fail_open(exc)
            return VeritellEvaluationResult.default(reason=reason, fail_open=True)

    raise exc
