"""C08 local teaching service. Run: python activity_server.py --port 8766.

Fictional immutable activities; draft state is reset on restart.
Only binds loopback. This is a teaching fixture, not a deployment server.
"""
import argparse
import json
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs, urlsplit

ACTIVITIES = [
    {"activity_id": "001", "title": "写作交流", "remaining": 8},
    {"activity_id": "002", "title": "数据练习", "remaining": 0},
    {"activity_id": "003", "title": "阅读讨论", "remaining": 5},
]
SNAPSHOT = "demo-v1"


class Handler(BaseHTTPRequestHandler):
    def log_message(self, format, *args):
        pass

    def send_data(self, status, data, raw=False):
        body = data if raw else json.dumps(data, ensure_ascii=False).encode("utf-8")
        self.send_response(status)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        try:
            self.wfile.write(body)
        except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError):
            pass

    def do_GET(self):
        parsed = urlsplit(self.path)
        if parsed.path == "/bad-json":
            self.send_data(200, b"{incomplete", raw=True)
            return
        if parsed.path == "/bad-schema":
            self.send_data(200, {"items": [{"activity_id": "001", "title": "写作交流", "remaining": "8"}], "next_cursor": None, "total": 1, "snapshot": SNAPSHOT})
            return
        if parsed.path == "/drafts":
            self.send_data(200, {"items": self.server.drafts})
            return
        if parsed.path != "/activities":
            self.send_data(404, {"error": "unknown endpoint"})
            return
        query = parse_qs(parsed.query, keep_blank_values=True)
        if any(key not in {"q", "cursor"} or len(values) != 1 for key, values in query.items()):
            self.send_data(400, {"error": "invalid query"})
            return
        q = query.get("q", [""])[0]
        cursor = query.get("cursor", [""])[0]
        selected = [dict(row) for row in ACTIVITIES if q in row["title"]]
        if cursor not in {"", "page2"} or (cursor == "page2" and len(selected) <= 2):
            self.send_data(400, {"error": "invalid cursor"})
            return
        start = 2 if cursor == "page2" else 0
        self.send_data(200, {"items": selected[start:start + 2], "next_cursor": "page2" if len(selected) > 2 and start == 0 else None, "total": len(selected), "snapshot": SNAPSHOT})

    def do_POST(self):
        if urlsplit(self.path).path != "/drafts":
            self.send_data(404, {"error": "unknown endpoint"})
            return
        if self.headers.get_content_type() != "application/json":
            self.send_data(415, {"error": "JSON required"})
            return
        try:
            size = int(self.headers.get("Content-Length", "0"))
            if size <= 0 or size > 8192:
                raise ValueError("invalid body size")
            data = json.loads(self.rfile.read(size).decode("utf-8"))
            if not isinstance(data, dict) or not isinstance(data.get("title"), str) or not data["title"].strip():
                raise ValueError("title required")
            delay = float(self.headers.get("X-Demo-Delay", "0"))
            if not 0 <= delay <= 1:
                raise ValueError("invalid demo delay")
        except (ValueError, UnicodeError):
            self.send_data(400, {"error": "invalid draft"})
            return
        draft = {"draft_id": "D" + str(len(self.server.drafts) + 1).zfill(3), "title": data["title"]}
        self.server.drafts.append(draft)
        time.sleep(delay)
        self.send_data(201, draft)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--port", type=int, default=8766)
    args = parser.parse_args()
    HTTPServer.allow_reuse_address = False
    server = HTTPServer(("127.0.0.1", args.port), Handler)
    server.drafts = []
    print(f"教学服务已启动：http://127.0.0.1:{server.server_port}", flush=True)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()
