import contextlib
import time
import json
import asyncio
from datetime import datetime
from typing import List, Dict, Any, Optional, Tuple, Callable
from src.common.logger import get_logger
from src.config.config import global_config
from src.prompt.prompt_manager import prompt_manager
from src.services import llm_service as llm_api
from sqlmodel import select, col
from src.common.database.database import get_db_session
from src.common.database.database_model import ThinkingQuestion
from src.memory_system.retrieval_tools import get_tool_registry, init_all_tools
from src.llm_models.payload_content.message import MessageBuilder, RoleType, Message
from src.chat.message_receive.chat_manager import chat_manager as _chat_manager
from src.learners.jargon_explainer_old import retrieve_concepts_with_jargon

logger = get_logger("memory_retrieval")

THINKING_BACK_NOT_FOUND_RETENTION_SECONDS = 36000  # 未找到答案记录保留时长
THINKING_BACK_CLEANUP_INTERVAL_SECONDS = 3000  # 清理频率
_last_not_found_cleanup_ts: float = 0.0


def _cleanup_stale_not_found_thinking_back() -> None:
    """定期清理过期的未找到答案记录"""
    global _last_not_found_cleanup_ts

    now = time.time()
    if now - _last_not_found_cleanup_ts < THINKING_BACK_CLEANUP_INTERVAL_SECONDS:
        return

    threshold_time = now - THINKING_BACK_NOT_FOUND_RETENTION_SECONDS
    try:
        with get_db_session() as session:
            statement = select(ThinkingQuestion).where(
                col(ThinkingQuestion.found_answer).is_(False)
                & (ThinkingQuestion.updated_timestamp < datetime.fromtimestamp(threshold_time))
            )
            records = session.exec(statement).all()
            for record in records:
                session.delete(record)
        if records:
            logger.info(f"清理过期的未找到答案thinking_question记录 {len(records)} 条")
        _last_not_found_cleanup_ts = now
    except Exception as e:
        logger.error(f"清理未找到答案的thinking_back记录失败: {e}")


def init_memory_retrieval_sys():
    """初始化记忆检索相关工具"""
    # 注册所有工具
    init_all_tools()


def _log_conversation_messages(
    conversation_messages: List[Message],
    head_prompt: Optional[str] = None,
    final_status: Optional[str] = None,
) -> None:
    """输出对话消息列表的日志

    Args:
        conversation_messages: 对话消息列表
        head_prompt: 第一条系统消息（head_prompt）的内容，可选
        final_status: 最终结果状态描述（例如：找到答案/未找到答案），可选
    """
    if not global_config.debug.show_memory_prompt:
        return

    log_lines: List[str] = []

    # 如果有head_prompt，先添加为第一条消息
    if head_prompt:
        msg_info = "========================================\n[消息 1] 角色: System\n-----------------------------"
        msg_info += f"\n{head_prompt}"
        log_lines.append(msg_info)
        start_idx = 2
    else:
        start_idx = 1

    if not conversation_messages and not head_prompt:
        return

    for idx, msg in enumerate(conversation_messages, start_idx):
        role_name = msg.role.value if hasattr(msg.role, "value") else str(msg.role)

        # 构建单条消息的日志信息
        # msg_info = f"\n========================================\n[消息 {idx}] 角色: {role_name} 内容类型: {content_type}\n-----------------------------"
        msg_info = (
            f"\n========================================\n[消息 {idx}] 角色: {role_name}\n-----------------------------"
        )

        # if full_content:
        #     msg_info += f"\n{full_content}"
        if msg.content:
            msg_info += f"\n{msg.content}"

        if msg.tool_calls:
            msg_info += f"\n  工具调用: {len(msg.tool_calls)}个"
            for tool_call in msg.tool_calls:
                msg_info += f"\n    - {tool_call.func_name}: {json.dumps(tool_call.args, ensure_ascii=False)}"

        # if msg.tool_call_id:
        # msg_info += f"\n  工具调用ID: {msg.tool_call_id}"

        log_lines.append(msg_info)

    total_count = len(conversation_messages) + (1 if head_prompt else 0)
    log_text = f"消息列表 (共{total_count}条):{''.join(log_lines)}"
    if final_status:
        log_text += f"\n\n[最终结果] {final_status}"
    logger.info(log_text)


async def _react_agent_solve_question(
    chat_id: str,
    max_iterations: int = 5,
    timeout: float = 30.0,
    initial_info: str = "",
    chat_history: str = "",
) -> Tuple[bool, str, List[Dict[str, Any]], bool]:
    """使用ReAct架构的Agent来解决问题

    Args:
        chat_id: 聊天ID
        max_iterations: 最大迭代次数
        timeout: 超时时间（秒）
        initial_info: 初始信息，将作为collected_info的初始值
        chat_history: 聊天记录，将传递给 ReAct Agent prompt

    Returns:
        Tuple[bool, str, List[Dict[str, Any]], bool]: (是否找到答案, 答案内容, 思考步骤列表, 是否超时)
    """
    start_time = time.time()
    collected_info = initial_info or ""
    # 构造日志前缀：[聊天流名称]，用于在日志中标识聊天流
    try:
        chat_name = _chat_manager.get_session_name(chat_id) or chat_id
    except Exception:
        chat_name = chat_id
    react_log_prefix = f"[{chat_name}] "
    thinking_steps = []
    is_timeout = False
    conversation_messages: List[Message] = []
    first_head_prompt: Optional[str] = None  # 保存第一次使用的head_prompt（用于日志显示）
    last_tool_name: Optional[str] = None  # 记录最后一次使用的工具名称

    # 使用 while 循环，支持额外迭代
    iteration = 0
    max_iterations_with_extra = max_iterations
    while iteration < max_iterations_with_extra:
        # 检查超时
        if time.time() - start_time > timeout:
            logger.warning(f"ReAct Agent超时，已迭代{iteration}次")
            is_timeout = True
            break

        # 获取工具注册器
        tool_registry = get_tool_registry()

        # 获取bot_name
        bot_name = global_config.bot.nickname

        # 获取当前时间
        time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())

        # 计算剩余迭代次数
        current_iteration = iteration + 1
        remaining_iterations = max_iterations - current_iteration

        # 提取函数调用中参数的值，支持单引号和双引号
        def extract_quoted_content(text, func_name, param_name):
            """从文本中提取函数调用中参数的值，支持单引号和双引号

            Args:
                text: 要搜索的文本
                func_name: 函数名，如 'return_information'
                param_name: 参数名，如 'information'

            Returns:
                提取的参数值，如果未找到则返回None
            """
            if not text:
                return None

            # 查找函数调用位置（不区分大小写）
            func_pattern = func_name.lower()
            text_lower = text.lower()
            func_pos = text_lower.find(func_pattern)
            if func_pos == -1:
                return None

            # 查找参数名和等号
            param_pattern = f"{param_name}="
            param_pos = text_lower.find(param_pattern, func_pos)
            if param_pos == -1:
                return None

            # 跳过参数名、等号和空白
            start_pos = param_pos + len(param_pattern)
            while start_pos < len(text) and text[start_pos] in " \t\n":
                start_pos += 1

            if start_pos >= len(text):
                return None

            # 确定引号类型
            quote_char = text[start_pos]
            if quote_char not in ['"', "'"]:
                return None

            # 查找匹配的结束引号（考虑转义）
            end_pos = start_pos + 1
            while end_pos < len(text):
                if text[end_pos] == quote_char:
                    # 检查是否是转义的引号
                    if end_pos > start_pos + 1 and text[end_pos - 1] == "\\":
                        end_pos += 1
                        continue
                    # 找到匹配的引号
                    content = text[start_pos + 1 : end_pos]
                    # 处理转义字符
                    content = content.replace('\\"', '"').replace("\\'", "'").replace("\\\\", "\\")
                    return content
                end_pos += 1

            return None

        # 正常迭代：使用head_prompt决定调用哪些工具（包含return_information工具）
        tool_definitions = tool_registry.get_tool_definitions()
        # tool_names = [tool_def["name"] for tool_def in tool_definitions]
        # logger.debug(f"ReAct Agent 第 {iteration + 1} 次迭代，问题: {question}|可用工具: {', '.join(tool_names)} (共{len(tool_definitions)}个)")

        # head_prompt应该只构建一次，使用初始的collected_info，后续迭代都复用同一个
        if first_head_prompt is None:
            # 第一次构建，使用初始的collected_info（即initial_info）
            initial_collected_info = initial_info or ""
            # 使用 LPMM 知识库检索 prompt
            first_head_prompt_template = prompt_manager.get_prompt("memory_retrieval_react_prompt_head_lpmm")
            first_head_prompt_template.add_context("bot_name", bot_name)
            first_head_prompt_template.add_context("time_now", time_now)
            first_head_prompt_template.add_context("chat_history", chat_history)
            first_head_prompt_template.add_context("collected_info", initial_collected_info)
            first_head_prompt_template.add_context("current_iteration", str(current_iteration))
            first_head_prompt_template.add_context("remaining_iterations", str(remaining_iterations))
            first_head_prompt_template.add_context("max_iterations", str(max_iterations))
            first_head_prompt = await prompt_manager.render_prompt(first_head_prompt_template)

        # 后续迭代都复用第一次构建的head_prompt
        head_prompt = first_head_prompt

        def _build_messages(
            _client,
            *,
            _head_prompt: str = head_prompt,
            _conversation_messages: List[Message] = conversation_messages,
        ):
            messages: List[Message] = []

            system_builder = MessageBuilder()
            system_builder.set_role(RoleType.System)
            system_builder.add_text_content(_head_prompt)
            messages.append(system_builder.build())

            messages.extend(_conversation_messages)

            return messages

        message_factory_fn: Callable[..., List[Message]] = _build_messages  # pyright: ignore[reportGeneralTypeIssues]
        generation_result = await llm_api.generate(
            llm_api.LLMServiceRequest(
                task_name="tool_use",
                request_type="memory.react",
                message_factory=message_factory_fn,  # type: ignore[arg-type]
                tool_options=tool_definitions,
            )
        )
        success = generation_result.success
        response = generation_result.completion.response
        reasoning_content = generation_result.completion.reasoning
        tool_calls = generation_result.completion.tool_calls

        # logger.info(
        # f"ReAct Agent 第 {iteration + 1} 次迭代 模型: {model_name} ，调用工具数量: {len(tool_calls) if tool_calls else 0} ，调用工具响应: {response}"
        # )

        if not success:
            logger.error(f"ReAct Agent LLM调用失败: {response}")
            break

        # 注意：这里会检查return_information工具调用，如果检测到return_information工具，会根据information参数决定返回信息或退出查询

        assistant_message: Optional[Message] = None
        if tool_calls:
            assistant_builder = MessageBuilder()
            assistant_builder.set_role(RoleType.Assistant)
            if response and response.strip():
                assistant_builder.add_text_content(response)
            assistant_builder.set_tool_calls(tool_calls)
            assistant_message = assistant_builder.build()
        elif response and response.strip():
            assistant_builder = MessageBuilder()
            assistant_builder.set_role(RoleType.Assistant)
            assistant_builder.add_text_content(response)
            assistant_message = assistant_builder.build()

        # 记录思考步骤
        step: Dict[str, Any] = {
            "iteration": iteration + 1,
            "thought": response,
            "actions": [],
            "observations": [],
        }

        if assistant_message:
            conversation_messages.append(assistant_message)

        # 记录思考过程到collected_info中
        if reasoning_content or response:
            thought_summary = reasoning_content or (response[:200] if response else "")
            if thought_summary:
                collected_info += f"\n[思考] {thought_summary}\n"

        # 处理工具调用
        if not tool_calls:
            # 如果没有工具调用，检查响应文本中是否包含return_information函数调用格式或JSON格式
            if response and response.strip():
                # 首先尝试解析JSON格式的return_information
                def parse_json_return_information(text: str):
                    """从文本中解析JSON格式的return_information，返回information字符串，如果未找到则返回None"""
                    if not text:
                        return None, None

                    try:
                        # 尝试提取JSON对象（可能包含在代码块中或直接是JSON）
                        json_text = text.strip()

                        # 如果包含代码块标记，提取JSON部分
                        if "```json" in json_text:
                            start = json_text.find("```json") + 7
                            end = json_text.find("```", start)
                            if end != -1:
                                json_text = json_text[start:end].strip()
                        elif "```" in json_text:
                            start = json_text.find("```") + 3
                            end = json_text.find("```", start)
                            if end != -1:
                                json_text = json_text[start:end].strip()

                        # 尝试解析JSON
                        data = json.loads(json_text)

                        # 检查是否包含return_information字段
                        if isinstance(data, dict) and "return_information" in data:
                            information = data.get("information", "")
                            return information
                    except (json.JSONDecodeError, ValueError, TypeError):
                        # 如果JSON解析失败，尝试在文本中查找JSON对象
                        with contextlib.suppress(json.JSONDecodeError, ValueError, TypeError):
                            # 查找第一个 { 和最后一个 } 之间的内容（更健壮的JSON提取）
                            first_brace = text.find("{")
                            if first_brace != -1:
                                # 从第一个 { 开始，找到匹配的 }
                                brace_count = 0
                                json_end = -1
                                for i in range(first_brace, len(text)):
                                    if text[i] == "{":
                                        brace_count += 1
                                    elif text[i] == "}":
                                        brace_count -= 1
                                        if brace_count == 0:
                                            json_end = i + 1
                                            break

                                if json_end != -1:
                                    json_text = text[first_brace:json_end]
                                    data = json.loads(json_text)
                                    if isinstance(data, dict) and "return_information" in data:
                                        information = data.get("information", "")
                                        return information

                    return None

                # 尝试从文本中解析return_information函数调用
                def parse_return_information_from_text(text: str):
                    """从文本中解析return_information函数调用，返回information字符串，如果未找到则返回None"""
                    if not text:
                        return None

                    # 查找return_information函数调用位置（不区分大小写）
                    func_pattern = "return_information"
                    text_lower = text.lower()
                    func_pos = text_lower.find(func_pattern)
                    if func_pos == -1:
                        return None

                    # 解析information参数（字符串，使用extract_quoted_content）
                    information = extract_quoted_content(text, "return_information", "information")

                    # 如果information存在（即使是空字符串），也返回它
                    return information

                # 首先尝试解析JSON格式
                parsed_information_json = parse_json_return_information(response)
                is_json_format = parsed_information_json is not None

                # 如果JSON解析成功，使用JSON结果
                if is_json_format:
                    parsed_information = parsed_information_json
                else:
                    # 如果JSON解析失败，尝试解析函数调用格式
                    parsed_information = parse_return_information_from_text(response)

                if parsed_information is not None or is_json_format:
                    # 检测到return_information格式（可能是JSON格式或函数调用格式）
                    format_type = "JSON格式" if is_json_format else "函数调用格式"
                    # 返回信息（即使为空字符串也返回）
                    step["actions"].append(
                        {
                            "action_type": "return_information",
                            "action_params": {"information": parsed_information or ""},
                        }
                    )
                    parsed_info_text = parsed_information if isinstance(parsed_information, str) else ""
                    if parsed_info_text.strip():
                        step["observations"] = [f"检测到return_information{format_type}调用，返回信息"]
                        thinking_steps.append(step)
                        logger.info(
                            f"{react_log_prefix}第 {iteration + 1} 次迭代 通过return_information{format_type}返回信息: {parsed_info_text[:100]}..."
                        )

                        _log_conversation_messages(
                            conversation_messages,
                            head_prompt=first_head_prompt,
                            final_status=f"返回信息：{parsed_info_text}",
                        )

                        return True, parsed_info_text, thinking_steps, False
                    else:
                        # 信息为空，直接退出查询
                        step["observations"] = [f"检测到return_information{format_type}调用，信息为空"]
                        thinking_steps.append(step)
                        logger.info(
                            f"{react_log_prefix}第 {iteration + 1} 次迭代 通过return_information{format_type}判断信息为空"
                        )

                        _log_conversation_messages(
                            conversation_messages,
                            head_prompt=first_head_prompt,
                            final_status="信息为空：通过return_information文本格式判断信息为空",
                        )

                        return False, "", thinking_steps, False

                # 如果没有检测到return_information格式，记录思考过程，继续下一轮迭代
                step["observations"] = [f"思考完成，但未调用工具。响应: {response}"]
                logger.info(f"{react_log_prefix}第 {iteration + 1} 次迭代 思考完成但未调用工具: {response}")
                collected_info += f"思考: {response}"
            else:
                logger.warning(f"{react_log_prefix}第 {iteration + 1} 次迭代 无工具调用且无响应")
                step["observations"] = ["无响应且无工具调用"]
            thinking_steps.append(step)
            iteration += 1  # 在continue之前增加迭代计数，避免跳过iteration += 1
            continue

        # 处理工具调用
        # 首先检查是否有return_information工具调用，如果有则立即返回，不再处理其他工具
        return_information_info = None
        for tool_call in tool_calls:
            tool_name = tool_call.func_name
            tool_args = tool_call.args or {}

            if tool_name == "return_information":
                return_information_info = tool_args.get("information", "")

                # 返回信息（即使为空也返回）
                step["actions"].append(
                    {
                        "action_type": "return_information",
                        "action_params": {"information": return_information_info},
                    }
                )
                if return_information_info and return_information_info.strip():
                    # 有信息，返回
                    step["observations"] = ["检测到return_information工具调用，返回信息"]
                    thinking_steps.append(step)
                    logger.info(
                        f"{react_log_prefix}第 {iteration + 1} 次迭代 通过return_information工具返回信息: {return_information_info}"
                    )

                    _log_conversation_messages(
                        conversation_messages,
                        head_prompt=first_head_prompt,
                        final_status=f"返回信息：{return_information_info}",
                    )

                    return True, return_information_info, thinking_steps, False
                else:
                    # 信息为空，直接退出查询
                    step["observations"] = ["检测到return_information工具调用，信息为空"]
                    thinking_steps.append(step)
                    logger.info(f"{react_log_prefix}第 {iteration + 1} 次迭代 通过return_information工具判断信息为空")

                    _log_conversation_messages(
                        conversation_messages,
                        head_prompt=first_head_prompt,
                        final_status="信息为空：通过return_information工具判断信息为空",
                    )

                    return False, "", thinking_steps, False

        # 如果没有return_information工具调用，继续处理其他工具
        tool_tasks = []
        for i, tool_call in enumerate(tool_calls):
            tool_name = tool_call.func_name
            tool_args = tool_call.args or {}

            logger.debug(
                f"{react_log_prefix}第 {iteration + 1} 次迭代 工具调用 {i + 1}/{len(tool_calls)}: {tool_name}({tool_args})"
            )

            # 跳过return_information工具调用（已经在上面处理过了）
            if tool_name == "return_information":
                continue

            # 记录最后一次使用的工具名称（用于判断是否需要额外迭代）
            last_tool_name = tool_name

            # 普通工具调用
            tool = tool_registry.get_tool(tool_name)
            if tool:
                # 准备工具参数（需要添加chat_id如果工具需要）
                import inspect

                sig = inspect.signature(tool.execute_func)
                tool_params = tool_args.copy()
                if "chat_id" in sig.parameters:
                    tool_params["chat_id"] = chat_id

                # 创建异步任务
                async def execute_single_tool(tool_instance, params, tool_name_str, iter_num):
                    try:
                        observation = await tool_instance.execute(**params)
                        param_str = ", ".join([f"{k}={v}" for k, v in params.items() if k != "chat_id"])
                        return f"查询{tool_name_str}({param_str})的结果：{observation}"
                    except Exception as e:
                        error_msg = f"工具执行失败: {str(e)}"
                        logger.error(f"{react_log_prefix}第 {iter_num + 1} 次迭代 工具 {tool_name_str} {error_msg}")
                        return f"查询{tool_name_str}失败: {error_msg}"

                tool_tasks.append(execute_single_tool(tool, tool_params, tool_name, iteration))
                step["actions"].append({"action_type": tool_name, "action_params": tool_args})
            else:
                error_msg = f"未知的工具类型: {tool_name}"
                logger.warning(
                    f"{react_log_prefix}第 {iteration + 1} 次迭代 工具 {i + 1}/{len(tool_calls)} {error_msg}"
                )
                tool_tasks.append(asyncio.create_task(asyncio.sleep(0, result=f"查询{tool_name}失败: {error_msg}")))

        # 并行执行所有工具
        if tool_tasks:
            observations = await asyncio.gather(*tool_tasks, return_exceptions=True)

            # 处理执行结果
            for i, (tool_call_item, observation) in enumerate(zip(tool_calls, observations, strict=False)):
                if isinstance(observation, Exception):
                    observation = f"工具执行异常: {str(observation)}"
                    logger.error(f"{react_log_prefix}第 {iteration + 1} 次迭代 工具 {i + 1} 执行异常: {observation}")

                observation_text = observation if isinstance(observation, str) else str(observation)
                stripped_observation = observation_text.strip()
                step["observations"].append(observation_text)
                collected_info += f"\n{observation_text}\n"
                if stripped_observation:
                    # 不再自动检测工具输出中的jargon，改为通过 query_words 工具主动查询
                    tool_builder = MessageBuilder()
                    tool_builder.set_role(RoleType.Tool)
                    tool_builder.add_text_content(observation_text)
                    tool_builder.add_tool_call(tool_call_item.call_id)
                    conversation_messages.append(tool_builder.build())

        thinking_steps.append(step)

        # 检查是否需要额外迭代：如果最后一次使用的工具是 search_chat_history 且达到最大迭代次数，额外增加一回合
        if iteration + 1 >= max_iterations and last_tool_name == "search_chat_history" and not is_timeout:
            max_iterations_with_extra = max_iterations + 1
            logger.info(
                f"{react_log_prefix}达到最大迭代次数（已迭代{iteration + 1}次），最后一次使用工具为 search_chat_history，额外增加一回合尝试"
            )

        iteration += 1

    # 正常迭代结束后，如果达到最大迭代次数或超时，执行最终评估
    # 最终评估单独处理，不算在迭代中
    should_do_final_evaluation = False
    if is_timeout:
        should_do_final_evaluation = True
        logger.warning(f"{react_log_prefix}超时，已迭代{iteration}次，进入最终评估")
    elif iteration >= max_iterations:
        should_do_final_evaluation = True
        logger.info(f"{react_log_prefix}达到最大迭代次数（已迭代{iteration}次），进入最终评估")

    if should_do_final_evaluation:
        # 获取必要变量用于最终评估
        tool_registry = get_tool_registry()
        bot_name = global_config.bot.nickname
        time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        current_iteration = iteration + 1
        remaining_iterations = 0

        # 提取函数调用中参数的值，支持单引号和双引号
        def extract_quoted_content(text, func_name, param_name):
            """从文本中提取函数调用中参数的值，支持单引号和双引号

            Args:
                text: 要搜索的文本
                func_name: 函数名，如 'return_information'
                param_name: 参数名，如 'information'

            Returns:
                提取的参数值，如果未找到则返回None
            """
            if not text:
                return None

            # 查找函数调用位置（不区分大小写）
            func_pattern = func_name.lower()
            text_lower = text.lower()
            func_pos = text_lower.find(func_pattern)
            if func_pos == -1:
                return None

            # 查找参数名和等号
            param_pattern = f"{param_name}="
            param_pos = text_lower.find(param_pattern, func_pos)
            if param_pos == -1:
                return None

            # 跳过参数名、等号和空白
            start_pos = param_pos + len(param_pattern)
            while start_pos < len(text) and text[start_pos] in " \t\n":
                start_pos += 1

            if start_pos >= len(text):
                return None

            # 确定引号类型
            quote_char = text[start_pos]
            if quote_char not in ['"', "'"]:
                return None

            # 查找匹配的结束引号（考虑转义）
            end_pos = start_pos + 1
            while end_pos < len(text):
                if text[end_pos] == quote_char:
                    # 检查是否是转义的引号
                    if end_pos > start_pos + 1 and text[end_pos - 1] == "\\":
                        end_pos += 1
                        continue
                    # 找到匹配的引号
                    content = text[start_pos + 1 : end_pos]
                    # 处理转义字符
                    content = content.replace('\\"', '"').replace("\\'", "'").replace("\\\\", "\\")
                    return content
                end_pos += 1

            return None

        # 执行最终评估
        evaluation_prompt_template = prompt_manager.get_prompt("memory_retrieval_react_final")
        evaluation_prompt_template.add_context("bot_name", bot_name)
        evaluation_prompt_template.add_context("time_now", time_now)
        evaluation_prompt_template.add_context("chat_history", chat_history)
        evaluation_prompt_template.add_context("collected_info", collected_info or "暂无信息")
        evaluation_prompt_template.add_context("current_iteration", str(current_iteration))
        evaluation_prompt_template.add_context("remaining_iterations", str(remaining_iterations))
        evaluation_prompt_template.add_context("max_iterations", str(max_iterations))
        evaluation_prompt = await prompt_manager.render_prompt(evaluation_prompt_template)

        evaluation_result = await llm_api.generate(
            llm_api.LLMServiceRequest(
                task_name="tool_use",
                request_type="memory.react.final",
                prompt=evaluation_prompt,
                tool_options=[],
            )
        )
        eval_success = evaluation_result.success
        eval_response = evaluation_result.completion.response

        if not eval_success:
            logger.error(f"ReAct Agent 最终评估阶段 LLM调用失败: {eval_response}")
            _log_conversation_messages(
                conversation_messages,
                head_prompt=first_head_prompt,
                final_status="未找到答案：最终评估阶段LLM调用失败",
            )
            return False, "最终评估阶段LLM调用失败", thinking_steps, is_timeout

        if global_config.debug.show_memory_prompt:
            logger.info(f"{react_log_prefix}最终评估Prompt: {evaluation_prompt}")
            logger.info(f"{react_log_prefix}最终评估响应: {eval_response}")

        # 从最终评估响应中提取return_information
        return_information_content = None

        if eval_response:
            return_information_content = extract_quoted_content(eval_response, "return_information", "information")

        # 如果提取到信息，返回（无论是否超时，都视为成功完成）
        if return_information_content is not None:
            eval_step = {
                "iteration": current_iteration,
                "thought": f"[最终评估] {eval_response}",
                "actions": [
                    {"action_type": "return_information", "action_params": {"information": return_information_content}}
                ],
                "observations": ["最终评估阶段检测到return_information"],
            }
            thinking_steps.append(eval_step)
            if return_information_content and return_information_content.strip():
                logger.info(f"ReAct Agent 最终评估阶段返回信息: {return_information_content}")
                _log_conversation_messages(
                    conversation_messages,
                    head_prompt=first_head_prompt,
                    final_status=f"返回信息：{return_information_content}",
                )
                return True, return_information_content, thinking_steps, False
            else:
                logger.info("ReAct Agent 最终评估阶段判断信息为空")
                _log_conversation_messages(
                    conversation_messages,
                    head_prompt=first_head_prompt,
                    final_status="信息为空：最终评估阶段判断信息为空",
                )
                return False, "", thinking_steps, False

        # 如果没有明确判断，视为not_enough_info，返回空字符串（不返回任何信息）
        eval_step = {
            "iteration": current_iteration,
            "thought": f"[最终评估] {eval_response}",
            "actions": [{"action_type": "return_information", "action_params": {"information": ""}}],
            "observations": ["已到达最大迭代次数，信息为空"],
        }
        thinking_steps.append(eval_step)
        logger.info("ReAct Agent 已到达最大迭代次数，信息为空")

        _log_conversation_messages(
            conversation_messages,
            head_prompt=first_head_prompt,
            final_status="未找到答案：已到达最大迭代次数，无法找到答案",
        )

        return False, "", thinking_steps, is_timeout

    # 如果正常迭代过程中提前找到答案返回，不会到达这里
    # 如果正常迭代结束但没有触发最终评估（理论上不应该发生），直接返回
    logger.warning("ReAct Agent正常迭代结束，但未触发最终评估")
    _log_conversation_messages(
        conversation_messages,
        head_prompt=first_head_prompt,
        final_status="未找到答案：正常迭代结束",
    )

    return False, "", thinking_steps, is_timeout


def _get_recent_query_history(chat_id: str, time_window_seconds: float = 600.0) -> str:
    """获取最近一段时间内的查询历史（用于避免重复查询）

    Args:
        chat_id: 聊天ID
        time_window_seconds: 时间窗口（秒），默认10分钟

    Returns:
        str: 格式化的查询历史字符串
    """
    try:
        _current_time = time.time()

        with get_db_session() as session:
            statement = (
                select(ThinkingQuestion)
                .where(col(ThinkingQuestion.context) == chat_id)
                .order_by(col(ThinkingQuestion.updated_timestamp).desc())
                .limit(5)
            )
            records = session.exec(statement).all()

        if not records:
            return ""

        history_lines = ["最近已查询的问题和结果："]

        for record in records:
            status = "✓ 已找到答案" if record.found_answer else "✗ 未找到答案"
            answer_preview = ""
            # 只有找到答案时才显示答案内容
            if record.found_answer and record.answer:
                # 截取答案前100字符
                answer_preview = record.answer[:100]
                if len(record.answer) > 100:
                    answer_preview += "..."

            history_lines.extend([f"- 问题：{record.question}", f"  状态：{status}"])
            if answer_preview:
                history_lines.append(f"  答案：{answer_preview}")
            history_lines.append("")  # 空行分隔

        return "\n".join(history_lines)

    except Exception as e:
        logger.error(f"获取查询历史失败: {e}")
        return ""


def _get_recent_found_answers(chat_id: str, time_window_seconds: float = 600.0) -> List[str]:
    """获取最近一段时间内已找到答案的查询记录（用于返回给 replyer）

    Args:
        chat_id: 聊天ID
        time_window_seconds: 时间窗口（秒），默认10分钟

    Returns:
        List[str]: 格式化的答案列表，每个元素格式为 "问题：xxx\n答案：xxx"
    """
    try:
        _current_time = time.time()

        # 查询最近时间窗口内已找到答案的记录，按更新时间倒序
        with get_db_session() as session:
            statement = (
                select(ThinkingQuestion)
                .where(col(ThinkingQuestion.context) == chat_id)
                .where(col(ThinkingQuestion.found_answer))
                .where(col(ThinkingQuestion.answer).is_not(None))
                .where(col(ThinkingQuestion.answer) != "")
                .order_by(col(ThinkingQuestion.updated_timestamp).desc())
                .limit(3)
            )
            records = session.exec(statement).all()

        if not records:
            return []

        return [f"问题：{record.question}\n答案：{record.answer}" for record in records if record.answer]

    except Exception as e:
        logger.error(f"获取最近已找到答案的记录失败: {e}")
        return []


def _store_thinking_back(
    chat_id: str, question: str, context: str, found_answer: bool, answer: str, thinking_steps: List[Dict[str, Any]]
) -> None:
    """存储或更新思考过程到数据库（如果已存在则更新，否则创建）

    Args:
        chat_id: 聊天ID
        question: 问题
        context: 上下文信息
        found_answer: 是否找到答案
        answer: 答案内容
        thinking_steps: 思考步骤列表
    """
    try:
        now = time.time()

        # 先查询是否已存在相同chat_id和问题的记录
        with get_db_session() as session:
            statement = (
                select(ThinkingQuestion)
                .where(col(ThinkingQuestion.context) == chat_id)
                .where(col(ThinkingQuestion.question) == question)
                .order_by(col(ThinkingQuestion.updated_timestamp).desc())
                .limit(1)
            )
            if record := session.exec(statement).first():
                record.context = context
                record.found_answer = found_answer
                record.answer = answer
                record.thinking_steps = json.dumps(thinking_steps, ensure_ascii=False)
                record.updated_timestamp = datetime.fromtimestamp(now)
                session.add(record)
                logger.info(f"已更新思考过程到数据库，问题: {question[:50]}...")
                return

            new_record = ThinkingQuestion(
                question=question,
                context=chat_id,
                found_answer=found_answer,
                answer=answer,
                thinking_steps=json.dumps(thinking_steps, ensure_ascii=False),
                created_timestamp=datetime.fromtimestamp(now),
                updated_timestamp=datetime.fromtimestamp(now),
            )
            session.add(new_record)
    except Exception as e:
        logger.error(f"存储思考过程失败: {e}")


async def _process_memory_retrieval(
    chat_id: str,
    context: str,
    initial_info: str = "",
    max_iterations: Optional[int] = None,
    chat_history: str = "",
) -> Optional[str]:
    """处理记忆检索

    Args:
        chat_id: 聊天ID
        context: 上下文信息
        initial_info: 初始信息，将传递给ReAct Agent
        max_iterations: 最大迭代次数
        chat_history: 聊天记录，将传递给 ReAct Agent

    Returns:
        Optional[str]: 如果找到答案，返回答案内容，否则返回None
    """
    _cleanup_stale_not_found_thinking_back()

    question_initial_info = initial_info or ""

    # 直接使用ReAct Agent进行记忆检索
    # 如果未指定max_iterations，使用配置的默认值
    if max_iterations is None:
        max_iterations = global_config.memory.max_agent_iterations

    found_answer, answer, thinking_steps, is_timeout = await _react_agent_solve_question(
        chat_id=chat_id,
        max_iterations=max_iterations,
        timeout=global_config.memory.agent_timeout_seconds,
        initial_info=question_initial_info,
        chat_history=chat_history,
    )

    # 不再存储到数据库，直接返回答案
    if is_timeout:
        logger.info("ReAct Agent超时，不返回结果")

    return answer if found_answer and answer else None


async def build_memory_retrieval_prompt(
    message: str,
    sender: str,
    target: str,
    chat_stream,
    think_level: int = 1,
    unknown_words: Optional[List[str]] = None,
) -> str:
    """构建记忆检索提示
    Args:
        message: 聊天历史记录
        sender: 发送者名称
        target: 目标消息内容
        chat_stream: 聊天流对象
        think_level: 思考深度等级
        unknown_words: Planner 提供的未知词语列表，优先使用此列表而不是从聊天记录匹配

    Returns:
        str: 记忆检索结果字符串
    """
    start_time = time.time()

    # 构造日志前缀：[聊天流名称]，用于在日志中标识聊天流（优先群名称/用户昵称）
    try:
        group_info = chat_stream.group_info
        user_info = chat_stream.user_info
        # 群聊优先使用群名称
        if group_info is not None and getattr(group_info, "group_name", None):
            stream_name = group_info.group_name.strip() or str(group_info.group_id)
        # 私聊使用用户昵称
        elif user_info is not None and getattr(user_info, "user_nickname", None):
            stream_name = user_info.user_nickname.strip() or str(user_info.user_id)
        # 兜底使用 stream_id
        else:
            stream_name = chat_stream.stream_id
    except Exception:
        stream_name = chat_stream.stream_id
    log_prefix = f"[{stream_name}] " if stream_name else ""

    logger.info(f"{log_prefix}检测是否需要回忆，元消息：{message[:30]}...，消息长度: {len(message)}")
    try:
        chat_id = chat_stream.stream_id

        # 初始阶段：使用 Planner 提供的 unknown_words 进行检索（如果提供）
        initial_info = ""
        if unknown_words and len(unknown_words) > 0:
            # 清理和去重 unknown_words
            cleaned_concepts = []
            for word in unknown_words:
                if isinstance(word, str):
                    if cleaned := word.strip():
                        cleaned_concepts.append(cleaned)
            if cleaned_concepts:
                # 对匹配到的概念进行jargon检索，作为初始信息
                concept_info = await retrieve_concepts_with_jargon(cleaned_concepts, chat_id)
                if concept_info:
                    initial_info += concept_info
                    logger.info(
                        f"{log_prefix}使用 Planner 提供的 unknown_words，共 {len(cleaned_concepts)} 个概念，检索结果: {concept_info[:100]}..."
                    )
                else:
                    logger.debug(f"{log_prefix}unknown_words 检索未找到任何结果")

        # 直接使用 ReAct Agent 进行记忆检索（跳过问题生成步骤）
        base_max_iterations = global_config.memory.max_agent_iterations
        # 根据think_level调整迭代次数：think_level=1时不变，think_level=0时减半
        if think_level == 0:
            max_iterations = max(1, base_max_iterations // 2)  # 至少为1
        else:
            max_iterations = base_max_iterations
        timeout_seconds = global_config.memory.agent_timeout_seconds
        logger.debug(
            f"{log_prefix}直接使用 ReAct Agent 进行记忆检索，think_level={think_level}，设置最大迭代次数: {max_iterations}（基础值: {base_max_iterations}），超时时间: {timeout_seconds}秒"
        )

        # 直接调用 ReAct Agent 处理记忆检索
        try:
            result = await _process_memory_retrieval(
                chat_id=chat_id,
                context=message,
                initial_info=initial_info,
                max_iterations=max_iterations,
                chat_history=message,
            )
        except Exception as e:
            logger.error(f"{log_prefix}处理记忆检索时发生异常: {e}")
            result = None

        end_time = time.time()

        if result:
            logger.info(f"{log_prefix}记忆检索成功，耗时: {(end_time - start_time):.3f}秒")
            return f"你回忆起了以下信息：\n{result}\n如果与回复内容相关，可以参考这些回忆的信息。\n"
        else:
            logger.debug(f"{log_prefix}记忆检索未找到相关信息")
            return ""

    except Exception as e:
        logger.error(f"{log_prefix}记忆检索时发生异常: {str(e)}")
        return ""
