"""Unit tests for sublocation_registry — loader shape, ref resolution, the
REC-125 ref-file guard, and the undecomposed-location None contract.

Self-contained: builds a fake location tree under tmp_path (a real >1KB PNG via
PIL random-noise + a 99-byte text stub), so it depends on neither the committed
project data nor the Dropbox data root.
"""

from __future__ import annotations

import json
import os

import pytest
from PIL import Image

from recoil.core.paths import ProjectPaths
from recoil.pipeline._lib.sublocation_registry import (
    load_location_registry,
    location_base_dir,
    sublocation_ref,
    validate_ref_file,
)

LOCATION_ID = "int_lower_decks_maintenance_shaft"


def _write_noise_png(path, size=(64, 64)):
    """Write a 64x64 RGB random-noise PNG — incompressible, so well over 1KB."""
    path.parent.mkdir(parents=True, exist_ok=True)
    w, h = size
    img = Image.frombytes("RGB", (w, h), os.urandom(w * h * 3))
    img.save(path, "PNG")


@pytest.fixture
def decomposed_location(tmp_path):
    """A decomposed location tree: base/location.json + a real PNG ref + a stub."""
    project_root = tmp_path / "tartarus"
    paths = ProjectPaths(project_root=project_root)
    base = location_base_dir(paths, LOCATION_ID)
    sublocs = base / "sublocations"

    real_png = sublocs / "sublocation_pod_platform_v01.png"
    _write_noise_png(real_png)

    stub = sublocs / "sublocation_stub_v01.png"  # 99-byte text masquerading as a png
    stub.parent.mkdir(parents=True, exist_ok=True)
    stub.write_bytes(b"x" * 99)

    registry = {
        "schema_version": 1,
        "location_id": LOCATION_ID,
        "sublocations": {
            "pod_platform": {
                "ref": "sublocations/sublocation_pod_platform_v01.png",
                "source_sha256": "deadbeef",
            }
        },
        "adjacency": [["shaft_lip", "pod_platform"]],
    }
    (base / "location.json").write_text(json.dumps(registry))

    return paths, base, real_png, stub


def test_load_location_registry_shape(decomposed_location):
    paths, _base, _real, _stub = decomposed_location
    reg = load_location_registry(paths, LOCATION_ID)
    assert reg is not None
    assert reg["schema_version"] == 1
    assert reg["location_id"] == LOCATION_ID
    assert "pod_platform" in reg["sublocations"]
    assert reg["sublocations"]["pod_platform"]["ref"].endswith(
        "sublocation_pod_platform_v01.png"
    )
    assert reg["adjacency"] == [["shaft_lip", "pod_platform"]]


def test_sublocation_ref_resolution(decomposed_location):
    paths, base, real_png, _stub = decomposed_location
    resolved = sublocation_ref(paths, LOCATION_ID, "pod_platform")
    assert resolved == base / "sublocations" / "sublocation_pod_platform_v01.png"
    assert resolved == real_png


def test_sublocation_ref_missing_entry_returns_none(decomposed_location):
    paths, _base, _real, _stub = decomposed_location
    assert sublocation_ref(paths, LOCATION_ID, "not_a_sublocation") is None


def test_validate_ref_file_passes_real_png(decomposed_location):
    _paths, _base, real_png, _stub = decomposed_location
    assert validate_ref_file(real_png) is None


def test_validate_ref_file_flags_stub(decomposed_location):
    _paths, _base, _real, stub = decomposed_location
    err = validate_ref_file(stub)
    assert err is not None
    assert str(stub) in err


def test_validate_ref_file_flags_missing(tmp_path):
    assert validate_ref_file(tmp_path / "nope.png") is not None


def test_validate_ref_file_flags_non_image_over_1kb(tmp_path):
    """A >1KB file that is NOT a decodable image fails the REC-125 PIL guard."""
    fake = tmp_path / "fake.png"
    fake.write_bytes(b"not an image " * 200)  # ~2.6KB of text
    err = validate_ref_file(fake)
    assert err is not None
    assert "decodable image" in err


def test_undecomposed_location_returns_none(tmp_path):
    """A location with no base/location.json is undecomposed → loader None."""
    paths = ProjectPaths(project_root=tmp_path / "tartarus")
    assert load_location_registry(paths, "int_some_undecomposed_room") is None
    assert sublocation_ref(paths, "int_some_undecomposed_room", "anything") is None
