"""Admin curation of the Daily Word answer pool: add, remove (tombstone any
word — curated or added), restore, and bulk import with dedupe + validation."""
import pytest

from goodnews import games
from goodnews.db import connect, init_db


@pytest.fixture
def conn(tmp_path):
    c = connect(str(tmp_path / "t.db"))
    init_db(c)
    return c


def _a_curated(variant):
    return games._POOL[variant][0]  # a known baked-in word


def _fresh(variant, n=1):
    """n dictionary words of the given length that are NOT already in the pool."""
    pool = set(games._POOL[variant])
    out = [w for w in sorted(games._DICT[variant]) if w not in pool and w.isalpha()]
    return out[:n]


def test_add_and_remove_added_word(conn):
    w = _fresh("5")[0]
    res = games.add_pool_word(conn, w)
    assert res.get("added") and res["variant"] == "5"
    assert w in games.answer_pool(conn, "5")
    # Removing an added word tombstones it → drops out of the live pool.
    games.remove_pool_word(conn, w)
    assert w not in games.answer_pool(conn, "5")
    # ...and is reversible.
    games.restore_pool_word(conn, w)
    assert w in games.answer_pool(conn, "5")


def test_remove_curated_word_via_tombstone(conn):
    w = _a_curated("5")
    assert w in games.answer_pool(conn, "5")
    games.remove_pool_word(conn, w)  # curated word — only a tombstone can pull it
    assert w not in games.answer_pool(conn, "5")
    summary = games.pool_summary(conn)
    assert w in summary["5"]["removed"]
    games.restore_pool_word(conn, w)
    assert w in games.answer_pool(conn, "5")


def test_add_rejects_non_dictionary_and_bad_length(conn):
    assert "error" in games.add_pool_word(conn, "zzzzz")   # not in dict
    assert "error" in games.add_pool_word(conn, "hi")      # wrong length
    assert "error" in games.add_pool_word(conn, _a_curated("5"))  # already in pool


def test_add_relifts_tombstone(conn):
    w = _a_curated("6")
    games.remove_pool_word(conn, w)
    assert w not in games.answer_pool(conn, "6")
    # Re-adding a removed word lifts the tombstone rather than erroring.
    res = games.add_pool_word(conn, w)
    assert res.get("added")
    assert w in games.answer_pool(conn, "6")


def test_import_dedupes_and_validates(conn):
    new5 = _fresh("5", 2)        # two fresh 5-letter words
    added_word = new5[0]
    games.add_pool_word(conn, added_word)
    existing_curated = _a_curated("5")
    res = games.import_pool_words(conn, [
        new5[1],             # valid new
        new5[1].upper(),     # same word, different case → counted once
        added_word,          # dup (already added)
        existing_curated,    # dup (curated)
        "zzzzz",             # rejected: not in dictionary
        "hi",                # rejected: wrong length
        "  ",                # blank → ignored
    ])
    assert set(res["added"]) == {new5[1]}
    assert res["counts"]["added"] == 1
    assert res["counts"]["duplicates"] >= 2
    assert {r["word"] for r in res["rejected"]} == {"zzzzz", "hi"}
    assert new5[1] in games.answer_pool(conn, "5")


def test_picker_survives_empty_live_pool(conn):
    """An overzealous admin could tombstone every answer in a variant. The live
    pool then reads empty, but the daily picker must not divide-by-zero — it falls
    back to the curated baseline rather than crashing."""
    for w in list(games._POOL["5"]):
        games.remove_pool_word(conn, w)
    assert games.answer_pool(conn, "5") == []          # live pool truly empty
    ans = games._pick_answer(conn, "2026-06-11", "5")  # must not raise
    assert ans in set(games._POOL["5"])                # fell back to curated


def test_import_relifts_removed_word(conn):
    w = _a_curated("5")
    games.remove_pool_word(conn, w)
    res = games.import_pool_words(conn, [w])
    assert w in res["added"]
    assert w in games.answer_pool(conn, "5")
