"""狼人杀 live 服务(多人房间版):密钥建房 → 邀请码加入 → 多真人 + AI 补位,每房一局隔离。

  PYTHONPATH=src python -m genesis.server.werewolf_server            # 真 AI(MiniMax)
  GENESIS_WW_BRAIN=random PYTHONPATH=src python -m genesis.server.werewolf_server   # 随机 bot,免费快测

身份:create/join 返回 token(浏览器存 localStorage),后续请求带 token → 定位房间+座位。
房间接口:/api/room/{create,join,start,leave,close,continue};对局接口按 token:/api/{state,action,spoken,voice}。
语音:/api/tts(text+seat→mp3,法官 seat=0)、/api/asr(16k PCM→文本)、ws://host:port+1 流式 ASR。静态走 repo 根。
"""

from __future__ import annotations

import argparse
import json
import os
import threading
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from urllib.parse import parse_qs, urlparse

from genesis.obs.logging_setup import get_logger
from genesis.werewolf.rooms import RoomManager, tts_mp3

logger = get_logger("server.werewolf")
ROOT = Path(__file__).resolve().parents[3]
_BRAIN = os.environ.get("GENESIS_WW_BRAIN", "llm")
_MGR = RoomManager(brain=_BRAIN)


class Handler(SimpleHTTPRequestHandler):
    def __init__(self, *a, **kw):
        super().__init__(*a, directory=str(ROOT), **kw)

    # ── 小工具 ──
    def _json(self, obj, code: int = 200) -> None:
        b = json.dumps(obj, ensure_ascii=False).encode("utf-8")
        self.send_response(code)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(b)))
        self.send_header("Cache-Control", "no-store")
        self.end_headers()
        self.wfile.write(b)

    def _body(self) -> dict:
        n = int(self.headers.get("Content-Length", 0))
        try:
            return json.loads(self.rfile.read(n) if n else b"{}")
        except Exception:
            return {}

    def _audio(self, mp3: bytes) -> None:
        self.send_response(200)
        self.send_header("Content-Type", "audio/mpeg")
        self.send_header("Content-Length", str(len(mp3)))
        self.send_header("Cache-Control", "no-store")
        self.end_headers()
        self.wfile.write(mp3)

    # ── GET ──
    def do_GET(self):
        u = urlparse(self.path)
        if u.path == "/api/state":
            token = (parse_qs(u.query).get("token") or [""])[0]
            room = _MGR.room_of(token)
            self._json(room.state_for(token) if room else {"phase": "none"})
            return
        super().do_GET()

    # ── POST ──
    def do_POST(self):
        p = urlparse(self.path).path
        b = self._body() if p != "/api/asr" else {}
        token = b.get("token", "")

        if p == "/api/room/create":
            out = _MGR.create(b.get("key", ""), b.get("nickname", ""))
            self._json(out or {"error": "密钥错误,无权创建房间"}, 200 if out else 403)
            return
        if p == "/api/room/join":
            self._json(_MGR.join(b.get("code", ""), b.get("nickname", "")))
            return

        room = _MGR.room_of(token)
        if p in ("/api/room/start", "/api/room/leave", "/api/room/close", "/api/room/kick",
                 "/api/room/continue", "/api/action", "/api/spoken", "/api/voice"):
            if not room:
                self._json({"ok": False, "error": "房间不存在(可能已解散)"}, 404); return
            if p == "/api/room/start":
                self._json({"ok": room.start_game(token)})
            elif p == "/api/room/leave":
                room.leave(token); self._json({"ok": True})
            elif p == "/api/room/kick":
                self._json({"ok": room.kick(token, b.get("pid", ""))})
            elif p == "/api/room/close":
                self._json({"ok": room.close(token)})
            elif p == "/api/room/continue":
                self._json({"ok": room.continue_game(token, bool(b.get("cont")))})
            elif p == "/api/action":
                self._json({"ok": room.submit(token, b.get("answer", ""))})
            elif p == "/api/spoken":
                room.mark_spoken(b.get("seat"), b.get("text")); self._json({"ok": True})
            elif p == "/api/voice":
                room.voice_on = bool(b.get("on")); self._json({"ok": True, "voice": room.voice_on})
            return

        if p == "/api/tts":
            text = (b.get("text") or "").strip()[:400]
            seat = int(b.get("seat", 0))
            if not text:
                self._json({"ok": False}, 400); return
            from genesis.voice.volc_tts import voice_for_seat
            try:
                self._audio(tts_mp3(text, voice_for_seat(seat)))
            except Exception as e:
                logger.warning("TTS 失败:%s", str(e)[:160]); self._json({"ok": False, "error": str(e)[:200]}, 500)
            return
        if p == "/api/asr":
            n = int(self.headers.get("Content-Length", 0))
            pcm = self.rfile.read(n) if n else b""
            if not pcm:
                self._json({"ok": False, "error": "empty"}, 400); return
            from genesis.voice.volc_asr import recognize
            try:
                self._json({"ok": True, "text": recognize(pcm, rate=16000)})
            except Exception as e:
                logger.warning("ASR 失败:%s", str(e)[:160]); self._json({"ok": False, "error": str(e)[:200]}, 500)
            return
        self.send_error(404)

    def log_message(self, *a):
        pass


def _start_asr_ws(host: str, port: int) -> None:
    """流式 ASR 的 WebSocket 服务(浏览器边说边送 PCM,火山中间识别结果实时回传)。独立端口,守护线程 asyncio。"""
    import asyncio
    try:
        import websockets
    except Exception as e:
        logger.warning("未装 websockets,流式 ASR 不可用:%s", e); return
    from genesis.voice.volc_asr import relay

    async def handler(ws, *args):
        try:
            await relay(ws)
        except Exception as e:
            logger.warning("ASR WS 桥异常:%s", str(e)[:160])

    async def serve():
        async with websockets.serve(handler, host, port, max_size=None):
            await asyncio.Future()

    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        loop.run_until_complete(serve())
    except Exception as e:
        logger.warning("ASR WS 服务起不来(端口 %d):%s", port, str(e)[:160])


def _cleanup_loop() -> None:
    """周期清理:CLOSED / 闲置(空房 5min,否则 1h)房间销毁 + 掉线补位检查。"""
    import time
    while True:
        try:
            _MGR.cleanup()
        except Exception:
            logger.exception("房间清理异常")
        time.sleep(15)


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--host", default=os.environ.get("GENESIS_WW_HOST", "127.0.0.1"))
    ap.add_argument("--port", type=int, default=int(os.environ.get("GENESIS_WW_PORT", "8132")))
    args = ap.parse_args()
    asr_port = args.port + 1
    threading.Thread(target=_start_asr_ws, args=(args.host, asr_port), daemon=True, name="asr-ws").start()
    threading.Thread(target=_cleanup_loop, daemon=True, name="room-cleanup").start()
    logger.info("🐺 狼人杀(多人房)live:http://%s:%d/src/genesis/viz/web/werewolf.html (brain=%s)",
                args.host, args.port, _BRAIN)
    logger.info("🎤 流式 ASR WS:ws://%s:%d", args.host, asr_port)
    ThreadingHTTPServer((args.host, args.port), Handler).serve_forever()


if __name__ == "__main__":
    main()
