"""
AI 智能助手路由 —— /api/chat
- POST /stream: 流式对话（SSE）
- GET /mcp/tools: 返回已注册的 MCP 工具清单
- POST /mcp/execute: 执行指定 MCP 工具
- POST /action-card/callback: Action Card 确认/取消回调
"""
from __future__ import annotations

import asyncio
import json
import re
import uuid
from typing import Any

from fastapi import APIRouter, Body, Depends
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_current_user
from app.db.database import get_db
from app.schemas.auth import CurrentUserPayload
from app.schemas.action_card import ActionCardCallback
from app.schemas.response import ok

# 导入 MCP 注册中心（导入 tools 模块会触发 @register_tool 装饰器注册）
from app.mcp.registry import get_tools_manifest, execute_tool, MCPToolResult
import app.mcp.tools  # noqa: F401 — 触发工具注册
from app.core.action_card_queue import pop_cards

# 导入 service 层（用于 action card 回调真正执行）
from app.services import customer_service, order_service
from app.schemas.crm import CustomerCreate
from app.schemas.order import OrderCreate

router = APIRouter(prefix="/chat", tags=["AI 智能助手"])


class ChatRequest(BaseModel):
    message: str
    conversation_id: str = ""  # Dify 会话 ID，空则新建会话


# ── Dify 可用性探测 ──────────────────────────────────────
async def _check_dify_available() -> bool:
    """检测 Dify API 是否可用（2s 超时），探测根路径即可"""
    from app.core.config import settings
    if not settings.DIFY_API_BASE_URL or not settings.DIFY_API_KEY:
        return False
    try:
        import httpx
        async with httpx.AsyncClient(timeout=2.0) as client:
            resp = await client.get(settings.DIFY_API_BASE_URL)
            return resp.status_code < 500  # 2xx/3xx/4xx 都说明服务存活
    except Exception:
        return False


# ── <think> 标签流式过滤器 ────────────────────────────────
class _ThinkFilter:
    """
    状态机：跨多个 SSE chunk 过滤 <think>...</think>。
    流式场景下标签可能被拆分到不同 chunk 中到达。
    """
    def __init__(self):
        self._inside = False    # 是否正处于 <think> 块内
        self._buf = ""          # 缓冲可能是不完整标签的尾部

    def feed(self, text: str) -> str:
        """输入一段文本，返回过滤后应输出的部分"""
        out = []
        self._buf += text
        while self._buf:
            if self._inside:
                # 在 think 块内，找 </think> 结束标签
                end_pos = self._buf.lower().find("</think>")
                if end_pos >= 0:
                    # 找到了，跳过 </think> 本身（8 字符）
                    self._buf = self._buf[end_pos + 8:]
                    self._inside = False
                elif len(self._buf) > 8 and "</think>" not in self._buf.lower():
                    # 确认不含部分标签，整块吞掉
                    # 保留最后 8 字符以防 </think> 跨 chunk
                    self._buf = self._buf[-8:]
                    break
                else:
                    # 缓冲区太短或可能含部分标签，等下一个 chunk
                    break
            else:
                # 不在 think 块内，找 <think> 开始标签
                start_pos = self._buf.lower().find("<think>")
                if start_pos >= 0:
                    # <think> 之前的内容输出
                    out.append(self._buf[:start_pos])
                    # 跳过 <think> 本身（7 字符），进入 inside 状态
                    self._buf = self._buf[start_pos + 7:]
                    self._inside = True
                elif len(self._buf) > 7:
                    # 安全输出，保留最后 7 字符防跨 chunk 的 <think>
                    out.append(self._buf[:-7])
                    self._buf = self._buf[-7:]
                    break
                else:
                    break
        return "".join(out)


# ── Dify 流式转发生成器 ──────────────────────────────────
async def _dify_stream_generator(message: str, user_id: str, conversation_id: str = ""):
    """将用户消息转发到 Dify chat-messages API，流式返回"""
    from app.core.config import settings
    import httpx

    url = f"{settings.DIFY_API_BASE_URL}/v1/chat-messages"
    headers = {
        "Authorization": f"Bearer {settings.DIFY_API_KEY}",
        "Content-Type": "application/json",
    }
    payload = {
        "inputs": {},
        "query": message,
        "response_mode": "streaming",
        "conversation_id": conversation_id,
        "user": user_id,
    }

    try:
        async with httpx.AsyncClient(timeout=settings.DIFY_TIMEOUT_MS / 1000) as client:
            async with client.stream("POST", url, json=payload, headers=headers) as resp:
                if resp.status_code != 200:
                    error_text = ""
                    async for chunk in resp.aiter_text():
                        error_text += chunk
                    yield f"data: {json.dumps({'type': 'text', 'content': f'Dify 返回错误 ({resp.status_code}): {error_text[:200]}'}, ensure_ascii=False)}\n\n"
                    return

                # 用原始 text 做 SSE 解析，因为 aiter_lines 可能丢失 event 行
                buf = ""
                think_filter = _ThinkFilter()  # 跨 chunk 过滤 <think> 标签
                message_ended = False  # 标记文本流是否结束
                async for chunk in resp.aiter_text():
                    buf += chunk
                    # 按双换行分割完整 SSE 事件
                    while "\n\n" in buf:
                        event_block, buf = buf.split("\n\n", 1)
                        data_line = ""
                        for line in event_block.split("\n"):
                            if line.startswith("data: "):
                                data_line = line[6:]
                        if not data_line or data_line.strip() == "[DONE]":
                            continue
                        try:
                            event = json.loads(data_line)
                        except json.JSONDecodeError:
                            continue

                        event_type = event.get("event", "")

                        # ── Chatflow 模式：message 事件
                        if event_type == "message" and not message_ended:
                            answer = think_filter.feed(event.get("answer", ""))
                            if answer:
                                yield f"data: {json.dumps({'type': 'text', 'content': answer}, ensure_ascii=False)}\n\n"

                        # ── Agent 模式：agent_message 事件（最终文本回复）
                        elif event_type == "agent_message" and not message_ended:
                            answer = think_filter.feed(event.get("answer", ""))
                            if answer:
                                yield f"data: {json.dumps({'type': 'text', 'content': answer}, ensure_ascii=False)}\n\n"

                        # ── Agent 模式：agent_thought — 提取工具调用的 action_card
                        elif event_type == "agent_thought":
                            observation = event.get("observation", "")
                            if observation:
                                try:
                                    obs_data = json.loads(observation)
                                    for tool_key, tool_val in (obs_data.items() if isinstance(obs_data, dict) else []):
                                        inner = tool_val
                                        if isinstance(tool_val, str):
                                            try:
                                                inner = json.loads(tool_val)
                                            except json.JSONDecodeError:
                                                continue
                                        if not isinstance(inner, dict):
                                            continue
                                        result_data = inner.get("data", inner)
                                        if isinstance(result_data, dict):
                                            resp_type = result_data.get("response_type", "")
                                            result_inner = result_data.get("result", {})
                                            if resp_type == "action_card" and isinstance(result_inner, dict):
                                                yield f"data: {json.dumps({'type': 'action_card', 'content': result_inner.get('summary', '请确认操作'), 'card': result_inner}, ensure_ascii=False)}\n\n"
                                                print(f"[Dify SSE] ✅ 注入 action_card: {result_inner.get('card_type', 'unknown')}")
                                except (json.JSONDecodeError, TypeError) as e:
                                    print(f"[Dify SSE] observation 解析失败: {e}")

                        # ── message_end / 结束 — 不再 break，设置标记继续处理剩余事件
                        elif event_type in ("message_end", "agent_message_end"):
                            conv_id = event.get("conversation_id", "")
                            if conv_id:
                                yield f"data: {json.dumps({'type': 'conversation_id', 'conversation_id': conv_id}, ensure_ascii=False)}\n\n"
                            # 检查工具端点是否推送了 action_card
                            cards = await pop_cards()
                            for card in cards:
                                yield f"data: {json.dumps({'type': 'action_card', 'content': card.get('summary', '请确认操作'), 'card': card}, ensure_ascii=False)}\n\n"
                                print(f"[Dify SSE] ✅ 注入 action_card: {card.get('card_type', 'unknown')}")
                            message_ended = True

                        # ── 错误
                        elif event_type == "error":
                            err_msg = event.get("message", "未知错误")
                            yield f"data: {json.dumps({'type': 'text', 'content': f'\\n⚠️ Dify 错误: {err_msg}'}, ensure_ascii=False)}\n\n"
                            return  # 出错直接返回

                # ── 流结束后，刷新 buf 中可能剩余的事件（重要：agent_thought 可能在 message_end 之后）
                if buf.strip():
                    for remaining_block in buf.split("\n\n"):
                        remaining_block = remaining_block.strip()
                        if not remaining_block:
                            continue
                        data_line = ""
                        for line in remaining_block.split("\n"):
                            if line.startswith("data: "):
                                data_line = line[6:]
                        if not data_line or data_line.strip() == "[DONE]":
                            continue
                        try:
                            event = json.loads(data_line)
                            if event.get("event") == "agent_thought":
                                observation = event.get("observation", "")
                                if observation:
                                    obs_data = json.loads(observation)
                                    for tool_key, tool_val in (obs_data.items() if isinstance(obs_data, dict) else []):
                                        inner = tool_val
                                        if isinstance(tool_val, str):
                                            try:
                                                inner = json.loads(tool_val)
                                            except json.JSONDecodeError:
                                                continue
                                        if not isinstance(inner, dict):
                                            continue
                                        result_data = inner.get("data", inner)
                                        if isinstance(result_data, dict) and result_data.get("response_type") == "action_card":
                                            result_inner = result_data.get("result", {})
                                            if isinstance(result_inner, dict):
                                                yield f"data: {json.dumps({'type': 'action_card', 'content': result_inner.get('summary', '请确认操作'), 'card': result_inner}, ensure_ascii=False)}\n\n"
                                                print(f"[Dify SSE] ✅ 注入 action_card (post-flush): {result_inner.get('card_type', 'unknown')}")
                        except (json.JSONDecodeError, TypeError):
                            pass

    except httpx.TimeoutException:
        yield f"data: {json.dumps({'type': 'text', 'content': '\n⚠️ Dify 响应超时，请稍后重试'}, ensure_ascii=False)}\n\n"
    except Exception as e:
        print(f"[Dify SSE] 异常: {e!s}")
        yield f"data: {json.dumps({'type': 'text', 'content': f'\n⚠️ Dify 连接失败: {e!s}'}, ensure_ascii=False)}\n\n"


# ── Mock 流式生成器（降级用） ────────────────────────────
async def _mock_stream_generator(message: str, user_id: str):
    """Dify 不可用时的降级 mock 回复"""
    await asyncio.sleep(0.3)

    notice = "⚠️ AI 引擎暂不可用，当前为本地模拟回复。\n\n"
    for char in notice:
        yield f"data: {json.dumps({'type': 'text', 'content': char}, ensure_ascii=False)}\n\n"
        await asyncio.sleep(0.02)

    reply = f"收到指令，正在解析：【{message}】...\n来自用户: {user_id}\n"
    for char in reply:
        yield f"data: {json.dumps({'type': 'text', 'content': char}, ensure_ascii=False)}\n\n"
        await asyncio.sleep(0.04)


# ── 1. POST /api/chat/stream — 流式对话（含意图网关） ────
@router.post("/stream", summary="流式对话接口（SSE）")
async def chat_stream(
    body: ChatRequest,
    current_user: CurrentUserPayload = Depends(get_current_user),
):
    user_id = str(current_user.user_id)
    print(f"[AI Chat] user_id={user_id}, scope={current_user.data_scope}, msg='{body.message}'")

    # Step 1: 意图分类（4060 Qwen3.5-4B，5s 超时）
    from app.services.intent_service import classify_intent
    intent_result = await classify_intent(body.message)
    intent = intent_result.get("intent", "general")
    route = intent_result.get("route", "dify_agent")
    print(f"[AI Chat] 意图网关: intent={intent}, route={route}")

    # Step 2: 根据意图路由到不同后端
    dify_available = await _check_dify_available()

    if route == "dify_agent" and dify_available:
        generator = _dify_stream_generator(body.message, user_id, body.conversation_id)
    elif route == "dify_workflow_report" and dify_available:
        # 周报走 Workflow，非流式，包装成 SSE
        generator = _report_workflow_generator(body.message, user_id)
    elif dify_available:
        generator = _dify_stream_generator(body.message, user_id, body.conversation_id)
    else:
        generator = _mock_stream_generator(body.message, user_id)

    return StreamingResponse(generator, media_type="text/event-stream")


# ── 周报 Workflow 生成器 ───────────────────────────
async def _report_workflow_generator(message: str, user_id: str):
    """调用周报 Workflow 并把结果包装成 SSE 流"""
    from app.core.config import settings
    import httpx

    yield f"data: {json.dumps({'type': 'text', 'content': '📊 正在调用 AI 生成周报，请稍候...\n\n'}, ensure_ascii=False)}\n\n"

    if not settings.DIFY_WORKFLOW_REPORT_KEY:
        yield f"data: {json.dumps({'type': 'text', 'content': '⚠️ 周报 Workflow 未配置，请联系管理员。'}, ensure_ascii=False)}\n\n"
        return

    url = f"{settings.DIFY_API_BASE_URL}/v1/workflows/run"
    headers = {
        "Authorization": f"Bearer {settings.DIFY_WORKFLOW_REPORT_KEY}",
        "Content-Type": "application/json",
    }
    payload = {
        "inputs": {"user_id": user_id, "request": message},
        "response_mode": "blocking",
        "user": user_id,
    }

    try:
        async with httpx.AsyncClient(timeout=60) as client:
            resp = await client.post(url, json=payload, headers=headers)
            if resp.status_code == 200:
                data = resp.json()
                output_text = data.get("data", {}).get("outputs", {}).get("text", "周报生成完成，但未获取到内容。")
                yield f"data: {json.dumps({'type': 'text', 'content': output_text}, ensure_ascii=False)}\n\n"
            else:
                yield f"data: {json.dumps({'type': 'text', 'content': f'⚠️ 周报 Workflow 返回错误 ({resp.status_code})'}, ensure_ascii=False)}\n\n"
    except Exception as e:
        yield f"data: {json.dumps({'type': 'text', 'content': f'⚠️ 周报生成失败: {e}'}, ensure_ascii=False)}\n\n"


# ── 2. GET /api/chat/mcp/tools — 工具清单 ───────────────
@router.get("/mcp/tools", summary="获取已注册 MCP 工具列表（供 Dify 配置）")
async def list_mcp_tools(
    _: CurrentUserPayload = Depends(get_current_user),
) -> dict:
    return ok(data=get_tools_manifest())


# ── 3. POST /api/chat/mcp/execute — 执行工具 ────────────
class MCPExecuteRequest(BaseModel):
    tool_name: str
    params: dict[str, Any] = {}


@router.post("/mcp/execute", summary="执行指定 MCP 工具（Dify function_call 回调）")
async def execute_mcp_tool(
    body: MCPExecuteRequest,
    db: AsyncSession = Depends(get_db),
    current_user: CurrentUserPayload = Depends(get_current_user),
) -> dict:
    print(f"[MCP Execute] user_id={current_user.user_id}, tool={body.tool_name}, params={body.params}")
    result: MCPToolResult = await execute_tool(body.tool_name, db, current_user, body.params)
    return ok(data={
        "success": result.success,
        "response_type": result.response_type,
        "data": result.data,
        "message": result.message,
    })


# ── 4. POST /api/chat/action-card/callback — 卡片回调 ──
@router.post("/action-card/callback", summary="Action Card 确认/取消回调")
async def action_card_callback(
    body: ActionCardCallback,
    db: AsyncSession = Depends(get_db),
    current_user: CurrentUserPayload = Depends(get_current_user),
) -> dict:
    print(f"[Action Card] user_id={current_user.user_id}, type={body.card_type}, action={body.action_key}")

    if body.action_key == "cancel":
        return ok(message="操作已取消")

    # 根据 card_type 路由到具体 service
    if body.card_type == "create_customer":
        p = body.params
        result = await customer_service.create_customer(
            db, current_user,
            CustomerCreate(
                name=p.get("name", ""),
                level=p.get("level", "C"),
                contact=p.get("contact"),
                phone=p.get("phone"),
            ),
        )
        return ok(data=result.model_dump(mode="json"), message="客户创建成功")

    elif body.card_type == "create_order":
        from datetime import date
        p = body.params
        items_raw = p.get("items", [])
        from app.schemas.order import OrderItemCreate
        items = [
            OrderItemCreate(
                sku_id=uuid.UUID(i["sku_id"]),
                qty=i["qty"],
                unit_price=i["unit_price"],
            )
            for i in items_raw
        ]
        result = await order_service.create_order(
            db, current_user,
            OrderCreate(
                customer_id=uuid.UUID(p["customer_id"]),
                items=items,
                remark=p.get("remark"),
                order_date=date.today(),
            ),
        )
        return ok(data=result.model_dump(mode="json"), message=f"订单 {result.order_no} 创建成功")

    return ok(message=f"未知的卡片类型: {body.card_type}")


# ── 5. GET /api/chat/history — 对话历史 ──────────────────
@router.get("/history", summary="获取当前用户的 AI 对话历史")
async def get_chat_history(
    limit: int = 50,
    db: AsyncSession = Depends(get_db),
    current_user: CurrentUserPayload = Depends(get_current_user),
) -> dict:
    from app.services import chat_service
    history = await chat_service.load_history(db, current_user.user_id, limit=limit)
    return ok(data=history)
