# recoil/pipeline/tests/test_extension_masking.py
"""1000 random sequences of set_hero across .png/.jpg/.jpeg/.webp.

Asserts that ref_resolver always returns the correct current hero
regardless of extension collisions. This is a structural defense against
the bug class behind the April 6 9-round debug convergence.
"""

import random

import pytest

from recoil.pipeline._lib import asset_ops
from recoil.core import ref_resolver

EXTENSIONS = [".png", ".jpg", ".jpeg", ".webp"]


@pytest.fixture
def project_root(tmp_path):
    # v3 layout: asset_ops writes under assets/{class}/{subject}/ and the ops
    # log under _pipeline/state/visual/.
    (tmp_path / "assets" / "char").mkdir(parents=True)
    (tmp_path / "_pipeline" / "state" / "visual").mkdir(parents=True)
    return tmp_path


def _make_source(tmp_path, name):
    src = tmp_path / name
    src.write_bytes(b"\x89PNG\r\n\x1a\n" + b"0" * 256)
    return src


@pytest.mark.xfail(
    strict=True,
    reason="REC-154: asset_ops.set_hero writes non-conformant 'hero.{ext}' which "
    "ref_resolver (conformant-filename-only) cannot read; the round-trip resolves "
    "to {} until the asset_ops<->ref_resolver naming contract is reconciled. "
    "This regression guard is correct; the prod bug is real. Un-xfail when REC-154 lands.",
)
@pytest.mark.parametrize("seed", range(20))  # 20 seeds x 50 ops = 1000 ops total
def test_extension_masking_random_sequences(project_root, tmp_path, seed):
    from recoil.core.paths import ProjectPaths

    rng = random.Random(seed)
    expected_ext = None
    paths = ProjectPaths.from_root(project_root)

    for step in range(50):
        ext = rng.choice(EXTENSIONS)
        src = _make_source(tmp_path, f"step_{step}{ext}")
        asset_ops.set_hero(
            project_root=project_root,
            asset_type="character",
            asset_id="sadie",
            source=src,
        )
        expected_ext = ext

        refs = ref_resolver.resolve_character_refs(paths, "sadie")
        hero = refs.get("hero") if isinstance(refs, dict) else None
        assert hero is not None, f"hero missing after step {step}"
        assert hero.suffix == expected_ext, (
            f"step {step}: expected hero ext {expected_ext}, got {hero.suffix}. "
            f"Extension masking bug class triggered."
        )
