import json, os, re, shutil, time
import pytest
from recoil.pipeline._lib.opus_oauth import call_opus_oauth, OpusOAuthError

RUN = os.getenv("RECOIL_RUN_LIVE_OAUTH") == "1"          # opt-IN to run at all
REQUIRE = os.getenv("RECOIL_REQUIRE_LIVE_OAUTH") == "1"   # binary MUST be present (no false-skip)


def _extract_json(raw: str) -> str:
    """Parse the SAME way production does: tolerate a ```json fenced block."""
    try:
        json.loads(raw)
        return raw
    except Exception:
        m = re.search(r"```(?:json)?\s*(.*?)```", raw, re.DOTALL)
        return m.group(1).strip() if m else raw


@pytest.mark.live_oauth
@pytest.mark.skipif(not RUN, reason="opt-in: set RECOIL_RUN_LIVE_OAUTH=1")
def test_oauth_emits_json_not_narration():
    """A real OAuth structured call returns JSON (possibly fenced), not a prose
    confirmation. Faithful to the real failure: requires top-level scalars + an array
    (the 'do not omit top-level keys' contract Phase 1 adds). Tiny => fast + $0."""
    if shutil.which("claude") is None:
        if REQUIRE:
            pytest.fail("live OAuth REQUIRED but `claude` not on PATH — fix the gate's PATH, do NOT skip")
        pytest.skip("no claude CLI on PATH")

    schema = {
        "type": "object",
        "required": ["episode_id", "total_shots", "items"],
        "properties": {
            "episode_id": {"type": "string"},
            "total_shots": {"type": "integer"},
            "items": {"type": "array", "items": {"type": "object",
                "required": ["idx", "label"],
                "properties": {"idx": {"type": "integer"}, "label": {"type": "string"}}}},
        },
    }
    system = "You are a JSON emitter. Output only the requested JSON object."
    user = ('Emit episode_id="EP001", total_shots=2, and items = exactly 2 objects with '
            'idx 1..2 and a one-word label each.')

    # Transient OAuth infra errors → retry. Under REQUIRE (the build gate),
    # retry-exhaustion FAILS (a skip would let the gate green via `1 skipped` —
    # pytest exits 0 on skips — defeating the whole point). Only a non-REQUIRE
    # local run skips on infra outage. The chassis re-dispatches transient
    # failures with backoff, so a genuine blip recovers on the next attempt;
    # a persistent narration bug fails every attempt → CAPS → human.
    raw, last_exc = None, None
    for attempt in range(3):
        try:
            raw = call_opus_oauth("claude-opus-4-8", system, user, json_schema=schema, timeout=300)
            break
        except OpusOAuthError as e:
            last_exc = e
            time.sleep(2 * (attempt + 1))
    if raw is None:
        if REQUIRE:
            pytest.fail(f"live OAuth REQUIRED but unreachable after 3 retries: {last_exc}")
        pytest.skip(f"live OAuth unreachable (infra, not a code failure): {last_exc}")

    # From here, any failure is a REAL bug (narration / wrong content) → fail hard.
    payload = _extract_json(raw)
    obj = json.loads(payload)   # narration has no JSON object → JSONDecodeError → FAIL
    assert obj["episode_id"] == "EP001", f"wrong content: {raw[:160]!r}"
    assert obj["total_shots"] == 2, f"missing/!=2 total_shots: {raw[:160]!r}"
    assert len(obj["items"]) == 2 and obj["items"][0]["idx"] == 1, f"bad items: {raw[:160]!r}"
