"""Stub BGG XML fixtures for the 27 titles extracted from the real shelf
photos, aligned with the objectids in the real collection snapshots.

Like write_stub_fixtures.py these are hand-written approximations (BGG's
API is locked until the registration is approved). They cover: the plain
search per title, empty results for the long Avalon Hill/SPI box titles
(what real search does with them), the truncated-head retry searches,
tie-break stats (Cosmic Encounter, Wiz-War, Sorcerer), and version lists
for every title with publisher/edition/year cues. Once BGG_API_TOKEN
exists: delete the cache dirs and re-run resolve/record_fixtures to
replace all of this with real recordings.

Usage: uv run python scripts/write_photo_fixtures.py
"""

from __future__ import annotations

from fixture_common import FIXTURE_CACHE, esc, write_cache_marker, write_data_marker

from bggpipe.bgg_client import SEARCH_TYPES, cache_key
from bggpipe.config import Config

TARGETS = (FIXTURE_CACHE, Config().cache_dir)


BG, EXP = "boardgame", "boardgameexpansion"

# (bgg_id, name, year, type, name_type)
SEARCHES: dict[str, list[tuple]] = {
    "ROYALS": [(165986, "Royals", 2014, BG, "primary")],
    "CAT CRIMES": [(235096, "Cat Crimes", 2017, BG, "primary")],
    "COSMIC ENCOUNTER": [
        (39, "Cosmic Encounter", 1977, BG, "primary"),
        (40529, "Cosmic Encounter", 2008, BG, "primary"),
    ],
    "SHERIFF OF NOTTINGHAM": [
        (157969, "Sheriff of Nottingham", 2014, BG, "primary"),
        (289411, "Sheriff of Nottingham (2nd Edition)", 2020, BG, "primary"),
    ],
    "5 MINUTE DUNGEON": [(207830, "5-Minute Dungeon", 2017, BG, "primary")],
    "Britannia": [(240, "Britannia", 1986, BG, "primary")],
    # BGG search finds nothing for the full transcribed box titles
    "CIVILIZATION Game of the Heroic Age - The Dawn of History 8000 BC to 250 BC": [],
    "CIVILIZATION Game of the Heroic Age": [],
    "CIVILIZATION": [
        (71, "Civilization", 1980, BG, "primary"),
        (170416, "Sid Meier's Civilization: The Board Game", 2002, BG, "primary"),
        (233247, "Civilization: A New Dawn", 2017, BG, "primary"),
    ],
    "ADVANCED CIVILIZATION Game Expansion of the Heroic Age - "
    "Featuring New Civilization, Commodity, and Calamity Cards": [],
    "ADVANCED CIVILIZATION Game Expansion of the Heroic Age": [],
    "ADVANCED CIVILIZATION": [(177, "Advanced Civilization", 1991, EXP, "primary")],
    "DOCTOR WHO The Game of Time & Space": [
        (3090, "Doctor Who: The Game of Time & Space", 1980, BG, "primary"),
        (125675, "Doctor Who: The Card Game", 2012, BG, "primary"),
    ],
    "SORCERER The Game of Magical Conflict": [],
    "SORCERER": [
        (3585, "Sorcerer", 1975, BG, "primary"),
        (244115, "Sorcerer", 2019, BG, "primary"),
    ],
    "STARFORCE ALPHA CENTAURI Interstellar Conflict in the 25th Century": [],
    "STARFORCE ALPHA": [
        (
            2524,
            "StarForce 'Alpha Centauri': Interstellar Conflict in the 25th Century",
            1974,
            BG,
            "primary",
        ),
    ],
    "SKIP-BO": [(1078, "Skip-Bo", 1967, BG, "primary")],
    "a Gentle Rain": [(268163, "A Gentle Rain", 2019, BG, "primary")],
    "PATCH WORK": [(163412, "Patchwork", 2014, BG, "primary")],
    "SANTORINI": [
        (194655, "Santorini", 2016, BG, "primary"),
        (255563, "Santorini: New York", 2020, BG, "primary"),
    ],
    "Utter Nonsense!": [(181254, "Utter Nonsense", 2015, BG, "primary")],
    "Cards Against Humanity": [(50381, "Cards Against Humanity", 2009, BG, "primary")],
    "Joking Hazard": [(193621, "Joking Hazard", 2016, BG, "primary")],
    "EXPLODING KITTENS": [
        (172225, "Exploding Kittens", 2015, BG, "primary"),
        (172242, "Exploding Kittens: NSFW Edition", 2015, BG, "primary"),
    ],
    "Herbaceous": [(195314, "Herbaceous", 2017, BG, "primary")],
    # vision misread of Herbaceous on one run — search finds nothing;
    # review's free-text re-search ("f Herbaceous") rescues it
    "Hebarceos": [],
    "Wiz-War": [
        (1218, "Wiz-War", 1983, BG, "primary"),
        (104710, "Wiz-War", 2012, BG, "alternate"),  # primary: Wiz-War (Eighth Edition)
    ],
    "SCRAWL": [(218866, "Scrawl", 2017, BG, "primary")],
    "FLAT TOP": [(2529, "Flat Top", 1977, BG, "primary")],
    "Diplomacy": [(483, "Diplomacy", 1959, BG, "primary")],
    "Age of Renaissance": [(26, "Age of Renaissance", 1996, BG, "primary")],
    "History of the World": [(224, "History of the World", 1991, BG, "primary")],
    "Dungeon!": [(1339, "Dungeon!", 1975, BG, "primary")],
    "DUNGEON!": [(1339, "Dungeon!", 1975, BG, "primary")],  # spelling drift
    # Alice Is Missing (IMG_4566) is an RPG — a geekdo rpgitem, so the
    # boardgame,boardgameexpansion search realistically returns nothing for
    # the full titles or any truncation head. It belongs on RPGGeek, not in
    # the BGG boardgame collection; expect these to stay unmatched.
    "ALICE IS MISSING: A SILENT ROLE PLAYING GAME": [],
    "ALICE IS MISSING: A SILENT ROLE PLAYING": [],
    "ALICE IS MISSING: SILENT FALLS EXPANSION": [],
    "ALICE IS MISSING": [],
    "ALICE IS": [],
}

# rpgitem-search results for titles that live on RPGGeek, not BGG. Ids are
# SYNTHETIC like everything else here; the marker guards them. Queries not
# listed get an empty rpgitem stub automatically.
RPG = "rpgitem"
RPG_SEARCHES: dict[str, list[tuple]] = {
    "ALICE IS MISSING: A SILENT ROLE PLAYING GAME": [
        (400001, "Alice Is Missing: A Silent Role Playing Game", 2020, RPG, "primary")
    ],
    "ALICE IS MISSING: SILENT FALLS EXPANSION": [
        (400002, "Alice Is Missing: Silent Falls Expansion", 2023, RPG, "primary")
    ],
}

# ids-param -> [(bgg_id, name, year, type, owned, rank, [publishers])]
THING_STATS: dict[str, list[tuple]] = {
    "39,40529": [
        (39, "Cosmic Encounter", 1977, BG, 7000, 800, ["Eon"]),
        (40529, "Cosmic Encounter", 2008, BG, 75000, 150, ["Fantasy Flight Games"]),
    ],
    "1218,104710": [
        (1218, "Wiz-War", 1983, BG, 4000, 2500, ["Chessex"]),
        (
            104710,
            "Wiz-War (Eighth Edition)",
            2012,
            BG,
            8000,
            1200,
            ["Fantasy Flight Games"],
        ),
    ],
    "3585,244115": [
        (
            3585,
            "Sorcerer",
            1975,
            BG,
            900,
            5000,
            ["Simulations Publications, Inc. (SPI)"],
        ),
        (244115, "Sorcerer", 2019, BG, 6000, 1500, ["White Wizard Games"]),
    ],
}

# bgg_id -> [(version_id, name, year, [publishers], [languages])]
VERSIONS: dict[int, list[tuple]] = {
    400001: [],  # Alice Is Missing (rpgitem): no cataloged versions in stubs
    400002: [],
    235096: [(360982, "English edition", 2017, ["ThinkFun"], ["English"])],
    240: [
        (
            25668,
            "Avalon Hill English edition",
            1986,
            ["The Avalon Hill Game Co"],
            ["English"],
        ),
        (90000, "Fantasy Flight edition", 2006, ["Fantasy Flight Games"], ["English"]),
        (
            90001,
            "Welt der Spiele German edition",
            1986,
            ["Welt der Spiele"],
            ["German"],
        ),
    ],
    71: [
        (
            71001,
            "Avalon Hill English edition",
            1981,
            ["The Avalon Hill Game Co"],
            ["English"],
        ),
        (
            71002,
            "Hartland Trefoil first edition",
            1980,
            ["Hartland Trefoil Ltd."],
            ["English"],
        ),
        (71003, "Gibsons Games edition", 1988, ["Gibsons Games"], ["English"]),
    ],
    177: [
        (
            177001,
            "Avalon Hill English edition",
            1991,
            ["The Avalon Hill Game Co"],
            ["English"],
        )
    ],
    3090: [
        (
            309001,
            "Games Workshop English edition",
            1980,
            ["Games Workshop Ltd."],
            ["English"],
        ),
    ],
    3585: [
        (
            358501,
            "SPI Designer's Edition",
            1975,
            ["Simulations Publications, Inc. (SPI)"],
            ["English"],
        ),
        (
            358502,
            "SPI folio edition",
            1975,
            ["Simulations Publications, Inc. (SPI)"],
            ["English"],
        ),
    ],
    2524: [
        (
            252401,
            "SPI Designer's Edition",
            1974,
            ["Simulations Publications, Inc. (SPI)"],
            ["English"],
        ),
        (
            252402,
            "SPI folio edition",
            1974,
            ["Simulations Publications, Inc. (SPI)"],
            ["English"],
        ),
    ],
    268163: [
        (268164, "Bloom Edition", 2021, ["Mondo Games"], ["English"]),
        (268165, "First edition", 2019, ["Mondo Games"], ["English"]),
    ],
    193621: [(19362101, "First edition", 2016, ["Cyanide & Happiness"], ["English"])],
    2529: [
        (252901, "Avalon Hill edition", 1981, ["The Avalon Hill Game Co"], ["English"]),
        (252902, "Battleline first edition", 1977, ["Battleline"], ["English"]),
    ],
    483: [
        (
            48301,
            "Avalon Hill 1976 edition",
            1976,
            ["The Avalon Hill Game Co"],
            ["English"],
        ),
        (
            48302,
            "Avalon Hill 1999 edition",
            1999,
            ["The Avalon Hill Game Co"],
            ["English"],
        ),
        (48303, "Gibsons Games edition", 1963, ["Gibsons Games"], ["English"]),
    ],
    26: [
        (
            2601,
            "Avalon Hill English edition",
            1996,
            ["The Avalon Hill Game Co"],
            ["English"],
        )
    ],
    224: [
        (
            22401,
            "Avalon Hill English edition",
            1993,
            ["The Avalon Hill Game Co"],
            ["English"],
        ),
        (
            22402,
            "Ragnar Brothers first edition",
            1991,
            ["Ragnar Brothers"],
            ["English"],
        ),
    ],
    1339: [
        (133901, "TSR first edition", 1975, ["TSR"], ["English"]),
        (133902, "TSR second edition", 1980, ["TSR"], ["English"]),
        (133903, "TSR New Dungeon edition", 1989, ["TSR"], ["English"]),
    ],
}


def search_xml(results: list[tuple]) -> str:
    items = "".join(
        f'<item type="{type_}" id="{bgg_id}">'
        f'<name type="{name_type}" value="{esc(name)}"/>'
        f'<yearpublished value="{year}"/></item>'
        for bgg_id, name, year, type_, name_type in results
    )
    return f'<items total="{len(results)}">{items}</items>'


def stats_xml(things: list[tuple]) -> str:
    items = ""
    for bgg_id, name, year, type_, owned, rank, publishers in things:
        links = "".join(
            f'<link type="boardgamepublisher" id="1" value="{esc(p)}"/>'
            for p in publishers
        )
        items += (
            f'<item type="{type_}" id="{bgg_id}">'
            f'<name type="primary" value="{esc(name)}"/>'
            f'<yearpublished value="{year}"/>{links}'
            f"<statistics><ratings>"
            f'<owned value="{owned}"/>'
            f'<ranks><rank type="subtype" id="1" name="boardgame" '
            f'value="{rank}"/></ranks>'
            f"</ratings></statistics></item>"
        )
    return f"<items>{items}</items>"


def versions_xml(bgg_id: int, versions: list[tuple]) -> str:
    version_items = ""
    for vid, name, year, publishers, languages in versions:
        links = "".join(
            f'<link type="boardgamepublisher" id="1" value="{esc(p)}"/>'
            for p in publishers
        ) + "".join(
            f'<link type="language" id="1" value="{esc(lang)}"/>' for lang in languages
        )
        version_items += (
            f'<item type="boardgameversion" id="{vid}">'
            f'<name type="primary" value="{esc(name)}"/>'
            f'<yearpublished value="{year}"/>{links}</item>'
        )
    return (
        f'<items><item type="boardgame" id="{bgg_id}">'
        f'<name type="primary" value="game"/>'
        f"<versions>{version_items}</versions></item></items>"
    )


def main() -> None:
    files: dict[str, str] = {}
    for query, results in SEARCHES.items():
        key = cache_key("search", {"query": query, "type": SEARCH_TYPES})
        files[key] = search_xml(results)
        rpg_key = cache_key("search", {"query": query, "type": "rpgitem"})
        files[rpg_key] = search_xml(RPG_SEARCHES.get(query, []))
    for ids, things in THING_STATS.items():
        files[cache_key("thing", {"id": ids, "stats": "1"})] = stats_xml(things)
    for bgg_id, versions in VERSIONS.items():
        key = cache_key("thing", {"id": str(bgg_id), "versions": "1"})
        files[key] = versions_xml(bgg_id, versions)

    for target in TARGETS:
        target.mkdir(parents=True, exist_ok=True)
        for name, xml in files.items():
            (target / name).write_text(xml)
        # provenance marker: anything resolved from this cache is stub-derived
        # and NOT upload-ready; re-recording real fixtures removes the marker
        write_cache_marker(target)
    write_data_marker()
    print(f"Wrote {len(files)} fixture file(s) to {' and '.join(map(str, TARGETS))}")
    print("Wrote data/STUB_DATA.marker (committed; upload refuses while it exists)")


if __name__ == "__main__":
    main()
