# -*- coding: utf-8 -*-
"""
Fast 模式流式输出测试 Demo

模拟 /api/interpret/click/tun?mode=fast 的完整 LLM 调用链路：
  1. 读取 .env 配置 → 获取 deepseek-v4-flash 模型
  2. 构造与 Fast 模式完全一致的 system + user prompt
  3. 流式调用模型，逐 token 打印并统计延迟

  用法：
  cd D:\\workspace\\py\\vent_deep_agent_1.4
  python tests/test_fast_stream.py

  也可以带参数指定巷道 ID（会走 MCP 真实查询）：
  python tests/test_fast_stream.py --tun-id 581 --tun-name "二水平中部辅运大巷"
"""

import argparse
import asyncio
import json
import os
import sys
import time
from pathlib import Path

# 确保项目根目录在 sys.path 中
sys.path.insert(0, str(Path(__file__).parent.parent))

from dotenv import dotenv_values
from openai import AsyncOpenAI


# ── 与 routes.py 完全一致的 Fast 模式系统提示词 ──
FAST_TUN_SYSTEM_PROMPT = """你是一名煤矿通风安全专家。请根据提供的巷道监测数据，直接生成标准化结构化解读报告。

## 报告格式（严格按此模板输出，不要添加额外标题或代码块）

**{巷道名称}**
- 当前风速：{数值} m/s
- 规程允许范围：{min} ~ {max} m/s
- 当前风量：{数值} m³/min{，计划风量：{数值} m³/min}
- 瓦斯浓度：{数值}%（限值 {限值}%）
- CO浓度：{数值} ppm（限值 {限值} ppm）
- 温度：{数值}℃（限值 {限值}℃）
- 设备状态：{状态}
- 结论：{正常/重大风险/高风险/中风险}。{详细异常分析}
- 依据：《煤矿安全规程》相关条款
- 建议：{针对性通风整改措施}

## 风险分级标准
- 风速偏低：实测＜0.5倍下限→重大；0.5倍≤实测＜0.8倍下限→高；0.8倍≤实测＜下限→中
- 风速偏高：实测≥1.8倍上限→重大；1.4倍≤实测＜1.8倍上限→高；上限≤实测＜1.4倍上限→中
- 风量超配：实测≥1.8倍计划→重大；1.5倍≤实测＜1.8倍计划→高；计划＜实测＜1.5倍计划→中
- 风量不足：实测＜0.75倍计划→重大；0.75倍≤实测＜0.9倍计划→高；0.9倍≤实测＜计划→中

## 巷道类型编码对照
- 0-回采工作面；1-掘进工作面；2-辅运巷；3-主运巷；4-硐室；5-联络巷；6-进风井；7-回风井；8-专用回风巷

## 规则
- 所有分析必须基于提供的数据，绝不编造
- 全程使用简体中文输出
- 严格按照模板格式输出，不要添加额外标题、总结、代码块、JSON
- 禁止暴露巷道ID，统一用巷道名称代替
- 数据缺失项标注"无数据"并说明影响
"""

# ── 模拟巷道数据（与 MCP 返回格式一致） ──
MOCK_TUN_DATA = {
    "tunId": "581",
    "tunnelName": "二水平中部辅运大巷",
    "needAirVolume": 1200,
    "usingType": 2,
    "usingTypeName": "辅运巷",
    "permissibleMin": 0.25,
    "permissibleMax": 6.0,
    "permissibleVelocity": {
        "fmin": 0.25,
        "fmax": 6.0,
    },
    "devices": [
        {
            "sourceType": "wind",
            "sourceName": "二水平中部辅运大巷测风站",
            "installPos": "二水平中部辅运大巷中部",
            "airVolume": 1150.5,
            "windSpeed": 1.85,
            "warnFlag": 0,
            "netStatus": 1,
            "deviceStatusName": "在线",
            "readTime": "2026-07-15 10:30:00",
            "m3": 1150.5,
            "va": 1.85,
            "alarmDescription": "",
            "alarmDescriptions": [],
        }
    ],
    # 模拟瓦斯传感器
    "gasSensor": {
        "concentration": 0.12,
        "limit": 0.8,
        "unit": "%",
    },
    # 模拟 CO 传感器
    "coSensor": {
        "concentration": 5,
        "limit": 24,
        "unit": "ppm",
    },
    # 模拟温度传感器
    "tempSensor": {
        "value": 24.5,
        "limit": 30,
        "unit": "℃",
    },
}


def load_env():
    """读取 .env 配置（与 _get_chat_model 逻辑一致）"""
    env_file = Path(__file__).parent.parent / ".env"
    _cfg = dotenv_values(str(env_file))

    model_str = _cfg.get("SUMMARY_MODEL", "") or _cfg.get("DEEPAGENT_MODEL", "deepseek-v4-flash")
    base_url = _cfg.get("OPENAI_BASE_URL", "https://api.deepseek.com").strip().strip('"').strip("'")
    api_key = _cfg.get("OPENAI_API_KEY", "").strip().strip('"').strip("'")

    return model_str, base_url, api_key


def build_prompt(tun_name: str, data: dict) -> list[dict]:
    """构造与 routes.py _fast_tun_sse_generator 完全一致的 messages"""
    data_str = json.dumps(data, ensure_ascii=False, indent=2)
    return [
        {"role": "system", "content": FAST_TUN_SYSTEM_PROMPT},
        {"role": "user", "content": f"请根据以下巷道监测数据生成标准化解读报告：\n\n巷道名称：{tun_name}\n\n数据：\n{data_str}"},
    ]


async def stream_model(client: AsyncOpenAI, model: str, messages: list[dict]):
    """流式调用模型，逐 token 输出并计时"""
    t0 = time.perf_counter()
    first_token_time = None
    token_count = 0
    full_text = ""

    print("─" * 60)
    print(f"模型: {model}")
    print(f"System prompt 长度: {len(messages[0]['content'])} 字符")
    print(f"User prompt 长度:   {len(messages[1]['content'])} 字符")
    print("─" * 60)
    print("\n[LLM 流式输出开始]\n")

    stream = await client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,
        stream=True,
        stream_options={"include_usage": True},
    )

    async for chunk in stream:
        if chunk.choices and chunk.choices[0].delta.content:
            content = chunk.choices[0].delta.content
            if first_token_time is None:
                first_token_time = time.perf_counter()
                ttft = first_token_time - t0
                print(f"\n{'─' * 40}")
                print(f"⏱ 首 token 延迟 (TTFT): {ttft:.2f}s")
                print(f"{'─' * 40}\n")

            token_count += 1
            full_text += content
            # 实时逐 token 打印（不换行）
            print(content, end="", flush=True)

    t_total = time.perf_counter() - t0
    ttft = (first_token_time - t0) if first_token_time else t_total

    print("\n")
    print("─" * 60)
    print(f"📊 统计:")
    print(f"   首 token 延迟: {ttft:.2f}s")
    print(f"   总耗时:        {t_total:.2f}s")
    print(f"   总 token 数:   {token_count}")
    print(f"   平均速度:      {token_count / max(t_total - ttft, 0.01):.1f} tok/s")
    print(f"   输出长度:      {len(full_text)} 字符")
    print("─" * 60)

    return full_text


async def main():
    parser = argparse.ArgumentParser(description="Fast 模式流式输出测试")
    parser.add_argument("--tun-id", default="581", help="巷道 ID（默认 581，使用 mock 数据）")
    parser.add_argument("--tun-name", default="二水平中部辅运大巷", help="巷道名称")
    parser.add_argument("--real-data", action="store_true", help="通过 MCP 获取真实数据（需要 MCP 服务可用）")
    parser.add_argument("--model", default=None, help="覆盖模型名称（默认从 .env 读取 SUMMARY_MODEL）")
    args = parser.parse_args()

    # 1. 加载环境配置
    model_str, base_url, api_key = load_env()
    if args.model:
        model_str = args.model

    print(f"🔧 API Base: {base_url}")
    print(f"🔧 模型:     {model_str}")

    if not api_key:
        print("❌ 未找到 OPENAI_API_KEY，请检查 .env 文件")
        return

    # 2. 获取数据
    if args.real_data:
        print("🌐 通过 MCP 获取真实数据...")
        t_mcp_start = time.perf_counter()
        from tools.vent_tools import query_tun_data_by_id
        raw = await query_tun_data_by_id(args.tun_id)
        data = json.loads(raw) if isinstance(raw, str) else raw
        t_mcp = time.perf_counter() - t_mcp_start
        print(f"   MCP 耗时: {t_mcp:.2f}s")
    else:
        print("📦 使用 Mock 数据（跳过 MCP 调用）")
        data = MOCK_TUN_DATA

    # 3. 构造提示词
    messages = build_prompt(args.tun_name, data)

    # 4. 初始化 OpenAI 客户端
    client = AsyncOpenAI(
        api_key=api_key,
        base_url=base_url + "/v1" if not base_url.endswith("/v1") else base_url,
        timeout=180.0,
        max_retries=2,
    )

    # 5. 流式调用
    full_text = await stream_model(client, model_str, messages)

    # 6. 可选：保存结果
    output_file = Path(__file__).parent / "fast_test_output.txt"
    output_file.write_text(full_text, encoding="utf-8")
    print(f"\n💾 完整输出已保存到: {output_file}")


if __name__ == "__main__":
    asyncio.run(main())
