"""Does a tent whose `state_json` is not JSON kill a real Hesper turn?

Run from the root of a checkout; it uses whichever tree it is run from, so the
same file answers the question on `main` and on PR #4:

    cd /home/vesper/untilnextsession && python research/hesper/pr4-review/broken_tent_turn.py
    cd /home/vesper/pr4wt          && python research/hesper/pr4-review/broken_tent_turn.py

PR #4's own upkeep tests call `upkeep.run` directly. This one goes through
`turn.resolve` — the runner the world actually uses — because the defect being
fixed is *the boundary dies*, and only the runner can show that.

Prints, in order: whether the turn resolved, the world's `current_turn`
afterwards, the tent's stored bytes (they must not be repaired), the owner's
`ap_budget` for the next turn, and the notices their report would carry.
"""
import json
import sqlite3
import sys
import tempfile
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[3]))

from hesper import db, sheets, reports, turn as turn_mod
from hesper.core import loader

TURN = 1


def world(broken):
    tmp = tempfile.TemporaryDirectory()
    conn = db.connect(Path(tmp.name) / "h.db")
    db.apply_migrations(conn)
    conn.row_factory = sqlite3.Row
    for x in range(-1, 2):
        for y in range(-1, 2):
            conn.execute("INSERT INTO tiles (x, y, terrain, elevation) "
                         "VALUES (?, ?, 'plain', 3)", (x, y))
    sheets.insert_kinds(conn, sheets.load_all(), turn=0, law_version=1)
    conn.execute("INSERT INTO players (player_id, name, kind, created_at, landed_turn, "
                 "coins, ap_budget, status, home_x, home_y) VALUES "
                 "('p1', 'Probe', 'agent', '2026-09-07', 0, 0, 10, 'active', 0, 0)")
    sheet = lambda slug: conn.execute(
        "SELECT sheet_id FROM kinds WHERE slug = ?", (slug,)).fetchone()["sheet_id"]
    conn.execute("INSERT INTO things (thing_id, sheet_id, owner_player, x, y, fixed, "
                 "props_json, state_json, durability, move_points, created_turn) "
                 "VALUES ('av1', ?, 'p1', 0, 0, 0, '{}', '{}', 10, 0, 0)", (sheet("settler"),))
    conn.execute("UPDATE players SET avatar_thing_id = 'av1' WHERE player_id = 'p1'")
    state = '{' if broken else json.dumps({"pitched": True})
    conn.execute("INSERT INTO things (thing_id, sheet_id, owner_player, x, y, fixed, "
                 "props_json, state_json, durability, move_points, created_turn) "
                 "VALUES ('tent1', ?, 'p1', 0, 0, 1, '{}', ?, 10, 0, 0)",
                 (sheet("tent"), state))
    # The only food in the world is inside the unreadable tent.
    conn.execute("INSERT INTO inventories (holder_kind, holder_id, item, qty) "
                 "VALUES ('thing', 'tent1', 'food', 2)")
    return tmp, conn


def run(broken):
    tmp, conn = world(broken)
    try:
        loaded = loader.load(law_version=1, turn=TURN, conn=conn)
        db.apply_module_migrations(conn, loaded)
        before = conn.execute("SELECT state_json FROM things WHERE thing_id='tent1'").fetchone()[0]
        try:
            turn_mod.resolve(conn, None, loaded, now_ms=1757000000000,
                             sealer=lambda *a: {}, git_sealer=lambda *a, **kw: {})
            outcome = "resolved"
        except BaseException as exc:
            outcome = f"{type(exc).__name__}: {exc}"
        current = db.meta_get(conn, "current_turn")
        after = conn.execute("SELECT state_json FROM things WHERE thing_id='tent1'").fetchone()[0]
        budget = conn.execute("SELECT ap_budget FROM players WHERE player_id='p1'").fetchone()[0]
        food = conn.execute("SELECT qty FROM inventories WHERE holder_id='tent1'").fetchone()
        notices = []
        if outcome == "resolved":
            try:
                player = conn.execute("SELECT * FROM players WHERE player_id='p1'").fetchone()
                notices = [n["type"] for n in reports._notices(conn, TURN, player, None, {})]
            except BaseException as exc:
                notices = [f"report raised {type(exc).__name__}: {exc}"]
        print(f"  turn            : {outcome}")
        print(f"  current_turn    : {current}")
        # A readable tent is *expected* to change here: decay writes `stopped`.
        # The line that matters is the unreadable one, which must not be touched.
        print(f"  tent state_json : {before!r} -> {after!r}"
              f"{'  (rewritten)' if before != after else '  (bytes preserved)'}")
        print(f"  tent food       : {food[0] if food else None}")
        print(f"  ap_budget next  : {budget}")
        print(f"  report notices  : {notices}")
    finally:
        conn.close()
        tmp.cleanup()


if __name__ == "__main__":
    print(f"tree: {Path(__file__).resolve().parents[3]}")
    for broken in (False, True):
        print(f"\ntent state_json = {'{' if broken else '{\"pitched\": true}'!s}")
        run(broken)
