"""
WebSocket 端點(階段 3c)。

連線方式(前端 JS):
    const ws = new WebSocket(`ws://localhost:8000/api/ws?token=${access_token}`);
    ws.onmessage = (e) => { ... };

server → client 事件型別:
    {type: "hello", user_id, online_count}                — 連線成功歡迎
    {type: "message_new", channel_id, message}            — 新訊息
    {type: "message_updated", channel_id, message}        — 訊息被編輯
    {type: "message_deleted", channel_id, message_id}     — 訊息被刪
    {type: "reaction_changed", channel_id, message}       — 反應變動(完整 message 為新狀態)
    {type: "read_receipt", channel_id, user_id, last_read_at}
                                                           — 有人讀到此刻
    {type: "channel_new", channel}                        — 新增頻道
    {type: "channel_updated", channel}                    — 頻道資訊變動
    {type: "channel_deleted", channel_id}                 — 頻道被刪
    {type: "ping"}                                        — 伺服器 keepalive

client → server 訊息(目前都不需要,只接收):
    {type: "ping"} → 回 {type: "pong"}
"""

import asyncio

from fastapi import APIRouter, Query, WebSocket, WebSocketDisconnect, status

from app.core.security import TokenError, decode_token
from app.core.ws_manager import manager


router = APIRouter()


@router.websocket("/api/ws")
async def websocket_endpoint(
    websocket: WebSocket,
    token: str = Query(..., description="access token,跟 HTTP Authorization header 用同一把"),
):
    """主要的 WebSocket 入口。

    Token 過期 / 無效 → close(1008 policy violation)
    """
    # 1. 驗 token
    try:
        user_id = decode_token(token, expected_type="access")
    except TokenError:
        await websocket.close(code=status.WS_1008_POLICY_VIOLATION, reason="invalid token")
        return

    # 2. 接受連線、登記
    await websocket.accept()
    await manager.connect(websocket, user_id)

    try:
        # 3. 寄歡迎訊息
        await websocket.send_json({
            "type": "hello",
            "user_id": str(user_id),
            "online_count": manager.connection_count(),
        })

        # 4. 持續監聽客戶端訊息(目前主要是處理 ping)
        while True:
            try:
                # receive_text 會在連線關閉時拋 WebSocketDisconnect
                data = await asyncio.wait_for(websocket.receive_text(), timeout=60.0)
                # 如果客戶端傳東西進來,簡單 echo / 處理 ping
                if data:
                    try:
                        import json
                        msg = json.loads(data)
                        if msg.get("type") == "ping":
                            await websocket.send_json({"type": "pong"})
                    except Exception:
                        pass   # 客戶端送了奇怪的東西,忽略
            except asyncio.TimeoutError:
                # 60 秒沒訊息,送 ping 確認還活著
                try:
                    await websocket.send_json({"type": "ping"})
                except Exception:
                    break
    except WebSocketDisconnect:
        pass
    finally:
        manager.disconnect(websocket, user_id)
