"""Test Caddy HTTP + WebSocket forwarding. Run: python3 smoke-test.py /path/to/caddy."""
import os
import pathlib
import subprocess
import sys
import tempfile
import time
import urllib.request

binary = sys.argv[1] if len(sys.argv) > 1 else "caddy"
example = pathlib.Path(__file__).resolve().parents[1]
# Test the published Caddyfile with a local HTTP listener in place of the public domain.
with tempfile.TemporaryDirectory(prefix="browser-hosting-") as directory:
    config = pathlib.Path(directory) / "Caddyfile"
    config.write_text((example / "deploy/Caddyfile").read_text().replace("game.example.com", "http://127.0.0.1:18080").replace("127.0.0.1:3000", "127.0.0.1:13000"))
    environment = dict(os.environ, XDG_DATA_HOME=directory, XDG_CONFIG_HOME=directory, PORT="13000")
    with open(pathlib.Path(directory) / "server.log", "w+") as server_log, open(pathlib.Path(directory) / "caddy.log", "w+") as proxy_log:
        server = subprocess.Popen(["node", "server.js"], cwd=example, env=environment, stdout=server_log, stderr=server_log)
        proxy = subprocess.Popen([binary, "run", "--config", str(config), "--adapter", "caddyfile"], env=environment, stdout=proxy_log, stderr=proxy_log)
        try:
            deadline = time.monotonic() + 10
            while time.monotonic() < deadline:
                try:
                    with urllib.request.urlopen("http://127.0.0.1:18080/", timeout=1) as response:
                        assert response.status == 200 and b"Multiplayer demo" in response.read()
                    break
                except (OSError, AssertionError):
                    time.sleep(0.1)
            else:
                raise RuntimeError("Proxy/server startup failed")
            print("PASS HTTP page through Caddy")
            test = subprocess.run(["node", "--input-type=module", "-e", '''
import assert from "node:assert/strict";
const clients = [new WebSocket("ws://127.0.0.1:18080"), new WebSocket("ws://127.0.0.1:18080")];
const ids = new Set();
const counts = new Set();
let pongs = 0;
const timer = setTimeout(() => { console.error("WebSocket smoke test timed out"); process.exit(1); }, 5000);
for (const ws of clients) {
  ws.onopen = () => {
    ws.send(JSON.stringify({type: "input", input: {up: false, down: false, left: false, right: true}}));
    ws.send(JSON.stringify({type: "ping", t: 1234}));
  };
  ws.onerror = () => process.exit(1);
  ws.onmessage = ({data}) => {
    const message = JSON.parse(data);
    if (message.type === "welcome") ids.add(message.id);
    if (message.type === "pong") { assert.equal(message.t, 1234); pongs++; }
    if (message.type === "state" && message.players.length === 2) counts.add(ws);
    if (ids.size === 2 && counts.size === 2 && pongs === 2) {
      clearTimeout(timer);
      console.log("PASS two WebSocket players, shared state and ping replies through Caddy");
      clients.forEach(client => client.close());
    }
  };
}
'''], cwd=example, capture_output=True, text=True, timeout=10)
            if test.returncode:
                raise RuntimeError((test.stdout + test.stderr)[:2000])
            print(test.stdout.strip())
        finally:
            for process in (proxy, server):
                process.terminate()
                process.wait(timeout=5)
            if sys.exc_info()[0]:
                for log in (server_log, proxy_log):
                    log.seek(0)
                    print(log.read()[:2000], file=sys.stderr)
