import os
import re
import time
from typing import Any


async def send_reply_flow(client, original_msg: dict, reply_content: str):
    """
    发送回复消息（从 websocket_client.py 拆出）。

    Args:
        original_msg: 收到的原始消息字典
        reply_content: 回复内容（文本或本地文件路径/http地址）
    """
    trace_id = original_msg.get("_trace_id", "")
    if not client.websocket:
        client._activity_log(
            "send_reply_skipped",
            trace_id=trace_id,
            reason="websocket_not_connected",
            acc_id=original_msg.get("acc_id", ""),
            customer_id=original_msg.get("from_id", ""),
        )
        return

    reply_content = colloquialize_outbound_reply(reply_content)
    reply_content = await ai_generate_outbound_reply(
        client=client,
        original_msg=original_msg,
        reply_content=str(reply_content or ""),
    )

    # 同一客户外发限流：N 秒内最多 1 条
    try:
        from config.config import OUTBOUND_PER_CUSTOMER_COOLDOWN_SECONDS
        cooldown = max(0, int(OUTBOUND_PER_CUSTOMER_COOLDOWN_SECONDS))
    except Exception:
        cooldown = 5
    if cooldown > 0:
        ckey = f"{original_msg.get('acc_id', '')}:{original_msg.get('from_id', '')}"
        now_mono = time.monotonic()
        last = client._last_reply_sent_at.get(ckey, 0.0)
        if (now_mono - last) < cooldown:
            client._activity_log(
                "send_reply_throttled",
                trace_id=trace_id,
                key=ckey,
                cooldown_s=cooldown,
                msg=str(reply_content),
            )
            return
        client._last_reply_sent_at[ckey] = now_mono

    shop_id = original_msg.get("acc_id", "")

    # 根据轻简API文档：
    # from_id = 客户ID（收消息方）
    # cy_id   = 非群聊时与 from_id 相同
    customer_id = original_msg.get("from_id", "")
    customer_name = original_msg.get("from_name", "")

    allow_send, checked_reply, guard_reason = await ai_guard_outbound_reply(
        client=client,
        original_msg=original_msg,
        reply_content=str(reply_content),
    )
    client._activity_log(
        "reply_guard_decision",
        trace_id=trace_id,
        acc_id=shop_id,
        customer_id=customer_id,
        result="ok" if allow_send else "blocked",
        reason=guard_reason,
        original_reply=str(reply_content),
        final_reply=str(checked_reply or ""),
    )
    if not allow_send:
        return

    reply_content = checked_reply or str(reply_content)
    pass_send, _ = client._outbound_arbiter(
        original_msg=original_msg,
        reply_content=reply_content,
        trace_id=trace_id,
    )
    if not pass_send:
        return

    reply = {
        "msg_id": "",
        "acc_id": shop_id,
        "msg": reply_content,
        "from_id": customer_id,
        "from_name": customer_name,
        "cy_id": customer_id,
        "acc_type": original_msg.get("acc_type", ""),
        "msg_type": 0,
        "cy_name": customer_name,
    }
    client._log_outbound_once(original_msg, str(reply_content))
    client._activity_log(
        "send_reply_attempt",
        trace_id=trace_id,
        acc_id=shop_id,
        customer_id=customer_id,
        msg=str(reply_content),
    )
    reply["_trace_id"] = trace_id
    await client.send_message(reply)


async def ai_generate_outbound_reply(client, original_msg: dict, reply_content: str) -> str:
    """
    强制全量 AI 出站生成层：
    - 所有普通文本外发先由 AI 生成最终话术；
    - 控制命令/纯链接/转接指令直接绕过。
    """
    text = (reply_content or "").strip()
    if not text:
        return text
    if text.startswith("话术|") or "[转移会话]" in text or "TRANSFER_REQUESTED" in text:
        return text
    if re.fullmatch(r"https?://\S+", text):
        return text
    if not client._force_ai_generate_reply or not client.enable_agent or not client.agent or not client.AgentDeps:
        return text
    try:
        deps = client.AgentDeps(
            msg_id=str(original_msg.get("msg_id", "") or "outbound_generate"),
            acc_id=str(original_msg.get("acc_id", "") or ""),
            from_id=str(original_msg.get("from_id", "") or ""),
            platform=str(original_msg.get("acc_type", "") or ""),
        )
        customer_msg = client.to_chinese(str(original_msg.get("msg", "") or ""))
        prompt = (
            "你是淘宝客服外发文案生成器。请根据“回复意图草稿”生成最终发给客户的话。\n"
            "要求：\n"
            "1) 保留原意，不新增价格/承诺/流程；\n"
            "2) 自然像真人聊天，不用固定模板句；\n"
            "3) 1-2句；\n"
            "4) 只输出最终回复文本。\n\n"
            f"客户原话: {customer_msg}\n"
            f"回复意图草稿: {text}\n"
        )
        result = await client.agent.agent_natural_reply.run(prompt, deps=deps, message_history=[])
        out = str(getattr(result, "output", "") or "").strip()
        if not out:
            return text
        if out.startswith("话术|") or "[转移会话]" in out:
            return text
        client._activity_log(
            "ai_generate_reply",
            acc_id=str(original_msg.get("acc_id", "") or ""),
            customer_id=str(original_msg.get("from_id", "") or ""),
            draft=text[:160],
            generated=out[:160],
        )
        return out
    except Exception as e:
        client._activity_log(
            "ai_generate_reply_error",
            acc_id=str(original_msg.get("acc_id", "") or ""),
            customer_id=str(original_msg.get("from_id", "") or ""),
            error=str(e),
        )
        return text


def colloquialize_outbound_reply(text: Any) -> Any:
    """统一外发口语化处理，避免机械话术。"""
    if not isinstance(text, str):
        return text
    raw = text.strip()
    if not raw:
        return text
    # 控制指令/转接命令不得改写
    if raw.startswith("话术|") or "[转移会话]" in raw:
        return text
    # 纯链接不改
    if re.fullmatch(r"https?://\S+", raw):
        return text

    out = raw
    replacements = {
        "我这边": "我这边",
        "请您": "你",
        "您好": "你好",
        "稍后": "一会儿",
        "可以的话": "可以的话",
        "请稍等": "稍等哈",
        "先不乱报价": "先不急着给你乱报",
        "建议转人工评估更稳": "建议转人工看会更稳",
        "统一报价": "一起报价",
        "马上安排": "马上给你安排",
        "确认我就安排": "你点头我就开做",
        "收到，我看看哈": "收到，我先看下",
        "收到，我找找刚才那几张": "收到，我把刚才那几张一起看下",
        "这组图我这边暂时识别不稳定": "这组图我这边识别得不太稳",
        "这组图我这边暂时识别异常": "这组图我这边刚才识别有点异常",
        "你可以换一张更清晰的，我再给你准报价。": "你换张更清晰的发我，我再给你报准点。",
        "你可以换清晰图再发我。": "你换张清晰点的再发我哈。",
        "你可以稍后再发我。": "你晚点再发我也行。",
        "收到付款，我马上安排处理，有需要第一时间联系您": "收到付款啦，我马上安排处理，有进展第一时间告诉你",
        "亲，正在为您转接人工客服，请稍等~": "我这就给你转人工，稍等哈~",
    }
    for k, v in replacements.items():
        out = out.replace(k, v)
    return out


async def ai_guard_outbound_reply(client, original_msg: dict, reply_content: str) -> tuple[bool, str, str]:
    """
    专用AI质检：发送前判断“这句是否该发”，可拦截或改写。
    读取当前客户在当前店铺的完整对话上下文。
    """
    text = (reply_content or "").strip()
    if not text:
        return False, "", "empty_reply"
    if text.startswith("话术|") or "[转移会话]" in text:
        return True, text, "command_bypass"
    if not client._reply_guard_enabled or not client.enable_agent or not client.agent or not client.AgentDeps:
        return True, text, "guard_disabled"
    try:
        from db.chat_log_db import get_conversation
        import json as _json
        import re as _re

        acc_id = str(original_msg.get("acc_id", "") or "")
        customer_id = str(original_msg.get("from_id", "") or "")
        if not customer_id:
            return True, text, "no_customer_id"

        # 默认读取较大窗口，尽量覆盖完整上下文；可用环境变量继续放大。
        try:
            max_rows = max(50, int(os.getenv("AI_REPLY_GUARD_CONTEXT_ROWS", "500")))
        except Exception:
            max_rows = 500
        rows = get_conversation(customer_id=customer_id, limit=max_rows) or []
        shop_rows = [r for r in rows if str(r.get("acc_id", "") or "") == acc_id] if acc_id else rows

        context_lines = []
        for r in shop_rows:
            role = "客" if (r.get("direction") == "in") else "服"
            msg = client.to_chinese((r.get("message") or "").strip())
            if msg:
                context_lines.append(f"{role}:{msg}")
        context_text = "\n".join(context_lines) if context_lines else "无历史"

        deps = client.AgentDeps(
            msg_id=str(original_msg.get("msg_id", "") or "reply_guard"),
            acc_id=acc_id,
            from_id=customer_id,
            platform=str(original_msg.get("acc_type", "") or ""),
        )
        prompt = (
            "你是淘宝客服回复质检器。目标：判断候选回复是否和上下文一致，是否会造成重复触发式答复。\n"
            "必须检查：\n"
            "1) 是否答非所问；\n"
            "2) 是否重复说“马上报价/继续发图”但当前上下文不需要；\n"
            "3) 是否与历史状态冲突；\n"
            "4) 语气是否自然可直接发给客户。\n"
            "若不合适，给可直接发送的一句改写。\n"
            "只输出 JSON：{\"allow\":true/false,\"rewrite\":\"...\",\"reason\":\"...\"}\n\n"
            f"完整上下文(当前店铺):\n{context_text}\n\n"
            f"客户当前消息:{client.to_chinese(original_msg.get('msg', '') or '')}\n"
            f"候选回复:{text}\n"
        )
        result = await client.agent.agent_natural_reply.run(prompt, deps=deps, message_history=[])
        raw = str(getattr(result, "output", "") or "").strip()
        if not raw:
            return True, text, "guard_empty_output"
        m = _re.search(r"\{[\s\S]*\}", raw)
        if not m:
            return True, text, "guard_non_json"
        obj = _json.loads(m.group(0))
        allow = bool(obj.get("allow", True))
        rewrite = str(obj.get("rewrite", "") or "").strip()
        reason = str(obj.get("reason", "") or "").strip() or "guard_decision"
        if allow:
            return True, (rewrite or text), reason
        if rewrite:
            return True, rewrite, reason
        return False, "", reason
    except Exception as e:
        return True, text, f"guard_error:{e}"
