#!/usr/bin/env python3
"""
SHADES — dev server. Python stdlib only: no pip, no venv, no build step, no CDN.

    python3 server.py                # serve the yard on :8801
    python3 server.py --port 9000
    python3 server.py --selfcheck    # verify the tree, print URLs, exit

House rule (PLAN3D §0): zero dependencies. If this file ever seems to need
something from PyPI, it doesn't.

It serves the repo root rather than web/, so the 2D prototype stays reachable
next to the 3D game — you want to be able to flip between the reference
implementation and the port without stopping the server.
"""

from __future__ import annotations

import argparse
import http.server
import mimetypes
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent
DEFAULT_PORT = 8801

GAME = "/web/world/index.html"
SELFTEST = "/web/world/selftest.html"
PROTOTYPE = "/prototype/index.html"

# The files without which nothing boots. --selfcheck reports on exactly these.
REQUIRED = (
    "web/world/index.html",
    "web/world/selftest.html",
    "web/world/vendor/three.module.js",
    "web/world/vendor/three.core.js",
    "web/world/js/contracts.js",
    "web/world/js/main.js",
    "web/world/js/world.js",
    "web/world/js/camera.js",
    "web/world/js/testkit.js",
    "prototype/index.html",
)

# Don't inherit whatever the OS thinks .js is — some macOS setups still map it
# to application/x-javascript, which browsers refuse to load as a module.
mimetypes.add_type("text/javascript", ".js")
mimetypes.add_type("application/json", ".json")
mimetypes.add_type("model/gltf-binary", ".glb")
mimetypes.add_type("model/gltf+json", ".gltf")


class Handler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, directory=str(ROOT), **kwargs)

    def end_headers(self):
        # Dev only, and deliberate: nothing costs more time than debugging a
        # stale module you already fixed.
        self.send_header("Cache-Control", "no-store, no-cache, must-revalidate")
        super().end_headers()

    def do_GET(self):
        if self.path in ("/", "/index.html"):
            self.send_response(302)
            self.send_header("Location", GAME)
            self.end_headers()
            return
        super().do_GET()

    def log_message(self, fmt, *args):
        # Quiet the 200s (vendor alone is a wall of them); shout about the rest.
        if len(args) > 1 and str(args[1]) == "200":
            return
        super().log_message(fmt, *args)


def selfcheck(port: int) -> int:
    """Verify the tree and print where things live. Deliberately dumb: no node,
    no headless browser, no network. Exit 0 if the game can boot."""
    missing = []
    print(f"shades selfcheck — root {ROOT}")
    for rel in REQUIRED:
        ok = (ROOT / rel).exists()
        if not ok:
            missing.append(rel)
        print(f"  {'ok  ' if ok else 'MISS'}  {rel}")

    vendor = ROOT / "web/world/vendor"
    n_vendor = len(list(vendor.rglob("*.js"))) if vendor.is_dir() else 0
    print(f"  {'ok  ' if n_vendor else 'MISS'}  vendored three.js files: {n_vendor}")

    print()
    if missing:
        print(f"FAIL — {len(missing)} required file(s) missing:")
        for rel in missing:
            print(f"       {rel}")
        return 1

    print("PASS — tree is complete. Run `python3 server.py`, then:")
    print(f"       game      http://localhost:{port}{GAME}")
    print(f"       selftest  http://localhost:{port}{SELFTEST}")
    print(f"       prototype http://localhost:{port}{PROTOTYPE}")
    print()
    print("       The selftest asserts in-page and prints a JSON report to the")
    print("       console; it drives the sim with fixed-dt loops, so it does not")
    print("       need the tab focused.")
    return 0


def serve(port: int) -> None:
    with http.server.ThreadingHTTPServer(("", port), Handler) as httpd:
        print(f"shades on http://localhost:{port}")
        print(f"  game      http://localhost:{port}{GAME}")
        print(f"  selftest  http://localhost:{port}{SELFTEST}")
        print(f"  prototype http://localhost:{port}{PROTOTYPE}")
        print("ctrl-c to stop")
        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            print("\nstopped")


def main() -> None:
    ap = argparse.ArgumentParser(description="SHADES dev server (stdlib only)")
    ap.add_argument("--port", type=int, default=DEFAULT_PORT, help=f"default {DEFAULT_PORT}")
    ap.add_argument("--selfcheck", action="store_true", help="verify the tree and print URLs, then exit")
    args = ap.parse_args()

    if args.selfcheck:
        sys.exit(selfcheck(args.port))
    serve(args.port)


if __name__ == "__main__":
    main()
