"""Locks the word-search placement qualities players actually feel:

1. Every word gets placed (exhaustive candidate search — nothing silently dropped).
2. Grids INTERLOCK like a real puzzle (the "clean isolated words" regression).
3. Words SPREAD across the board (the "all clumped in one corner" regression).
4. Same date/seed → same grid (cross-device players must see identical puzzles).

Thresholds were calibrated against all curated themes × 12 seeds × 3 tiers
(288 grids/tier): crossing fraction averaged ~0.7 (old algorithm: ~0.3, with a
third of small grids having ZERO crossings), worst quadrant share 0.42, and all
four quadrants always held word cells. Deterministic, so no flake margin needed.
"""

import random
import statistics

from goodnews.games import _WS_FALLBACKS, WS_TIERS, _WS_ORDER, _build_grid, _place_words, _zone


def _tier_grids(tier):
    """Yield (placements, size) for every curated theme × 12 seeds in a tier."""
    t = WS_TIERS[tier]
    for _, words in _WS_FALLBACKS:
        for seed in range(12):
            rng = random.Random(seed * 1000 + 7)
            ws = list(words)
            rng.shuffle(ws)
            _, placements = _place_words(ws[: t["count"]], t["grid"], seed)
            yield placements, t["grid"]


def _cross_fraction(placements):
    """Fraction of placed words sharing at least one cell with another word."""
    owners: dict[tuple[int, int], list[str]] = {}
    for w, cells in placements:
        for cell in cells:
            owners.setdefault(cell, []).append(w)
    crossing = set()
    for ws in owners.values():
        if len(ws) > 1:
            crossing.update(ws)
    return len(crossing) / len(placements)


def test_all_words_placed():
    for tier in _WS_ORDER:
        for placements, _ in _tier_grids(tier):
            assert len(placements) == WS_TIERS[tier]["count"]


def test_grids_interlock_without_clumping():
    for tier in _WS_ORDER:
        fracs = []
        for placements, size in _tier_grids(tier):
            fracs.append(_cross_fraction(placements))
            # Spread: word cells must reach all four quadrants, and no quadrant
            # may hoard more than half of them (perfectly even would be 0.25).
            quad: dict[tuple[int, int], int] = {}
            cells = {c for _, cs in placements for c in cs}
            for r, c in cells:
                quad[_zone(r, c, size)] = quad.get(_zone(r, c, size), 0) + 1
            assert len(quad) == 4, f"{tier}: words confined to {len(quad)} quadrant(s)"
            assert max(quad.values()) / len(cells) <= 0.5, f"{tier}: clumped in one quadrant"
        # Interlock: every grid has some crossings; on average most words connect.
        assert min(fracs) >= 0.3, f"{tier}: a grid came out as disconnected clean words"
        assert 0.55 <= statistics.mean(fracs) <= 0.9, f"{tier}: avg crossing {statistics.mean(fracs):.2f}"


def test_grid_deterministic_and_honest():
    """Same inputs → byte-identical grid, and every reported word is really in it
    (forward or reversed along some line — spot-checked via placements)."""
    words = _WS_FALLBACKS[0][1][:9]
    rows1, placed1 = _build_grid(words, 11, 42)
    rows2, placed2 = _build_grid(words, 11, 42)
    assert rows1 == rows2 and placed1 == placed2
    _, placements = _place_words(words, 11, 42)
    for word, cells in placements:
        assert "".join(rows1[r][c] for r, c in cells) == word
