"""ASR 语音转写路由:整段 POST + 流式 WebSocket 代理(讯飞 IAT)。"""
from __future__ import annotations

import asyncio
from uuid import UUID

from fastapi import Depends, HTTPException, Request, WebSocket

from ..auth import AuthConfig, verify_token


def register_asr_routes(app, *, require_user, auth_cfg: AuthConfig) -> None:
    @app.post("/v1/asr/transcribe", tags=["asr"])
    async def asr_transcribe(
        request: Request,
        user_id: UUID = Depends(require_user),
    ):
        """整段语音转写:body 为 raw PCM(16kHz/16bit/单声道/小端),返回 {"text"}。

        前端麦克风按钮录音(Web Audio 采样降到 16k)→ 本端点 → 讯飞 IAT websocket
        (core/asr_xfyun.py)。上限 60 秒(讯飞单会话限制);XFYUN_* env 未配 → 501。
        """
        from core.asr_xfyun import (
            MAX_PCM_BYTES,
            MAX_SECONDS,
            XfyunASRError,
            XfyunASRNotConfigured,
            transcribe,
        )

        pcm = await request.body()
        if not pcm:
            raise HTTPException(400, "empty audio")
        if len(pcm) > MAX_PCM_BYTES:
            raise HTTPException(413, f"音频超长:单次最多 {MAX_SECONDS} 秒")
        try:
            text = await transcribe(pcm)
        except XfyunASRNotConfigured as e:
            raise HTTPException(501, str(e))
        except XfyunASRError as e:
            raise HTTPException(502, str(e))
        return {"text": text}

    @app.websocket("/v1/asr/stream")
    async def asr_stream(ws: WebSocket):
        """流式语音转写代理:浏览器边录边推,服务端实时转发讯飞并回推 partial 全文。

        协议(浏览器 WebSocket 塞不了 Authorization header;token 走首消息而非 URL
        query,避免进 access log):
        1. client → 文本帧 `{"token": "<jwt>"}`(连上后 10s 内)
        2. client → 二进制帧 = PCM 分片(16kHz/16bit/mono/小端,实时节奏);
           文本帧(任意内容,约定 `{"type":"end"}`)= 说完
        3. server → `{"text","final"}` 增量全文(wpgs 动态修正已在服务端合并,前端
           直接整体替换显示);final=true 为最终结果,随后正常关闭
        错误:`{"error": msg}` 后关闭;鉴权失败 close code 4401。引擎提前收尾
        (静音超 10s / 60s 上限)同样推 final=true,前端应就此收尾。
        """
        from core.asr_xfyun import XfyunASRError, XfyunStream

        await ws.accept()
        try:
            first = await asyncio.wait_for(ws.receive_json(), timeout=10)
            verify_token(auth_cfg, str((first or {}).get("token") or ""))
        except HTTPException as e:
            try:
                await ws.send_json({"error": f"auth: {e.detail}"})
            except Exception:
                pass
            await ws.close(code=4401)
            return
        except Exception:  # 非 JSON 首帧 / 超时 / 断开
            try:
                await ws.close(code=4401)
            except Exception:
                pass
            return

        async def push(text: str, final: bool):
            await ws.send_json({"text": text, "final": final})

        stream = XfyunStream(on_text=push)
        try:
            try:
                await stream.start()  # 未配 XFYUN_* / 连不上讯飞 → 直接报错关闭
            except XfyunASRError as e:
                await ws.send_json({"error": str(e)})
                await ws.close()
                return
            while True:
                # 90s 上限兜底(讯飞 60s + 静音余量):客户端挂着不说话不结束也不无限占用
                msg = await asyncio.wait_for(ws.receive(), timeout=90)
                if msg.get("type") == "websocket.disconnect":
                    return  # 客户端取消/刷新:直接丢弃会话
                if msg.get("bytes"):
                    await stream.feed(msg["bytes"])
                elif msg.get("text") is not None:
                    break  # 唯一文本指令:end
            try:
                await stream.finish()  # 最终文本已由 recv loop 经 push(final=True) 推出
            except XfyunASRError as e:
                await ws.send_json({"error": str(e)})
            await ws.close()
        except Exception:
            # 客户端断开 / 发送失败等:静默收尾(recv loop 的错误已尽力 push 过)
            pass
        finally:
            await stream.close()
