#!/usr/bin/env python3
"""
phone-mic/server.py — turn any phone into a wireless mic (open source).

Each phone opens this page, records its microphone, and POSTs audio chunks
over WiFi to this server. Each phone writes to its own file in ./recordings/.
Then transcode/mix with ffmpeg, or feed the main hub (hub.py).

Usage:
  python3 server.py [port] [--https]   # http(s)://0.0.0.0:PORT/

NOTE: browsers only allow microphone access over HTTPS (or localhost).
Use `--https` (auto-generates a self-signed cert) and trust the cert once on
each phone, OR use the hosted VDO.Ninja for a zero-setup WebRTC path.
"""
import argparse
import re
import ssl
import subprocess
import sys
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from urllib.parse import parse_qs, urlparse

BASE = Path(__file__).resolve().parent
REC = BASE / "recordings"
REC.mkdir(exist_ok=True)
CERTS = BASE / "certs"
HTML = (BASE / "index.html").read_text()


def ensure_cert():
    CERTS.mkdir(exist_ok=True)
    cert, key = CERTS / "cert.pem", CERTS / "key.pem"
    if not (cert.exists() and key.exists()):
        subprocess.run([
            "openssl", "req", "-x509", "-newkey", "rsa:2048",
            "-keyout", str(key), "-out", str(cert),
            "-days", "3650", "-nodes",
            "-subj", "/CN=phone-mic",
        ], check=True, capture_output=True)
    return str(cert), str(key)


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        path = urlparse(self.path).path
        if path in ("/", "/index.html"):
            body = HTML.encode()
            self._respond(200, "text/html; charset=utf-8", body)
        elif path == "/status":
            files = sorted(REC.iterdir(), key=lambda p: -p.stat().st_size)
            body = "".join(f"{f.name}: {f.stat().st_size} bytes\n" for f in files).encode()
            self._respond(200, "text/plain", body)
        else:
            self.send_error(404)

    def do_POST(self):
        path = urlparse(self.path).path
        if path == "/push":
            q = parse_qs(urlparse(self.path).query)
            pid = (q.get("id") or ["phone"])[0]
            pid = re.sub(r"[^A-Za-z0-9_.-]", "", pid) or "phone"
            sess = re.sub(r"[^0-9]", "", (q.get("s") or [""])[0])
            mime = ((q.get("t") or [""])[0] + self.headers.get("Content-Type", "")).lower()
            ext = "m4a" if "mp4" in mime else "webm"  # iPhone Safari records mp4, Android/Chrome webm
            n = int(self.headers.get("Content-Length", 0))
            data = self.rfile.read(n)
            name = f"{pid}_{sess}.{ext}" if sess else f"{pid}.{ext}"
            with open(REC / name, "ab") as f:
                f.write(data)
            self._respond(200, "text/plain", b"ok")
        else:
            self.send_error(404)

    def _respond(self, code, ctype, body):
        self.send_response(code)
        self.send_header("Content-Type", ctype)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, *a):
        pass


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("port", nargs="?", type=int, default=8200)
    ap.add_argument("--https", action="store_true")
    a = ap.parse_args()

    server = ThreadingHTTPServer(("0.0.0.0", a.port), Handler)
    server.daemon_threads = True
    scheme = "http"
    if a.https:
        cert, key = ensure_cert()
        ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        ctx.load_cert_chain(cert, key)
        server.socket = ctx.wrap_socket(server.socket, server_side=True)
        scheme = "https"

    print(f"phone-mic -> {scheme}://0.0.0.0:{a.port}/  (open on each phone)")
    print(f"  recordings -> {REC}")
    print("  add ?id=NAME to the URL to name each phone")
    server.serve_forever()


if __name__ == "__main__":
    main()
