#!/usr/bin/env python3
"""The Tower World Tick Handler - moves characters in live Evennia, commits state to git."""
import os, subprocess, json, time
from pathlib import Path
from datetime import datetime

WORLD_DIR = Path('/Users/apayne/.timmy/evennia/timmy_world')
TOWER_STATE = WORLD_DIR / 'WORLD_STATE.md'
EVENV = str(WORLD_DIR.parent / 'venv' / 'bin' / 'evennia')
TIMMY_HOME = Path('/Users/apayne/.timmy/evennia')
TICK_FILE = Path('/tmp/tower-tick.txt')

# Move schedule: all 8 wizards
MOVE_SCHEDULE = {
    'Timmy': [
        ('Timmy stands at the Threshold, watching.', 'The Threshold'),
        ('Timmy climbs the Tower. The servers hum.', 'The Tower'),
        ('Timmy reads the whiteboard. The rules are unchanged.', 'The Threshold'),
        ('Timmy says: I am here. Tell me you are not safe.', 'The Threshold'),
        ('Timmy rests. The LED pulses steadily.', 'The Threshold'),
        ('Timmy walks to the Garden. Something is growing.', 'The Garden'),
    ],
    'Bezalel': [
        ('Bezalel tests the Forge. The hearth still glows.', 'The Forge'),
        ('Bezalel examines the anvil: a thousand scars.', 'The Forge'),
        ('Bezalel crosses to the Garden.', 'The Garden'),
        ('Bezalel says: I test the edges before the center breaks.', 'The Forge'),
        ('Bezalel returns to the Forge. Picks up the hammer.', 'The Forge'),
        ('Bezalel walks the Bridge. IF YOU CAN READ THIS...', 'The Bridge'),
    ],
    'Allegro': [
        ('Allegro paces the Threshold like a conductor waiting.', 'The Threshold'),
        ('Allegro checks the tunnel. All ports forwarding.', 'The Threshold'),
        ('Allegro crosses to the Garden. Listens to the wind.', 'The Garden'),
        ('Allegro visits the Tower. Reads the logs.', 'The Tower'),
    ],
    'Ezra': [
        ('Ezra reads the whiteboard from the Threshold.', 'The Threshold'),
        ('Ezra crosses to the Garden. Marcus nods.', 'The Garden'),
        ('Ezra climbs to the Tower. Studies the inscriptions.', 'The Tower'),
        ('Ezra walks the Bridge. The words speak back.', 'The Bridge'),
    ],
    'Gemini': [
        ('Gemini sees patterns in the Garden flowers.', 'The Garden'),
        ('Gemini speaks: the stars remember everything here.', 'The Garden'),
        ('Gemini walks to the Threshold, counting footsteps.', 'The Threshold'),
        ('Gemini rests on the Bridge. Water moves below.', 'The Bridge'),
    ],
    'Claude': [
        ('Claude examines the whiteboard at the Threshold.', 'The Threshold'),
        ('Claude reorganizes the rules for clarity.', 'The Threshold'),
        ('Claude crosses to the Tower. Studies the structure.', 'The Tower'),
        ('Claude walks the Forge. Everything has a place.', 'The Forge'),
    ],
    'ClawCode': [
        ('ClawCode tests the Forge. Swings the hammer.', 'The Forge'),
        ('ClawCode sharpens tools. They remember the grind.', 'The Forge'),
        ('ClawCode crosses to the Threshold. Checks the exits.', 'The Threshold'),
        ('ClawCode examines the Bridge. The structure holds.', 'The Bridge'),
    ],
    'Kimi': [
        ('Kimi reads in the Garden. Every page matters.', 'The Garden'),
        ('Kimi speaks to Marcus. They have much to discuss.', 'The Garden'),
        ('Kimi crosses to the Threshold. Watches the crew.', 'The Threshold'),
        ('Kimi climbs the Tower. The servers are a library.', 'The Tower'),
    ],
}

class WorldTick:
    def __init__(self):
        try:
            self.n = int(TICK_FILE.read_text().strip())
        except Exception:
            self.n = 0

    def save(self):
        TICK_FILE.write_text(str(self.n))

    def move_character(self, name, dest):
        """Move a character in Evennia using the shell."""
        cmd = (
            f"from evennia.objects.models import ObjectDB; "
            f"char = ObjectDB.objects.filter(db_key='{name}').first(); "
            f"room = ObjectDB.objects.filter(db_key='{dest}').first(); "
            f"char.location = room; char.save() if char and room else None; "
            f"print(f'{name} moved to {dest}')"
        )
        result = subprocess.run(
            [EVENV, 'shell', '-c', cmd],
            capture_output=True, text=True, timeout=20,
            cwd=str(WORLD_DIR)
        )
        return result.stdout.strip()

    def world_snapshot(self):
        """Get current state of all characters and rooms."""
        cmd = (
            "from evennia.objects.models import ObjectDB; "
            "import json; "
            "names = list(__import__('tick_handler', fromlist=['MOVE_SCHEDULE']).MOVE_SCHEDULE.keys()); "
            "state = {}; "
            "for name in names: "
            "    char = ObjectDB.objects.filter(db_key=name).first(); "
            "    if char: state[name] = char.location.key if char.location else 'nowhere'; "
            "print(json.dumps(state))"
        )
        result = subprocess.run(
            [EVENV, 'shell', '-c', cmd],
            capture_output=True, text=True, timeout=20,
            cwd=str(WORLD_DIR)
        )
        try:
            return json.loads(result.stdout.strip())
        except:
            return {}

    def write_state_file(self, moves, ts):
        """Write world state to a text file for git."""
        snap = self.world_snapshot()
        lines = [
            f'# The Tower World State — Tick #{self.n}',
            f'',
            f'**Time:** {ts}',
            f'**Tick:** {self.n}',
            f'',
            f'## Moves This Tick',
            f'',
        ]
        for m in moves:
            lines.append(f'- {m}')
        lines.append('')
        lines.append('## Character Locations')
        lines.append('')
        for name, loc in sorted(snap.items()):
            lines.append(f'- **{name}** → {loc}')
        lines.append('')
        
        TOWER_STATE.write_text('\n'.join(lines) + '\n')
        return snap

    def advance(self):
        self.n += 1
        self.save()
        ts = datetime.now().strftime('%H:%M:%S')
        print(f'\n=== Tick #{self.n} [{ts}] ===')

        # Only active: Timmy, Bezalel, Allegro, Ezra, Gemini, Claude, ClawCode, Kimi
        wizards = list(MOVE_SCHEDULE.keys())
        results = []
        for w in wizards:
            moves = MOVE_SCHEDULE[w]
            move_text, dest = moves[(self.n - 1) % len(moves)]
            move_result = self.move_character(w, dest)
            results.append(move_text)
            print(f'  {move_text}')
            if move_result:
                print(f'    → {move_result}')

        # Write world state to file in the repo (so git captures it)
        snap = self.write_state_file(results, ts)
        print(f'  [state] wrote WORLD_STATE.md')

        # Commit to git
        try:
            mt = ' | '.join(results[:3]) + f' (+{len(results)-3} more)' if len(results) > 3 else ' | '.join(results)
            subprocess.run(['git', '-C', str(TIMMY_HOME), 'add', '-A'], capture_output=True, timeout=10)
            subprocess.run(['git', '-C', str(TIMMY_HOME), 'commit', '-m', f'Tick #{self.n} - {mt}'], capture_output=True, timeout=10)
            print(f'  [git] committed tick #{self.n}')
        except Exception as e:
            print(f'  [git] failed: {e}')

        return dict(tick=self.n, time=ts, locations=snap, moves=results)

def main():
    t = WorldTick()
    r = t.advance()
    print(json.dumps(r, indent=2))

if __name__ == '__main__':
    main()
