"""Phase 9 — Bug R tests for ref_resolver.

Bug R: `TURNAROUND_ANGLES` did not include `closeup`, so when a shot called
for a closeup-angle character ref, `resolve_entity_refs` skipped it entirely.
This phase appends `"closeup"` to the tuple. The companion manual step —
SeedVR2-upscaling JADE's `back.png` and `jade_three_quarter.png` from 512²
to 2048² — is out-of-harness (JT does it). These tests must therefore not
depend on actual PNG resolution; they exercise the resolver logic against
either real disk fixtures or `tmp_path`-built scratch dirs.
"""

from __future__ import annotations

from pathlib import Path

import pytest

from recoil.core.ref_resolver import (
    TURNAROUND_ANGLES,
    resolve_character_refs,
    resolve_entity_refs,
)


# ── Bug R code part — closeup in the angle tuple ─────────────────────


def test_closeup_in_turnaround_angles():
    """closeup must be a turnaround angle for resolve_entity_refs to pick it
    up from `_canonical/characters/<slug>/closeup.<ext>`."""
    assert "closeup" in TURNAROUND_ANGLES, (
        f"TURNAROUND_ANGLES missing 'closeup': {TURNAROUND_ANGLES}"
    )
    # The four legacy angles must still be present — additive change only.
    for legacy in ("front", "profile", "three_quarter", "back"):
        assert legacy in TURNAROUND_ANGLES, (
            f"TURNAROUND_ANGLES dropped legacy angle {legacy!r}: {TURNAROUND_ANGLES}"
        )


# ── Bug R end-to-end — closeup PNG actually resolves ─────────────────


def test_jade_closeup_ref_resolves(tmp_path: Path):
    """Requesting refs for a character with a closeup ref on disk must
    yield the closeup entry. Uses tmp_path so the test is independent of
    the JT-managed manual upscale step (out-of-harness)."""
    from recoil.core.paths import ProjectPaths

    # Build a minimal v2 assets/char/jade fixture.
    root = tmp_path / "proj"
    subject = root / "assets" / "char" / "jade"
    subject.mkdir(parents=True)
    # Hero (required) + closeup (the bug-R target).
    (subject / "jade_turn_hero_v01.png").write_bytes(b"")
    (subject / "jade_turn_closeup_v01.png").write_bytes(b"")

    refs = resolve_entity_refs(ProjectPaths.from_root(root), "characters", "JADE")
    assert "closeup" in refs, (
        f"closeup not resolved for jade character refs: {sorted(refs)}"
    )
    assert refs["closeup"].name == "jade_turn_closeup_v01.png"
    # Hero still wired up.
    assert "hero" in refs


def test_jade_closeup_ref_resolves_via_wrapper(tmp_path: Path):
    """Same path through the public `resolve_character_refs` wrapper that
    workspace / pipeline call from."""
    from recoil.core.paths import ProjectPaths

    root = tmp_path / "proj"
    subject = root / "assets" / "char" / "jade"
    subject.mkdir(parents=True)
    (subject / "jade_turn_hero_v01.png").write_bytes(b"")
    (subject / "jade_turn_closeup_v01.png").write_bytes(b"")

    refs = resolve_character_refs(ProjectPaths.from_root(root), "JADE")
    assert "closeup" in refs, (
        f"resolve_character_refs lost closeup: {sorted(refs)}"
    )


def test_closeup_missing_when_file_absent(tmp_path: Path):
    """Negative path: if no closeup ref exists, the key must not appear —
    silently skipping is the documented behavior (filesystem is canonical)."""
    from recoil.core.paths import ProjectPaths

    root = tmp_path / "proj"
    subject = root / "assets" / "char" / "jade"
    subject.mkdir(parents=True)
    (subject / "jade_turn_hero_v01.png").write_bytes(b"")
    (subject / "jade_turn_front_v01.png").write_bytes(b"")

    refs = resolve_entity_refs(ProjectPaths.from_root(root), "characters", "JADE")
    assert "closeup" not in refs, (
        f"closeup phantom-resolved with no file on disk: {sorted(refs)}"
    )
    # Front still present.
    assert "front" in refs


def test_jade_canonical_closeup_on_disk_if_present():
    """If the JT-managed canonical jade closeup exists on disk, the resolver
    must pick it up. Skip cleanly when JT hasn't completed the manual step
    (out-of-harness SeedVR2 upscale)."""
    try:
        from recoil.core.paths import projects_root
        projects_root_path = projects_root()
    except Exception as e:
        pytest.skip(f"projects_root() unavailable in this environment: {e}")

    # The actual production location of the canonical jade refs.
    refs_root = projects_root_path / "tartarus" / "output" / "refs"
    canonical = refs_root / "_canonical" / "characters" / "jade"
    if not canonical.exists():
        pytest.skip(
            f"tartarus jade canonical refs not present on this machine "
            f"({canonical}) — skip clean."
        )
    closeup_candidates = [canonical / f"closeup{ext}" for ext in (".png", ".jpg", ".jpeg", ".webp")]
    if not any(p.is_file() for p in closeup_candidates):
        pytest.skip(
            "jade/closeup.* not yet on disk — manual SeedVR2 upscale step "
            "is out-of-harness, JT runs it."
        )
    refs = resolve_entity_refs(refs_root, "characters", "JADE")
    assert "closeup" in refs


def test_underscore_location_id_resolves_hyphenated_filenames(tmp_path: Path):
    """Multi-word asset ids keep underscores in the DIRECTORY slug, but the
    filename grammar uses '_' as its field delimiter, so conformant filenames
    carry the subject HYPHENATED. The resolver must compare in hyphen space —
    regression for the 2026-06-11 corridor scene_1 manifest outage."""
    from recoil.core.paths import ProjectPaths

    root = tmp_path / "proj"
    loc_dir = root / "assets" / "loc" / "int_lower_decks_corridor"
    loc_dir.mkdir(parents=True)
    (loc_dir / "int-lower-decks-corridor_loc_hero_v01.png").write_bytes(b"x")
    # Underscored-subject filename is NON-conformant; must be ignored, not crash.
    (loc_dir / "int_lower_decks_corridor_loc_wide_v01.png").write_bytes(b"x")

    paths = ProjectPaths.from_root(root)
    refs = resolve_entity_refs(paths, "loc", "int_lower_decks_corridor")
    assert "hero" in refs, f"hyphenated conformant ref not resolved: {sorted(refs)}"
    assert refs["hero"].name == "int-lower-decks-corridor_loc_hero_v01.png"
    assert "wide" not in refs
