"""
Nexa Language — Package Registry Server (Phase 6 / v26)
registry.nexa-lang.dev (local/cloud deployment)

Serves the REST API for:
    GET  /packages                    — list all packages
    GET  /packages/<name>             — get package metadata
    GET  /packages/<name>/<version>   — get specific version
    POST /packages/publish            — publish (auth required)
    GET  /packages/<name>/download    — download tarball
    GET  /search?q=<query>            — search packages

Run:
    python -m nexa_lang.registry_server            (dev, port 8765)
    python -m nexa_lang.registry_server --port 443 (prod)
"""
from __future__ import annotations
import hashlib, json, os, time
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
from typing import Any

# ── Storage (JSON file-based, swap with PostgreSQL for production) ─────────────
REGISTRY_DIR     = os.path.expanduser("~/.nexa/registry")
PACKAGES_DB      = os.path.join(REGISTRY_DIR, "packages.json")
TARBALLS_DIR     = os.path.join(REGISTRY_DIR, "tarballs")


def _load_db() -> dict:
    os.makedirs(REGISTRY_DIR, exist_ok=True)
    if not os.path.exists(PACKAGES_DB):
        return {"packages": {}}
    with open(PACKAGES_DB) as f:
        return json.load(f)


def _save_db(db: dict):
    with open(PACKAGES_DB, "w") as f:
        json.dump(db, f, indent=2)


def _json_response(handler: BaseHTTPRequestHandler, status: int, body: Any):
    payload = json.dumps(body, indent=2).encode()
    handler.send_response(status)
    handler.send_header("Content-Type", "application/json")
    handler.send_header("Content-Length", str(len(payload)))
    handler.send_header("Access-Control-Allow-Origin", "*")
    handler.end_headers()
    handler.wfile.write(payload)


# ── Request Handler ────────────────────────────────────────────────────────────
class RegistryHandler(BaseHTTPRequestHandler):

    def log_message(self, fmt, *args):
        print(f"  [registry] {self.address_string()} — {fmt % args}")

    def do_GET(self):
        parsed = urlparse(self.path)
        path   = parsed.path.rstrip("/")
        qs     = parse_qs(parsed.query)
        db     = _load_db()

        # GET /packages
        if path == "/packages":
            pkgs = [
                {"name": n, "latest": v["latest"],
                 "description": v.get("description", ""),
                 "author": v.get("author", "unknown"),
                 "downloads": v.get("downloads", 0)}
                for n, v in db["packages"].items()
            ]
            return _json_response(self, 200, {"packages": pkgs, "total": len(pkgs)})

        # GET /packages/<name>
        if path.startswith("/packages/"):
            parts = path.split("/")
            name = parts[2] if len(parts) > 2 else None
            version = parts[3] if len(parts) > 3 else None

            pkg = db["packages"].get(name)
            if not pkg:
                return _json_response(self, 404, {"error": f"Package '{name}' not found"})

            if version:
                ver_data = pkg.get("versions", {}).get(version)
                if not ver_data:
                    return _json_response(self, 404, {"error": f"Version '{version}' not found"})
                return _json_response(self, 200, ver_data)

            return _json_response(self, 200, {
                "name":        name,
                "latest":      pkg["latest"],
                "description": pkg.get("description", ""),
                "author":      pkg.get("author", "unknown"),
                "license":     pkg.get("license", "MIT"),
                "homepage":    pkg.get("homepage", ""),
                "keywords":    pkg.get("keywords", []),
                "versions":    list(pkg.get("versions", {}).keys()),
                "downloads":   pkg.get("downloads", 0),
            })

        # GET /search?q=<query>
        if path == "/search":
            q = qs.get("q", [""])[0].lower()
            results = []
            for name, pkg in db["packages"].items():
                score = 0
                if q in name.lower(): score += 10
                if q in pkg.get("description", "").lower(): score += 5
                if q in " ".join(pkg.get("keywords", [])).lower(): score += 3
                if score > 0:
                    results.append({"name": name, "score": score,
                                    "description": pkg.get("description", ""),
                                    "latest": pkg["latest"]})
            results.sort(key=lambda r: -r["score"])
            return _json_response(self, 200, {"results": results, "query": q})

        # GET /health
        if path == "/health":
            return _json_response(self, 200, {
                "status": "ok",
                "registry": "registry.nexa-lang.dev (local)",
                "packages": len(db["packages"]),
                "version": "26.0",
                "time": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
            })

        _json_response(self, 404, {"error": "Not found"})

    def do_POST(self):
        parsed = urlparse(self.path)
        path   = parsed.path.rstrip("/")

        if path == "/packages/publish":
            length = int(self.headers.get("Content-Length", 0))
            body   = json.loads(self.rfile.read(length).decode())

            # Basic auth check (token from Authorization header)
            token = self.headers.get("Authorization", "")
            if not token.startswith("Bearer "):
                return _json_response(self, 401, {"error": "Unauthorized — Bearer token required"})

            name    = body.get("name")
            version = body.get("version", "0.1.0")
            desc    = body.get("description", "")
            author  = body.get("author", "unknown")
            files   = body.get("files", {})
            sig     = body.get("signature", None)   # optional signing manifest

            if not name:
                return _json_response(self, 400, {"error": "Package name is required"})

            db = _load_db()
            if name not in db["packages"]:
                db["packages"][name] = {"latest": version, "versions": {},
                                         "description": desc, "author": author,
                                         "downloads": 0, "keywords": body.get("keywords", [])}

            db["packages"][name]["versions"][version] = {
                "name": name, "version": version,
                "description": desc, "author": author,
                "files": files,
                "signature": sig,
                "published_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
                "checksum": hashlib.sha256(json.dumps(files, sort_keys=True).encode()).hexdigest(),
            }
            db["packages"][name]["latest"] = version
            _save_db(db)

            print(f"  📦 Published: {name}@{version} by {author}")
            return _json_response(self, 201, {
                "status": "published",
                "name": name, "version": version,
                "url": f"https://registry.nexa-lang.dev/packages/{name}/{version}",
            })

        _json_response(self, 404, {"error": "Not found"})

    def do_OPTIONS(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, Authorization")
        self.end_headers()


def run_registry(host: str = "0.0.0.0", port: int = 8765):
    os.makedirs(TARBALLS_DIR, exist_ok=True)
    print(f"""
  📦 Nexa Package Registry v26
  ─────────────────────────────
  Listening on http://{host}:{port}
  Storage:    {PACKAGES_DB}
  Endpoints:
    GET  /packages          — list all packages
    GET  /packages/<name>   — get package info
    GET  /search?q=<query>  — search packages
    POST /packages/publish  — publish a package
    GET  /health            — health check
  ─────────────────────────────
  Press Ctrl+C to stop.
""")
    server = HTTPServer((host, port), RegistryHandler)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n  Registry stopped.")
        server.shutdown()


if __name__ == "__main__":
    import argparse
    p = argparse.ArgumentParser(description="Nexa Package Registry Server")
    p.add_argument("--host", default="0.0.0.0")
    p.add_argument("--port", type=int, default=8765)
    args = p.parse_args()
    run_registry(args.host, args.port)
