#!/usr/bin/env python3
"""HTTP server with gzip compression + Thai vocab sync API."""
import http.server
import gzip
import json
import os
import re
from io import BytesIO

PORT = 8080
BIND = "0.0.0.0"
ROOT = "/home/mi/.openclaw/workspace"
DATA_DIR = os.path.join(ROOT, "game_data")

os.makedirs(DATA_DIR, exist_ok=True)

GZIPPED = {}

def precompress():
    for fname in ["thai-vocab.html", "thai-flashcards.html"]:
        path = os.path.join(ROOT, fname)
        if os.path.exists(path):
            buf = BytesIO()
            with open(path, "rb") as src:
                with gzip.GzipFile(fileobj=buf, mode="wb", compresslevel=6) as gz:
                    gz.write(src.read())
            GZIPPED["/" + fname] = buf.getvalue()
            orig = os.path.getsize(path)
            gzsz = len(GZIPPED["/" + fname])
            print(f"Pre-compressed {fname}: {orig//1024}KB → {gzsz//1024}KB ({100*gzsz//orig}%)")

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

    def send_cors(self):
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")

    def send_json(self, data, status=200):
        self.send_response(status)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.end_headers()
        self.wfile.write(json.dumps(data, ensure_ascii=False).encode())

    def end_headers(self):
        self.send_header("Cache-Control", "public, max-age=3600")
        super().end_headers()

    def do_OPTIONS(self):
        self.send_cors()
        self.end_headers()

    def _serve_gzip_html(self, path):
        # Pre-compressed
        if path in GZIPPED:
            data = GZIPPED[path]
            self.send_response(200)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.send_header("Content-Encoding", "gzip")
            self.send_header("Content-Length", str(len(data)))
            self.send_header("Cache-Control", "public, max-age=3600")
            self.end_headers()
            self.wfile.write(data)
            return True

        # Dynamic gzip
        accept = self.headers.get("Accept-Encoding", "")
        if "gzip" in accept:
            fpath = self.translate_path(path)
            if os.path.isfile(fpath):
                with open(fpath, "rb") as f:
                    content = f.read()
                buf = BytesIO()
                with gzip.GzipFile(fileobj=buf, mode="wb", compresslevel=6) as gz:
                    gz.write(content)
                compressed = buf.getvalue()
                self.send_response(200)
                self.send_header("Content-Type", self.guess_type(fpath))
                self.send_header("Content-Encoding", "gzip")
                self.send_header("Content-Length", str(len(compressed)))
                self.end_headers()
                self.wfile.write(compressed)
                return True
        return False

    # --- API ---
    def handle_api_get(self, uid):
        if not uid or not re.match(r'^[\w-]+$', uid):
            return self.send_json({"error": "invalid uid"}, 400)
        fpath = os.path.join(DATA_DIR, f"{uid}.json")
        if os.path.exists(fpath):
            with open(fpath) as f:
                data = json.load(f)
            return self.send_json(data)
        return self.send_json({"wrongWords": [], "stats": {}})

    def handle_api_post(self, uid):
        if not uid or not re.match(r'^[\w-]+$', uid):
            return self.send_json({"error": "invalid uid"}, 400)
        cl = int(self.headers.get("Content-Length", 0))
        body = self.rfile.read(cl) if cl > 0 else b"{}"
        try:
            data = json.loads(body)
        except Exception:
            return self.send_json({"error": "invalid json"}, 400)
        fpath = os.path.join(DATA_DIR, f"{uid}.json")
        with open(fpath, "w") as f:
            json.dump(data, f, ensure_ascii=False)
        self.send_json({"ok": True})

    def do_GET(self):
        path = self.path.split("?")[0]

        # List backups
        if path == "/api/backups":
            results = []
            for fname in sorted(os.listdir(DATA_DIR), reverse=True):
                if not fname.endswith(".json"): continue
                uid = fname[:-5]
                fpath = os.path.join(DATA_DIR, fname)
                try:
                    with open(fpath) as f:
                        d = json.load(f)
                    results.append({
                        "id": uid,
                        "wordStats": len(d.get("wordStats", {})),
                        "favWords": len(d.get("favWords", [])),
                        "wrongWords": len(d.get("wrongWords", [])),
                        "masteryMode": d.get("masteryMode", "?")
                    })
                except: pass
            results.sort(key=lambda x: x["wordStats"], reverse=True)
            return self.send_json(results)

        # API routes
        if path.startswith("/api/"):
            uid = path[5:].strip()
            return self.handle_api_get(uid)

        # Serve gzip HTML
        if self._serve_gzip_html(path):
            return

        super().do_GET()

    def do_POST(self):
        path = self.path.split("?")[0]
        if path.startswith("/api/"):
            uid = path[5:].strip()
            return self.handle_api_post(uid)
        self.send_json({"error": "not found"}, 404)

if __name__ == "__main__":
    precompress()
    http.server.HTTPServer.allow_reuse_address = True
    server = http.server.HTTPServer((BIND, PORT), Handler)
    print(f"Serving {ROOT} on {BIND}:{PORT} (gzip + API)")
    server.serve_forever()
