#!/usr/bin/env python3
from __future__ import annotations

import json
import os
import subprocess
import sys
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[2]
RUNTIME_ROOT = Path.home() / ".timmy" / "evennia"
GAME_DIR = RUNTIME_ROOT / "timmy_world"
VENV_DIR = RUNTIME_ROOT / "venv"
EVENNIA_BIN = VENV_DIR / "bin" / "evennia"
PYTHON_BIN = VENV_DIR / "bin" / "python3"
OPERATOR_USER = os.environ.get("TIMMY_EVENNIA_OPERATOR", "Alexander")
OPERATOR_EMAIL = os.environ.get("TIMMY_EVENNIA_OPERATOR_EMAIL", "alexpaynex@gmail.com")
OPERATOR_PASSWORD = os.environ.get("TIMMY_EVENNIA_OPERATOR_PASSWORD", "timmy-local-dev")
TIMMY_PASSWORD = os.environ.get("TIMMY_EVENNIA_TIMMY_PASSWORD", "timmy-world-dev")


def ensure_venv():
    if not PYTHON_BIN.exists():
        subprocess.run([sys.executable, "-m", "venv", str(VENV_DIR)], check=True)
        subprocess.run([str(PYTHON_BIN), "-m", "pip", "install", "--upgrade", "pip"], check=True)
        subprocess.run([str(PYTHON_BIN), "-m", "pip", "install", "evennia"], check=True)


def ensure_game_dir():
    if not GAME_DIR.exists():
        RUNTIME_ROOT.mkdir(parents=True, exist_ok=True)
        subprocess.run([str(EVENNIA_BIN), "--init", "timmy_world"], cwd=RUNTIME_ROOT, check=True)


def run_evennia(args, env=None, timeout=600):
    result = subprocess.run([str(EVENNIA_BIN), *args], cwd=GAME_DIR, env=env, timeout=timeout, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(result.stderr or result.stdout)
    return result.stdout


def ensure_db_and_owner():
    run_evennia(["migrate"])
    env = os.environ.copy()
    env.update(
        {
            "EVENNIA_SUPERUSER_USERNAME": OPERATOR_USER,
            "EVENNIA_SUPERUSER_EMAIL": OPERATOR_EMAIL,
            "EVENNIA_SUPERUSER_PASSWORD": OPERATOR_PASSWORD,
        }
    )
    run_evennia(["shell", "-c", "from evennia.accounts.models import AccountDB; print(AccountDB.objects.count())"], env=env)


def ensure_server_started():
    run_evennia(["start"], timeout=240)


def run_shell(code: str):
    env = os.environ.copy()
    env["PYTHONPATH"] = str(REPO_ROOT) + os.pathsep + env.get("PYTHONPATH", "")
    return run_evennia(["shell", "-c", code], env=env)


def ensure_timmy_and_world():
    code = f'''
import sys
sys.path.insert(0, {str(REPO_ROOT)!r})
from evennia.accounts.models import AccountDB
from evennia.accounts.accounts import DefaultAccount
from evennia import DefaultRoom, DefaultExit, create_object
from evennia.utils.search import search_object
from evennia_tools.layout import ROOMS, EXITS, OBJECTS
from typeclasses.objects import Object

acc = AccountDB.objects.filter(username__iexact="Timmy").first()
if not acc:
    acc, errs = DefaultAccount.create(username="Timmy", password={TIMMY_PASSWORD!r})

room_map = {{}}
for room in ROOMS:
    found = search_object(room.key, exact=True)
    obj = found[0] if found else None
    if obj is None:
        obj, errs = DefaultRoom.create(room.key, description=room.desc)
    else:
        obj.db.desc = room.desc
    room_map[room.key] = obj

for ex in EXITS:
    source = room_map[ex.source]
    dest = room_map[ex.destination]
    found = [obj for obj in source.contents if obj.key == ex.key and getattr(obj, "destination", None) == dest]
    if not found:
        DefaultExit.create(ex.key, source, dest, description=f"Exit to {{dest.key}}.", aliases=list(ex.aliases))

for spec in OBJECTS:
    location = room_map[spec.location]
    found = [obj for obj in location.contents if obj.key == spec.key]
    if not found:
        obj = create_object(typeclass=Object, key=spec.key, location=location)
    else:
        obj = found[0]
    obj.db.desc = spec.desc

char = list(acc.characters)[0]
char.location = room_map["Gate"]
char.home = room_map["Gate"]
char.save()
print("WORLD_OK")
print("TIMMY_LOCATION", char.location.key)
'''
    return run_shell(code)


def verify():
    status = run_evennia(["status"], timeout=120)
    info = run_evennia(["info"], timeout=120)
    web = subprocess.run(
        [sys.executable, "-c", "import urllib.request; r=urllib.request.urlopen('http://127.0.0.1:4001', timeout=10); print(r.status); print(r.headers.get('Content-Type'))"],
        timeout=30,
        capture_output=True,
        text=True,
        check=True,
    )
    return {"status": status.strip(), "info": info.strip(), "web": web.stdout.strip()}


def main():
    ensure_venv()
    ensure_game_dir()
    ensure_db_and_owner()
    ensure_server_started()
    provision = ensure_timmy_and_world()
    proof = verify()
    print(json.dumps({"provision_stdout": provision, "verification": proof}, indent=2))


if __name__ == "__main__":
    main()
