from __future__ import annotations

import asyncio
import json
import logging
import re
from dataclasses import dataclass
from typing import Any

logger = logging.getLogger("cs_agent")


@dataclass
class BrainDecision:
    action: str  # reply | quote | transfer | noop
    source: str
    reply: str = ""
    transfer_msg: str = ""
    should_reply: bool = False
    need_transfer: bool = False
    payload: dict[str, Any] | None = None


def _extract_json_obj(text: str) -> dict[str, Any] | None:
    if not text:
        return None
    m = re.search(r"\{[\s\S]*\}", text)
    if not m:
        return None
    try:
        return json.loads(m.group(0))
    except Exception:
        return None


async def _ai_policy_brain_decide(client, data: dict, *, msg_text: str, shop_type: str) -> BrainDecision | None:
    if not client.enable_agent or not client.agent or not client.AgentDeps:
        return None

    acc_id = str(data.get("acc_id", "") or "")
    customer_id = str(data.get("from_id", "") or "")
    current_urls = client._extract_image_urls(msg_text)
    recent_urls = client._collect_recent_image_urls(customer_id, acc_id, max_count=6)
    key = client._customer_key(data)
    pending_urls = client._pending_images.get(key) or []

    try:
        order_status = client._detect_order_status(msg_text)
        has_image_url = client._msg_has_image_url(msg_text)
        refers_images = client._msg_refers_images(msg_text)
        is_price = client._msg_is_price_inquiry(msg_text)
        is_req = client._msg_is_requirement(msg_text)
        ext_contact = client._msg_requests_external_contact(msg_text)
    except Exception:
        order_status, has_image_url, refers_images, is_price, is_req, ext_contact = "", False, False, False, False, False

    deps = client.AgentDeps(
        msg_id=str(data.get("msg_id", "") or "brain_policy"),
        acc_id=acc_id,
        from_id=customer_id,
        platform=str(data.get("acc_type", "") or "AliWorkbench"),
    )

    prompt = (
        "你是淘宝客服系统的主决策Brain，只做决策，不要解释。\n"
        "你必须根据历史规则和当前上下文，输出唯一动作。\n"
        "可选动作 action: reply / quote / transfer / noop。\n"
        "历史规则（完整继承）:\n"
        "1) 客户发图/补图：先自然承接，再根据上下文决定继续收集或报价；\n"
        "2) 客户询价且有可用图片（当前或最近）时，优先 action=quote；\n"
        "3) 若有 pending 图片且客户催报价/补充需求，优先 quote_mode=flush_pending；\n"
        "4) 仅打招呼/短无意义文本：可 action=reply 简短承接，不要机械模板；\n"
        "5) 索要外部联系方式（微信/QQ/手机号）时，不外呼，站内引导；\n"
        "6) 订单已付款：可回执安排处理；未付款/待付款：提醒完成付款；\n"
        "7) 地图/政治/高风险内容：谨慎，必要时 transfer 或拒绝性 reply；\n"
        "8) 尺寸超限/不可做场景：给明确边界，不要胡乱承诺；\n"
        "9) 客户没发图却问价：先承接，再引导发图；\n"
        "10) 避免重复外发，避免同一句话反复说。\n"
        "\n"
        "quote_mode 可选: flush_pending / analyze_current_or_recent / collect_only\n"
        "只输出 JSON：\n"
        '{"action":"reply|quote|transfer|noop","reply":"","transfer_msg":"","quote_mode":"","reason":""}\n\n'
        f"店铺类型: {shop_type}\n"
        f"legacy_fast_quote_enabled: {str(bool(client._legacy_fast_quote_enabled)).lower()}\n"
        f"客户原话: {msg_text}\n"
        f"has_image_url: {has_image_url}\n"
        f"current_image_urls_count: {len(current_urls)}\n"
        f"recent_image_urls_count: {len(recent_urls)}\n"
        f"pending_image_urls_count: {len(pending_urls)}\n"
        f"refers_images: {refers_images}\n"
        f"is_price_inquiry: {is_price}\n"
        f"is_requirement: {is_req}\n"
        f"requests_external_contact: {ext_contact}\n"
        f"order_status: {order_status or 'none'}\n"
    )

    try:
        result = await client.agent.agent_natural_reply.run(prompt, deps=deps, message_history=[])
        raw = str(getattr(result, "output", "") or "").strip()
        obj = _extract_json_obj(raw)
        if not obj:
            client._activity_log(
                "brain_policy_parse_error",
                acc_id=acc_id,
                customer_id=customer_id,
                raw=raw[:300],
            )
            return None

        action = str(obj.get("action", "") or "").strip().lower()
        reply = str(obj.get("reply", "") or "").strip()
        transfer_msg = str(obj.get("transfer_msg", "") or "").strip()
        quote_mode = str(obj.get("quote_mode", "") or "").strip().lower()
        reason = str(obj.get("reason", "") or "").strip()

        payload: dict[str, Any] | None = None
        if action == "quote":
            mode = quote_mode or "analyze_current_or_recent"
            if mode == "flush_pending":
                payload = {"mode": "flush_pending", "key": key, "pre_reply": reply}
            elif mode == "collect_only":
                payload = {"mode": "collect_only", "pre_reply": reply}
            else:
                urls = current_urls or recent_urls
                payload = {"mode": "analyze_urls", "urls": urls, "pre_reply": reply}

        decision = BrainDecision(
            action=action if action in {"reply", "quote", "transfer", "noop"} else "noop",
            source="brain_ai_policy",
            reply=reply,
            transfer_msg=transfer_msg,
            should_reply=bool(reply),
            need_transfer=(action == "transfer"),
            payload=payload,
        )
        client._activity_log(
            "brain_policy_raw",
            acc_id=acc_id,
            customer_id=customer_id,
            action=decision.action,
            quote_mode=quote_mode,
            reason=reason,
        )
        return decision
    except Exception as e:
        client._activity_log(
            "brain_policy_error",
            acc_id=acc_id,
            customer_id=customer_id,
            error=str(e),
        )
        return None


async def decide_brain_action(client, data: dict, customer_msg, *, trace_id: str, msg_text: str, shop_type: str) -> BrainDecision:
    """统一主决策层：优先由 Brain AI 决策；失败时回退 Agent 默认决策。"""
    ai_decision = await _ai_policy_brain_decide(client, data, msg_text=msg_text, shop_type=shop_type)
    if ai_decision is not None:
        return ai_decision

    # 回退：保持可用性
    logger.info("Agent 正在处理消息...")
    client._activity_log(
        "agent_process_start",
        trace_id=trace_id,
        acc_id=data.get("acc_id", ""),
        customer_id=data.get("from_id", ""),
        msg=msg_text,
    )
    response = await client.agent.process_message(customer_msg)
    client._activity_log(
        "agent_process_done",
        trace_id=trace_id,
        acc_id=data.get("acc_id", ""),
        customer_id=data.get("from_id", ""),
        result="ok",
        should_reply=bool(response.should_reply),
        need_transfer=bool(response.need_transfer),
    )
    if response.need_transfer:
        return BrainDecision(
            action="transfer",
            source="fallback_agent",
            reply=response.reply or "",
            transfer_msg=response.transfer_msg or "",
            should_reply=bool(response.should_reply),
            need_transfer=True,
        )
    if response.should_reply and response.reply:
        return BrainDecision(
            action="reply",
            source="fallback_agent",
            reply=response.reply,
            should_reply=True,
            need_transfer=False,
        )
    return BrainDecision(action="noop", source="fallback_agent", should_reply=False, need_transfer=False)


async def execute_brain_action(client, data: dict, *, decision: BrainDecision, trace_id: str, msg_text: str):
    """统一执行层：只执行标准动作。"""
    customer_id = data.get("from_id", "")

    if customer_id:
        client._touch_customer_last_contact(customer_id)

    if decision.action == "transfer":
        logger.info("Agent 决定转接人工")
        client._activity_log(
            "agent_transfer",
            trace_id=trace_id,
            acc_id=data.get("acc_id", ""),
            customer_id=data.get("from_id", ""),
            transfer_msg=decision.transfer_msg,
        )
        client._fire_and_forget(
            client._post_tianwang_callback(
                "message_processed",
                data,
                extra={
                    "should_reply": bool(decision.should_reply),
                    "need_transfer": True,
                    "agent_reply": decision.reply or "",
                    "transfer_msg": decision.transfer_msg or "",
                },
            )
        )
        await client.transfer_to_human(data, decision.transfer_msg)
        client._push_chat_to_wechat_safe(
            data=data,
            customer_msg=msg_text,
            reply_msg=decision.transfer_msg or "转接",
            tag="转人工",
        )
        return

    if decision.action == "reply":
        text = (decision.reply or "").strip()
        if not text:
            return
        await asyncio.sleep(0.6)
        client._activity_log(
            "agent_reply",
            trace_id=trace_id,
            acc_id=data.get("acc_id", ""),
            customer_id=data.get("from_id", ""),
            reply=text,
        )
        await client.send_reply(data, text)
        await client._maybe_schedule_auto_quote(data)
        client._fire_and_forget(
            client._post_tianwang_callback(
                "message_processed",
                data,
                extra={
                    "should_reply": True,
                    "need_transfer": False,
                    "agent_reply": text,
                },
            )
        )
        client._push_chat_to_wechat_safe(
            data=data,
            customer_msg=msg_text,
            reply_msg=text,
            tag="正常AI回复",
        )
        return

    if decision.action == "quote":
        payload = decision.payload or {}
        pre_reply = str(payload.get("pre_reply", "") or "").strip()
        if pre_reply:
            await client.send_reply(data, pre_reply)
        mode = str(payload.get("mode", "") or "")
        if mode == "flush_pending":
            key = str(payload.get("key", "") or "")
            if key:
                await client._flush_pending_images(key, data)
        elif mode == "analyze_urls":
            urls = payload.get("urls") or []
            if isinstance(urls, list) and urls:
                if len(urls) == 1:
                    asyncio.create_task(client._analyze_single_and_reply(data, urls[0]))
                else:
                    asyncio.create_task(client._analyze_multi_and_reply(data, urls))
            else:
                await client.send_reply(data, "你把要处理的图再发我一下，我马上给你看。")
        else:
            if not pre_reply:
                await client.send_reply(data, "收到，我先看一下哈，稍等哈。")
        return

    # noop
    client._activity_log(
        "agent_no_reply",
        trace_id=trace_id,
        acc_id=data.get("acc_id", ""),
        customer_id=data.get("from_id", ""),
    )
    client._fire_and_forget(
        client._post_tianwang_callback(
            "message_processed",
            data,
            extra={
                "should_reply": False,
                "need_transfer": False,
                "agent_reply": "",
            },
        )
    )
