#!/usr/bin/env python3
"""Tier-1 praxis backend stub — white-paper BACKEND.md contract (stdlib only).

Listens on 127.0.0.1:8080. Satisfies health + empty megapraxis coverage shapes so
HTTP consumers and health probes succeed. Does NOT provide MCP graph mode.

  py -3 backend_stub.py
  python3 backend_stub.py
"""
from __future__ import annotations

import json
import sqlite3
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from urllib.parse import parse_qs, urlparse

HOST, PORT = "127.0.0.1", 8080
DB_PATH = Path(__file__).resolve().parent / "stub_nodes.sqlite3"

EMPTY_COVERAGE = {
    "version": "1",
    "layers": {"meta": [], "genesis": [], "shadow": [], "mgs": []},
    "totals": {
        "total": 0,
        "per_layer": {"meta": 0, "genesis": 0, "shadow": 0, "mgs": 0},
        "unclassified": 0,
        "dark": 0,
    },
    "tarski_unclassified": [],
}


def db() -> sqlite3.Connection:
    conn = sqlite3.connect(DB_PATH)
    conn.execute(
        """CREATE TABLE IF NOT EXISTS nodes (
            id TEXT PRIMARY KEY,
            graph_id TEXT,
            name TEXT,
            x REAL DEFAULT 0,
            y REAL DEFAULT 0,
            payload TEXT
        )"""
    )
    conn.commit()
    return conn


class Handler(BaseHTTPRequestHandler):
    server_version = "praxis-backend-stub/1.5"

    def log_message(self, fmt: str, *args) -> None:
        print("%s - %s" % (self.address_string(), fmt % args))

    def _json(self, code: int, obj) -> None:
        raw = json.dumps(obj).encode("utf-8")
        self.send_response(code)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(raw)))
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        self.wfile.write(raw)

    def _read_json(self):
        n = int(self.headers.get("Content-Length") or 0)
        if n <= 0:
            return {}
        return json.loads(self.rfile.read(n).decode("utf-8"))

    def do_OPTIONS(self) -> None:  # noqa: N802
        self.send_response(204)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.end_headers()

    def do_GET(self) -> None:  # noqa: N802
        u = urlparse(self.path)
        path = u.path.rstrip("/") or "/"
        qs = parse_qs(u.query)

        if path == "/api/health":
            return self._json(200, {"status": "healthy", "tier": 1, "impl": "praxis-backend-stub"})

        if path == "/api/search":
            q = (qs.get("q") or [""])[0]
            limit = int((qs.get("limit") or ["20"])[0])
            conn = db()
            if q:
                rows = conn.execute(
                    "SELECT id, graph_id, name FROM nodes WHERE name LIKE ? LIMIT ?",
                    (f"%{q}%", limit),
                ).fetchall()
            else:
                rows = conn.execute(
                    "SELECT id, graph_id, name FROM nodes LIMIT ?", (limit,)
                ).fetchall()
            conn.close()
            nodes = [{"id": r[0], "graph_id": r[1], "name": r[2]} for r in rows]
            return self._json(200, {"nodes": nodes})

        if path in (
            "/api/megapraxis/mgs_coverage",
            "/api/megapraxis/agents_coverage",
        ):
            return self._json(200, EMPTY_COVERAGE)

        if path == "/api/megapraxis/verify_completeness":
            return self._json(
                200,
                {
                    "converge": {
                        "verdict": "incomplete",
                        "decidable_gaps": 0,
                        "tarski_acknowledged": True,
                        "note": "tier-1 stub — honest incomplete without live classifier",
                    }
                },
            )

        if path == "/api/megapraxis/swarms_active":
            return self._json(
                200,
                {
                    "active": [],
                    "methodology_active": [],
                    "recommended": [],
                    "total_scanned": 0,
                },
            )

        if path.startswith("/api/nodes/"):
            nid = path[len("/api/nodes/") :]
            conn = db()
            row = conn.execute(
                "SELECT id, graph_id, name, x, y, payload FROM nodes WHERE id=?", (nid,)
            ).fetchone()
            conn.close()
            if not row:
                return self._json(404, {"error": "not_found", "id": nid})
            payload = json.loads(row[5]) if row[5] else {}
            return self._json(
                200,
                {
                    "id": row[0],
                    "graph_id": row[1],
                    "name": row[2],
                    "x": row[3],
                    "y": row[4],
                    "values": payload,
                },
            )

        return self._json(404, {"error": "not_found", "path": path})

    def do_POST(self) -> None:  # noqa: N802
        u = urlparse(self.path)
        path = u.path.rstrip("/") or "/"
        body = self._read_json()
        if path in ("/api/nodes", "/api/nodes/create"):
            nid = body.get("id") or body.get("node_id")
            name = body.get("name") or ""
            if not nid or not name:
                return self._json(400, {"error": "id and name required"})
            conn = db()
            conn.execute(
                "INSERT OR REPLACE INTO nodes(id, graph_id, name, x, y, payload) VALUES (?,?,?,?,?,?)",
                (
                    nid,
                    body.get("graph_id") or "default",
                    name,
                    float(body.get("x") or 0),
                    float(body.get("y") or 0),
                    json.dumps(body.get("values") or {}),
                ),
            )
            conn.commit()
            conn.close()
            return self._json(201, {"id": nid, "name": name})
        return self._json(404, {"error": "not_found", "path": path})

    def do_PUT(self) -> None:  # noqa: N802
        return self.do_POST()


def main() -> None:
    db().close()
    httpd = ThreadingHTTPServer((HOST, PORT), Handler)
    print(f"praxis-backend-stub listening on http://{HOST}:{PORT}")
    print("contract: /api/health /api/search /api/megapraxis/* /api/nodes")
    print("tier=1 · MCP not provided · Ctrl+C to stop")
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\nshutdown")
        httpd.server_close()


if __name__ == "__main__":
    main()
