"""Content smoke tests — gpt-image-2 against real Tartarus assets.

Six smokes designed to surface USABLE output for visual evaluation, not
just an API handshake:

  1. test_character_jade_t2i    — character ref from bible (Jade, phase 1)
  2. test_character_wren_t2i    — character ref from bible (Wren, armored)
  3. test_location_corridor_t2i — env ref (int_lower_decks_corridor)
  4. test_location_shaft_t2i    — env ref (int_lower_decks_maintenance_shaft)
  5. test_previs_ep001_sh01_via_build_previs_prompt
                                  — exact plan-pass code path with the
                                    canonical prompt builder
  6. test_image_to_image_edit_existing_previs
                                  — i2i endpoint on an existing previz frame

Gated by RECOIL_RUN_LIVE_TESTS=1. Per-shot hard ceiling: $1.00 (mirrors
model_profiles.gpt-image-2.max_cost_per_shot_usd). Quality: "medium"
across the board (better signal than "low", well under the ceiling).

Outputs land in /tmp/gpt_image_2_content_smoke/ for review. Each test
prints the absolute path + measured cost on success.

NOTE: gpt-image-2's prompt-tuning needs are not yet codified. These tests
use build_previs_prompt (the generic Flash builder) for #5 — known not to
be tuned for gpt-image-2. Tests #1-4 build natural-language prompts inline
from bible content, which matches gpt-image-2's documented preference.
"""
from __future__ import annotations

import json
import os
import shutil
from pathlib import Path

import pytest

LIVE = os.environ.get("RECOIL_RUN_LIVE_TESTS") == "1"

_OUT_DIR = Path("/tmp/gpt_image_2_content_smoke")
_HARD_BUDGET_CEILING_USD = 1.00  # matches model_profiles.gpt-image-2.max_cost_per_shot_usd
_QUALITY = "medium"
_REPO_ROOT = Path(__file__).resolve().parents[4]
_BIBLE_PATH = _REPO_ROOT / "projects/tartarus/state/visual/global_bible.json"
_PLAN_PATH = _REPO_ROOT / "projects/tartarus/state/visual/plans/ep_001_plan.json"


def _ensure_out_dir():
    _OUT_DIR.mkdir(parents=True, exist_ok=True)


def _bible() -> dict:
    return json.loads(_BIBLE_PATH.read_text())


def _make_ctx(label: str):
    """Build a DispatchContext + StepRunner for a smoke test."""
    from recoil.pipeline.core import dispatch, DispatchContext
    from recoil.execution.step_runner import StepRunner
    from recoil.execution.execution_store import ExecutionStore
    from recoil.execution.step_types import ProjectPaths

    store = ExecutionStore(
        project="tartarus",
        db_path=Path(f"/tmp/gpt_image_2_smoke_store_{label}"),
    )
    paths = ProjectPaths.for_episode("tartarus", 1)
    sr = StepRunner(store=store, paths=paths)
    ctx = DispatchContext(
        caller_id=f"gpt_image_2_smoke_{label}",
        step_runner=sr,
        project="tartarus", episode=1,
        receipts_log_path="DISABLED",
    )
    return dispatch, ctx


def _resolve_output_path(raw: str) -> Path:
    """rr.output_path is project-relative (under projects/{project}/) per
    StepRunner/ProjectPaths convention. Resolve to absolute against the
    canonical projects_root.
    """
    p = Path(raw)
    if p.is_absolute():
        return p
    from recoil.core.paths import projects_root
    return projects_root() / "tartarus" / raw


def _assert_and_save(receipt, dest_name: str) -> tuple[Path, float]:
    """Common post-call assertions + copy output to /tmp/gpt_image_2_content_smoke/."""
    rr = receipt.run_result
    assert rr.success, f"dispatch failed: {rr.error}"
    assert rr.output_path is not None, "no output_path on RunResult"
    src = _resolve_output_path(str(rr.output_path))
    assert src.exists() and src.stat().st_size > 0, (
        f"output file missing or empty at {src}"
    )

    # gpt-image-2 fal billing format gap: _extract_fal_billing_cost_usd
    # doesn't read this endpoint's response shape, so cost_usd often lands
    # as 0.0 with a WARNING. That's a separate substrate fix — don't fail
    # the content smoke on it. Still enforce the hard ceiling for safety
    # if the extractor ever does return a value.
    cost_usd = (rr.metadata or {}).get("cost_usd") or 0.0
    if cost_usd > 0:
        assert cost_usd < _HARD_BUDGET_CEILING_USD, (
            f"cost ${cost_usd:.4f} breached "
            f"${_HARD_BUDGET_CEILING_USD:.2f} per-shot ceiling"
        )

    _ensure_out_dir()
    dest = _OUT_DIR / dest_name
    shutil.copy2(src, dest)
    cost_str = f"${cost_usd:.4f}" if cost_usd > 0 else "$? (extractor gap)"
    print(f"\n[smoke OK] {dest_name} → {dest} ({cost_str})")
    return dest, cost_usd


# ──────────────────────────────────────────────────────────────────────────
# 1. Character — Jade (phase 1 full-mask wardrobe)
# ──────────────────────────────────────────────────────────────────────────
@pytest.mark.skipif(not LIVE, reason="set RECOIL_RUN_LIVE_TESTS=1 to enable")
def test_character_jade_t2i():
    bible = _bible()
    jade = bible["characters"]["JADE"]
    phase1 = next(p for p in jade["phases"] if p["phase_id"] == "jade_phase_1_full_mask")

    prompt = (
        "Full-body character reference, 9:16 vertical. "
        f"Subject: {jade['display_name']}, {jade['gender']}. "
        f"{jade['visual_description']} "
        f"Wardrobe: {phase1['wardrobe_description']} "
        "Neutral pose, arms relaxed, facing camera, even soft frontal lighting, "
        "plain dark gray studio backdrop, full figure visible head-to-toe. "
        "Photorealistic, cinematic film stock grain, no text, no logos."
    )

    dispatch, ctx = _make_ctx("jade")
    receipt = dispatch("image_t2i", {
        "shot_id": "GPTIMG2_CHAR_JADE",
        "model": "gpt-image-2",
        "prompt": prompt,
        "aspect_ratio": "9:16",
        "quality": _QUALITY,
    }, context=ctx)
    _assert_and_save(receipt, "character_jade.png")


# ──────────────────────────────────────────────────────────────────────────
# 2. Character — Wren (armored figure; tests material rendering)
# ──────────────────────────────────────────────────────────────────────────
@pytest.mark.skipif(not LIVE, reason="set RECOIL_RUN_LIVE_TESTS=1 to enable")
def test_character_wren_t2i():
    bible = _bible()
    wren = bible["characters"]["WREN"]
    phase1 = wren["phases"][0]

    prompt = (
        "Full-body character reference, 9:16 vertical. "
        f"Subject: {wren['display_name']}, {wren['gender']}. "
        f"{wren['visual_description']} "
        f"Wardrobe: {phase1['wardrobe_description']} "
        "Standing pose, frontal, even neutral lighting, plain dark gray studio "
        "backdrop, full figure visible head-to-toe. Photorealistic, gritty "
        "weathered surfaces, fine metal and rust detail, no text, no logos."
    )

    dispatch, ctx = _make_ctx("wren")
    receipt = dispatch("image_t2i", {
        "shot_id": "GPTIMG2_CHAR_WREN",
        "model": "gpt-image-2",
        "prompt": prompt,
        "aspect_ratio": "9:16",
        "quality": _QUALITY,
    }, context=ctx)
    _assert_and_save(receipt, "character_wren.png")


# ──────────────────────────────────────────────────────────────────────────
# 3. Location — int_lower_decks_corridor
# ──────────────────────────────────────────────────────────────────────────
@pytest.mark.skipif(not LIVE, reason="set RECOIL_RUN_LIVE_TESTS=1 to enable")
def test_location_corridor_t2i():
    bible = _bible()
    loc = bible["locations"]["int_lower_decks_corridor"]
    palette = ", ".join(loc.get("color_palette") or [])
    lp = loc.get("lighting_profile") or {}

    prompt = (
        f"Environment reference plate, 9:16 vertical, no characters present. "
        f"Location: derelict spaceship corridor, {loc['habitat_zone']}. "
        f"Visual elements: {loc['description']}. "
        f"Atmosphere: {loc.get('atmosphere', '')[:200]} "
        f"Lighting: {lp.get('primary_source','')}, {lp.get('direction','')}, "
        f"{lp.get('quality','')} quality, {lp.get('color_temp','')} color temp. "
        f"Color palette: {palette}. "
        "Wide deep-perspective shot down the corridor. Photorealistic, "
        "cinematic film stock grain, no text, no logos."
    )

    dispatch, ctx = _make_ctx("corridor")
    receipt = dispatch("image_t2i", {
        "shot_id": "GPTIMG2_LOC_CORRIDOR",
        "model": "gpt-image-2",
        "prompt": prompt,
        "aspect_ratio": "9:16",
        "quality": _QUALITY,
    }, context=ctx)
    _assert_and_save(receipt, "location_corridor.png")


# ──────────────────────────────────────────────────────────────────────────
# 4. Location — int_lower_decks_maintenance_shaft
# ──────────────────────────────────────────────────────────────────────────
@pytest.mark.skipif(not LIVE, reason="set RECOIL_RUN_LIVE_TESTS=1 to enable")
def test_location_shaft_t2i():
    bible = _bible()
    loc = bible["locations"]["int_lower_decks_maintenance_shaft"]
    palette = ", ".join(loc.get("color_palette") or [])
    lp = loc.get("lighting_profile") or {}

    prompt = (
        f"Environment reference plate, 9:16 vertical, no characters present. "
        f"Location: derelict spaceship vertical maintenance shaft, {loc['habitat_zone']}. "
        f"Visual elements: {loc['description']}. "
        f"Atmosphere: {loc.get('atmosphere', '')[:200]} "
        f"Lighting: {lp.get('primary_source','')}, {lp.get('direction','')}, "
        f"{lp.get('quality','')} quality, {lp.get('color_temp','')} color temp. "
        f"Color palette: {palette}. "
        "Looking down into the shaft, sense of depth and vertigo. "
        "Photorealistic, cinematic film stock grain, no text, no logos."
    )

    dispatch, ctx = _make_ctx("shaft")
    receipt = dispatch("image_t2i", {
        "shot_id": "GPTIMG2_LOC_SHAFT",
        "model": "gpt-image-2",
        "prompt": prompt,
        "aspect_ratio": "9:16",
        "quality": _QUALITY,
    }, context=ctx)
    _assert_and_save(receipt, "location_shaft.png")


# ──────────────────────────────────────────────────────────────────────────
# 5. Previs via build_previs_prompt — exact plan-pass code path
# ──────────────────────────────────────────────────────────────────────────
@pytest.mark.skipif(not LIVE, reason="set RECOIL_RUN_LIVE_TESTS=1 to enable")
def test_previs_ep001_sh01_via_build_previs_prompt():
    """Real plan-pass smoke. Loads EP001_SH01 from the actual plan, runs
    it through build_previs_prompt (the canonical builder dispatched by
    the orchestrator's previz step), fires through gpt-image-2.
    """
    from recoil.pipeline._lib.prompt_engine import build_previs_prompt
    bible = _bible()
    plan = json.loads(_PLAN_PATH.read_text())
    shots = plan.get("shots") or [
        s for seq in (plan.get("sequences") or {}).values() for s in seq
    ]
    sh01 = next(s for s in shots if s["shot_id"] == "EP001_SH01")

    config = {"film_stock": "Kodak Vision3 500T"}
    prompt = build_previs_prompt(sh01, bible, config)
    assert prompt and len(prompt) > 20, "build_previs_prompt produced empty/trivial output"
    print(f"\n[plan-pass previs prompt ({len(prompt.split())} words)]:\n  {prompt[:240]}...\n")

    dispatch, ctx = _make_ctx("previs_sh01")
    receipt = dispatch("image_t2i", {
        "shot_id": "GPTIMG2_PREVIS_EP001_SH01",
        "model": "gpt-image-2",
        "prompt": prompt,
        "aspect_ratio": sh01.get("aspect_ratio") or "9:16",
        "quality": _QUALITY,
    }, context=ctx)
    _assert_and_save(receipt, "previs_ep001_sh01.png")


# ──────────────────────────────────────────────────────────────────────────
# 6. Image-to-image edit — gpt-image-2's edit endpoint on existing previz
# ──────────────────────────────────────────────────────────────────────────
@pytest.mark.skipif(not LIVE, reason="set RECOIL_RUN_LIVE_TESTS=1 to enable")
def test_image_to_image_edit_existing_previs():
    """Smoke for fal-ai/gpt-image-2/image-to-image. Takes the existing
    SH01 hero previs and asks gpt-image-2 to push the lighting darker /
    add visible rust + steam. Verifies the edit endpoint is wired (not
    just t2i) and that single-ref edit produces a usable result.

    Per model_profiles: max_reference_images=1, multi_ref_supported=false.
    Pass exactly one reference_image.
    """
    src = _REPO_ROOT / "projects/tartarus/output/previs/ep_001/shot_001_take018.png"
    if not src.exists():
        pytest.skip(f"expected previs frame not present at {src}")

    prompt = (
        "Edit this frame: push lighting moodier and darker, add visible "
        "steam wisps from the cracked pipes, intensify the rust streaks "
        "on the metal grating, preserve composition and subject placement."
    )

    dispatch, ctx = _make_ctx("edit_sh01")
    receipt = dispatch("image_t2i", {
        "shot_id": "GPTIMG2_EDIT_EP001_SH01",
        "model": "gpt-image-2",
        "prompt": prompt,
        "aspect_ratio": "9:16",
        "quality": _QUALITY,
        # Trigger the i2i endpoint per _infer_gpt_image_2_action in fal.py:77
        "reference_images": [str(src)],
    }, context=ctx)
    _assert_and_save(receipt, "edit_ep001_sh01.png")
