import asyncio
import logging
import re
import secrets

logger = logging.getLogger("cs_agent")


async def debounce_agent_reply(client, data: dict):
    """
    消息防抖：同一客户在 _DEBOUNCE_SECONDS 内的连续消息合并后再处理。
    订单通知、付款相关消息不走防抖，立即处理。
    """
    msg_body = data.get("msg", "")
    key = f"{data.get('acc_id','')}:{data.get('from_id','')}"
    client._cancel_auto_quote_task(key, reason="new_inbound")

    # 以下情况跳过防抖，立即处理（后台执行，不阻塞接收循环）
    immediate_keywords = ["买家已付款", "已付款", "[系统订单信息]"]
    if any(kw in msg_body for kw in immediate_keywords):
        client._activity_log(
            "debounce_bypass_immediate",
            acc_id=data.get("acc_id", ""),
            customer_id=data.get("from_id", ""),
            reason="payment_or_order",
            msg=msg_body,
        )
        client._fire_and_forget(client._agent_reply_serialized(data))
        return

    # 积攒消息
    if key not in client._pending_msgs:
        client._pending_msgs[key] = []
    client._pending_msgs[key].append(msg_body)
    client._activity_log(
        "debounce_enqueue",
        key=key,
        queue_size=len(client._pending_msgs[key]),
        msg=msg_body,
    )

    # 取消上一个等待任务（如果有）
    old_task = client._debounce_tasks.get(key)
    if old_task and not old_task.done():
        old_task.cancel()

    debounce_seconds = pick_debounce_seconds(client, data, msg_body)

    # 创建新的延迟处理任务
    async def _delayed(capture_key, capture_data, wait_s: float):
        await asyncio.sleep(wait_s)
        msgs = client._pending_msgs.pop(capture_key, [])
        if not msgs:
            return
        if len(msgs) == 1:
            merged_msg = msgs[0]
        else:
            merged_msg = "、".join(m for m in msgs if m.strip())
            logger.info(f"[{client.get_time()}] 防抖合并 {len(msgs)} 条消息: {merged_msg[:60]}")
        client._activity_log(
            "debounce_flush",
            key=capture_key,
            merged_count=len(msgs),
            merged_msg=merged_msg,
        )
        merged_data = dict(capture_data)
        merged_data["msg"] = merged_msg
        await client._agent_reply_serialized(merged_data)

    task = asyncio.create_task(_delayed(key, data, debounce_seconds))
    client._debounce_tasks[key] = task


def rand_between(low: float, high: float) -> float:
    if high <= low:
        return float(low)
    # 使用 secrets 增强随机性，避免固定周期导致机械感
    span = high - low
    return round(low + span * (secrets.randbelow(1000) / 1000.0), 2)


def guess_intent_for_debounce(client, msg: str) -> str:
    text = (msg or "").strip()
    if not text:
        return "unknown"
    if msg_has_image_url(text):
        return "image"
    try:
        from utils.intent_analyzer import detect_intent

        decision = detect_intent(text)
        intent = decision.intent
        if intent:
            client._activity_log(
                "debounce_intent_detected",
                intent=intent,
                source=decision.source,
                score=round(float(decision.score or 0.0), 4),
                msg=text[:120],
            )
    except Exception:
        intent = ""
    if intent:
        return intent
    lower = text.lower()
    if any(k in lower for k in ["报价", "多少钱", "价格", "贵", "优惠", "收费", "怎么收费", "咋收费"]):
        return "询价"
    if any(k in lower for k in ["做一下", "改一下", "需求", "门头", "上面的字", "处理"]):
        return "修改"
    if any(k in lower for k in ["在吗", "你好", "有人"]):
        return "打招呼"
    return "unknown"


def looks_like_requirement_text(msg: str) -> bool:
    text = (msg or "").strip().lower()
    if not text:
        return False
    req_kw = (
        "做一下",
        "改一下",
        "处理一下",
        "这个字",
        "上面的字",
        "门头",
        "去背景",
        "抠图",
        "换色",
        "调色",
        "清晰",
        "高清",
        "尺寸",
        "比例",
        "横版",
        "竖版",
        "排版",
        "改字",
        "按这个做",
        "照这个做",
        "就这张",
        "看看做",
        "弄一下",
    )
    return any(k in text for k in req_kw)


def pick_debounce_seconds(client, data: dict, msg: str) -> float:
    """意图驱动防抖：不同意图不同等待区间，并引入轻微随机。"""
    base = max(1.0, float(client._DEBOUNCE_SECONDS))
    if not client._adaptive_debounce_enabled:
        return base

    intent = guess_intent_for_debounce(client, msg)
    is_req = looks_like_requirement_text(msg)
    has_img = msg_has_image_url(msg)

    # 区间策略：越明确、越短消息，等待越短；需求描述类稍长
    if intent == "打招呼":
        low, high = 1.0, min(3.0, base)
    elif intent in ("询价", "砍价"):
        # 询价先略等一会，给客户补发图片/需求的窗口，减少机械两连回
        low, high = 4.0, min(7.0, max(base, 7.0))
    elif intent == "image":
        # 文本里直接贴图链接：短等合并上下文，避免和上一条询价并发
        low, high = 2.2, 4.2
    elif intent in ("修改", "批量"):
        low, high = max(3.0, base * 0.65), min(18.0, base + 2.0)
    elif intent == "转接":
        low, high = 1.0, 2.5
    else:
        low, high = max(2.0, base * 0.5), base

    # 发图后的需求描述，优先“多等一点”收集完整需求，减少半句回复
    # 约束到 12-14s，避免等待过长。
    if is_req and not has_img:
        low = max(low, 12.0)
        high = min(14.0, max(high, 12.6))

    # 短句更快，长句稍慢，避免把连续半句拆开
    text_len = len((msg or "").strip())
    if text_len <= 4:
        high = min(high, max(low + 0.2, 2.5))
    elif text_len >= 18:
        low = min(high, low + 0.6)

    wait_s = rand_between(low, high)
    logger.info(f"防抖等待 {wait_s}s | intent={intent} | len={text_len}")
    return wait_s


def msg_has_image_url(msg: str) -> bool:
    """判断文本消息里是否包含图片URL（客户粘贴了图片链接，可能带前缀文字如 有吗#*#https://...）"""
    if not msg:
        return False
    lower = msg.lower()
    image_exts = (".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp")
    image_hosts = ("alicdn.com", "imgextra", "taobao.com", "jd.com", "pinduoduo.com")
    if "http://" in lower or "https://" in lower:
        if any(ext in lower for ext in image_exts) or any(h in lower for h in image_hosts):
            return True
    return False


def msg_refers_images(msg: str) -> bool:
    """判断文本是否指代之前的图片（图一/图二/这张/那张/上面那张等）"""
    if not msg:
        return False
    refs = (
        "图一",
        "图二",
        "第一张",
        "第二张",
        "这张",
        "那张",
        "这图",
        "那个图",
        "这个",
        "这个呢",
        "上面那张",
        "下面那张",
        "刚才那张",
        "上一张",
        "下一张",
    )
    return any(r in msg for r in refs)


def extract_image_urls(msg: str) -> list:
    if not msg:
        return []
    parts = [p.strip() for p in msg.split("#*#") if p.strip()]
    urls = []
    for p in parts:
        if p.startswith("http://") or p.startswith("https://"):
            urls.append(p)
    if not urls and ("http://" in msg or "https://" in msg):
        tokens = re.findall(r"(https?://\S+)", msg)
        for t in tokens:
            if any(ext in t.lower() for ext in [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"]):
                urls.append(t)
    return urls[:8]


def collect_recent_image_urls(client, customer_id: str, acc_id: str, max_count: int = 6) -> list:
    """从最近对话中回溯收集图片URL（优先买家消息），用于慢发或引用图片的场景"""
    urls, seen = [], set()
    try:
        from db.chat_log_db import get_recent_conversation

        recent = get_recent_conversation(customer_id=customer_id, acc_id=acc_id, limit=20)
        # 从最近到更早遍历，收集买家(in)消息中的图片链接
        for item in reversed(recent):
            if item.get("direction") != "in":
                continue
            message = item.get("message") or ""
            found = extract_image_urls(message)
            for u in found:
                if u not in seen:
                    seen.add(u)
                    urls.append(u)
                    if len(urls) >= max_count:
                        return urls
    except Exception:
        logger.debug("收集近期图片URL失败", exc_info=True)
    return urls
