#!/usr/bin/env python3
"""Local dev server for inspecting the l4d2web UI without a real deploy.

Sets the same env vars the production systemd unit gets from
/etc/left4me/*.env, runs alembic upgrade head, and starts Flask. First
run also seeds an admin user + a tiny demo content set (one blueprint,
one script overlay, one files overlay) so the editor is visible at all
three call sites immediately.

Does NOT mock l4d2ctl, so server-deploy actions (initialize/start/stop)
still fail — they need real systemd + steamcmd. Blueprint + overlay
CRUD work fine.

Usage:    scripts/dev-server.py [--port N]    (default: 5051)
Reset:    rm -rf .tmp/dev-server               (next run reseeds)
"""
import json
import os
import pathlib
import sqlite3
import subprocess
import sys
from datetime import UTC, datetime

REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
DEV_ROOT = REPO_ROOT / ".tmp" / "dev-server"
DB_FILE = DEV_ROOT / "l4d2web.db"


def seed_demo_content():
    """Insert one blueprint, one script overlay, one files overlay, one server."""
    overlay_root = DEV_ROOT / "overlays"
    conn = sqlite3.connect(DB_FILE)
    cur = conn.cursor()
    admin_id = cur.execute(
        "SELECT id FROM users WHERE admin = 1 LIMIT 1"
    ).fetchone()[0]
    now = datetime.now(UTC).isoformat()
    blueprint_id = None  # captured below; needed for the server row

    config_lines = [
        "// L4D2 dev demo — try the srccfg highlighter + autocomplete",
        "sv_cheats 0",
        "mp_gamemode coop",
        "z_difficulty Normal",
        "// Type sv_ on a new line to see the cvar popup:",
        "",
    ]
    cur.execute(
        "INSERT INTO blueprints "
        "(user_id, name, arguments, config, created_at, updated_at) "
        "VALUES (?, ?, ?, ?, ?, ?)",
        (admin_id, "demo-srccfg", json.dumps([]),
         json.dumps(config_lines), now, now),
    )
    blueprint_id = cur.lastrowid

    script_text = (
        "#!/usr/bin/env bash\n"
        "# L4D2 dev demo — try the bash highlighter\n"
        "set -euo pipefail\n"
        "\n"
        "for f in /overlay/*.cfg; do\n"
        '    echo "processing $f"\n'
        "done\n"
    )
    cur.execute(
        "INSERT INTO overlays (name, path, type, user_id, script, "
        "last_build_status, created_at, updated_at) "
        "VALUES (?, ?, 'script', NULL, ?, '', ?, ?)",
        ("demo-bash", "_pending", script_text, now, now),
    )
    script_id = cur.lastrowid
    cur.execute("UPDATE overlays SET path = ? WHERE id = ?",
                (str(script_id), script_id))
    (overlay_root / str(script_id)).mkdir(parents=True, exist_ok=True)

    cur.execute(
        "INSERT INTO overlays (name, path, type, user_id, script, "
        "last_build_status, created_at, updated_at) "
        "VALUES (?, ?, 'files', NULL, '', '', ?, ?)",
        ("demo-files", "_pending", now, now),
    )
    files_id = cur.lastrowid
    cur.execute("UPDATE overlays SET path = ? WHERE id = ?",
                (str(files_id), files_id))
    files_dir = overlay_root / str(files_id)
    files_dir.mkdir(parents=True, exist_ok=True)
    (files_dir / "test.cfg").write_text(
        "// open this file in the editor modal — flip the language\n"
        "// dropdown to bash to see the runtime language switch.\n"
        "sv_cheats 0\n"
    )

    # Server: links the blueprint above to a port. desired_state stays
    # "stopped" so the GUI shows it without trying to deploy via l4d2ctl.
    cur.execute(
        "INSERT INTO servers (user_id, blueprint_id, name, port, "
        "desired_state, actual_state, last_error, created_at, updated_at) "
        "VALUES (?, ?, 'demo-server', 27015, 'stopped', 'unknown', '', ?, ?)",
        (admin_id, blueprint_id, now, now),
    )
    server_id = cur.lastrowid

    conn.commit()
    conn.close()
    print(f"    blueprint id={blueprint_id} 'demo-srccfg' -> /blueprints/{blueprint_id}")
    print(f"    overlay   id={script_id} 'demo-bash'  (script) -> /overlays/{script_id}")
    print(f"    overlay   id={files_id} 'demo-files' (files)  -> /overlays/{files_id}")
    print(f"    server    id={server_id} 'demo-server' (:27015) -> /servers/{server_id}")


def main():
    port = 5051
    if "--port" in sys.argv:
        port = int(sys.argv[sys.argv.index("--port") + 1])

    DEV_ROOT.mkdir(parents=True, exist_ok=True)
    (DEV_ROOT / "overlays").mkdir(exist_ok=True)
    (DEV_ROOT / "instances").mkdir(exist_ok=True)

    # Production-equivalent env (systemd gets these from /etc/left4me/*.env).
    os.environ.update({
        "SECRET_KEY": "local-dev-only-not-a-secret",
        "DATABASE_URL": f"sqlite:///{DB_FILE}",
        "LEFT4ME_ROOT": str(DEV_ROOT),
        "SESSION_COOKIE_SECURE": "0",  # so http://127.0.0.1 cookies stick
        "JOB_WORKER_ENABLED": "false",  # don't fire (sudo-requiring) l4d2ctl
    })

    print(f"==> Migrating {DB_FILE} to head", flush=True)
    subprocess.run(["uv", "run", "alembic", "upgrade", "head"],
                   cwd=REPO_ROOT / "l4d2web", check=True, stdout=subprocess.DEVNULL)

    user_count = sqlite3.connect(DB_FILE).execute(
        "SELECT COUNT(*) FROM users").fetchone()[0]
    admin_user = os.environ.get("LEFT4ME_DEV_ADMIN_USERNAME", "dev")
    admin_pw = os.environ.get("LEFT4ME_DEV_ADMIN_PASSWORD", "devdevdev")
    if user_count == 0:
        print(f"==> Seeding admin user '{admin_user}'", flush=True)
        subprocess.run(
            ["uv", "run", "flask", "--app", "l4d2web.app:create_app()",
             "create-user", admin_user, "--admin"],
            cwd=REPO_ROOT, check=True,
            env={**os.environ, "LEFT4ME_ADMIN_PASSWORD": admin_pw},
        )
        print("==> Seeding demo content", flush=True)
        seed_demo_content()

    print(f"\n    admin: {admin_user} / {admin_pw}", flush=True)
    print(f"    db:    {DB_FILE}", flush=True)
    print(f"    root:  {DEV_ROOT}", flush=True)
    print(f"\n    Ctrl-C to stop. Reset all state: rm -rf .tmp/dev-server\n", flush=True)
    print(f"==> Starting Flask on http://127.0.0.1:{port}", flush=True)

    # --debug enables: Python code reload on save, template auto-reload,
    # verbose error pages, the Werkzeug interactive debugger. Safe here
    # because we bind to 127.0.0.1 only.
    os.execvp("uv", ["uv", "run", "flask",
                     "--app", "l4d2web.app:create_app()",
                     "--debug", "run", "--port", str(port)])


if __name__ == "__main__":
    main()
