"""
GLAMWalk -- Volume 3: Data at Scale
06_recursive_image_finder_and_ranking.py

Recursively walking any JSON structure to find every image URL regardless of where it's nested, then ranking multiple candidates by filename-suffix quality (Flickr's own big/zoom/normal/square convention) instead of taking the first hit.

Source: Volume Three, Sections 3.3-3.4
Targets Python 3.11.4 -- see Volume One, Chapter 1.5/1.6.
"""

def looks_like_image(value):
    if not isinstance(value, str):
        return False
    return value.startswith("http") and value.lower().endswith((".jpg", ".jpeg", ".png", ".webp"))

def find_all_images(obj, found):
    """
    Walk ANY JSON structure — dict, list, or plain value, nested to
    any depth — and collect every string that looks like an image URL.
    Doesn't care what the surrounding structure is called or how deep
    it's buried; it only cares what the VALUES look like.
    """
    if isinstance(obj, dict):
        for value in obj.values():
            find_all_images(value, found)
    elif isinstance(obj, list):
        for item in obj:
            find_all_images(item, found)
    elif looks_like_image(obj):
        found.add(obj)

# A small stand-in record shaped like a real Cooper Hewitt object,
# with images buried at two different nesting depths on purpose —
# exactly the situation find_all_images() is built to handle.
cooper_hewitt_record = {
    "title": "Untitled",
    "images": [{"url": "https://images.example.org/12345_b.jpg"}],
    "media": {"primary": {"file": "https://images.example.org/12345_z.jpg"}},
}

found = set()
find_all_images(cooper_hewitt_record, found)
print(found)


QUALITY_RANK = {
    "_b.jpg": 5, "_b.png": 5,     # big — best available
    "_z.jpg": 4, "_z.png": 4,     # zoom — large
    "_n.jpg": 3, "_n.png": 3,     # normal — mid-size
    "_sq.jpg": 2, "_sq.png": 2,   # square — cropped thumbnail
    "_x.jpg": 1, "_x.png": 1,     # extra small
}

def image_quality_score(url):
    for suffix, score in QUALITY_RANK.items():
        if suffix in url:
            return score
    return 0   # unrecognized pattern — treat as lowest priority, not an error

best_first = sorted(found, key=image_quality_score, reverse=True)
primary_image = best_first[0] if best_first else ""
