"""Tests for retry_dispatcher schema-error classification — Phase 3."""

import sys
from pathlib import Path
from types import SimpleNamespace

# Path setup so these tests can import workspace + pipeline modules
_RECOIL_ROOT = Path(__file__).resolve().parent.parent
_PIPELINE_ROOT = _RECOIL_ROOT / "pipeline"
for _p in [str(_RECOIL_ROOT), str(_PIPELINE_ROOT)]:
    if _p not in sys.path:
        sys.path.insert(0, _p)

from orchestrator.retry_dispatcher import classify_failure
from orchestrator.production_types import DEFAULT_RETRY_POLICIES, FailureCategory


def mk_result(error, final_state=""):
    """Build a minimal step-result mock for classify_failure."""
    return SimpleNamespace(error=error, final_state=final_state, gate_verdict=None)


# ── Schema / 422 classification ────────────────────────────────────

def test_http_422_classified_as_prompt_duration_mismatch():
    result = mk_result("HTTP 422: fal.ai rejected the request")
    assert classify_failure(result) == FailureCategory.PROMPT_DURATION_MISMATCH


def test_input_should_be_string_classified_as_prompt_duration_mismatch():
    result = mk_result("Input should be a valid integer — fal validation error")
    assert classify_failure(result) == FailureCategory.PROMPT_DURATION_MISMATCH


def test_unprocessable_classified_as_prompt_duration_mismatch():
    result = mk_result("422 Unprocessable Entity: duration out of range")
    assert classify_failure(result) == FailureCategory.PROMPT_DURATION_MISMATCH


def test_schema_error_does_not_shadow_transient():
    """Transient check runs before schema check; 429 + 422 in same error → TRANSIENT."""
    result = mk_result("429 rate limit exceeded; also 422 unprocessable")
    assert classify_failure(result) == FailureCategory.TRANSIENT


# ── Default retry policy exists ─────────────────────────────────────

def test_default_retry_policy_exists_for_new_category():
    """DEFAULT_RETRY_POLICIES must have an entry for PROMPT_DURATION_MISMATCH."""
    policy = DEFAULT_RETRY_POLICIES[FailureCategory.PROMPT_DURATION_MISMATCH]
    assert policy.max_retries == 2
    assert policy.base_backoff_seconds == 0.0
