"""reply 内置工具。"""

from typing import Any, Optional

import traceback

from src.chat.replyer.replyer_manager import replyer_manager
from src.cli.maisaka_cli_sender import CLI_PLATFORM_NAME, render_cli_message
from src.common.data_models.reply_generation_data_models import ReplyGenerationResult
from src.common.logger import get_logger
from src.config import config as config_module
from src.core.tooling import ToolExecutionContext, ToolExecutionResult, ToolInvocation, ToolSpec
from src.maisaka.message_adapter import build_visible_text_from_sequence
from src.services import send_service

from .context import BuiltinToolRuntimeContext

logger = get_logger("maisaka_builtin_reply")


async def _run_expression_selector(tool_ctx: BuiltinToolRuntimeContext, system_prompt: str) -> str:
    """运行 replyer 侧表达方式选择子代理，并返回文本结果。"""
    response = await tool_ctx.runtime.run_sub_agent(
        context_message_limit=10,
        system_prompt=system_prompt,
        request_kind="expression_selector",
    )
    return (response.content or "").strip()


def get_tool_spec() -> ToolSpec:
    """获取 reply 工具声明。"""

    return ToolSpec(
        name="reply",
        brief_description="根据当前思考生成并发送一条可见回复。",
        detailed_description=(
            "参数说明：\n"
            "- msg_id：string，必填。要回复的目标用户消息编号。\n"
            "- set_quote：boolean，可选。以引用回复的方式发送，默认 true。\n"
            "- reference_info：string，可选。上文中有助于回复的所有参考信息，使用平文本格式。"
        ),
        parameters_schema={
            "type": "object",
            "properties": {
                "msg_id": {
                    "type": "string",
                    "description": "要回复的目标用户消息编号。",
                },
                "set_quote": {
                    "type": "boolean",
                    "description": "以引用回复的方式发送这条回复，不用每句都引用。",
                    "default": True,
                },
                "reference_info": {
                    "type": "string",
                    "description": "有助于回复的信息，之前搜集得到的事实性信息，记忆等，使用平文本格式。",
                    "default": True,
                },
            },
            "required": ["msg_id"],
        },
        provider_name="maisaka_builtin",
        provider_type="builtin",
    )


def _build_monitor_metadata(reply_result: ReplyGenerationResult) -> dict[str, object]:
    """从 reply 结果中提取统一监控详情。"""

    monitor_detail = reply_result.monitor_detail
    if isinstance(monitor_detail, dict):
        return {"monitor_detail": monitor_detail}
    return {}


def _build_send_result(
    *,
    index: int,
    segment: str,
    set_quote: bool,
    success: bool,
    message_id: str = "",
) -> dict[str, Any]:
    """构建分段回复的轻量发送结果。"""

    return {
        "index": index,
        "segment": segment,
        "set_quote": set_quote,
        "success": success,
        "message_id": message_id,
    }


async def handle_tool(
    tool_ctx: BuiltinToolRuntimeContext,
    invocation: ToolInvocation,
    context: Optional[ToolExecutionContext] = None,
) -> ToolExecutionResult:
    """执行 reply 内置工具。"""

    latest_thought = context.reasoning if context is not None else invocation.reasoning
    reference_info = str(invocation.arguments.get("reference_info") or "").strip()
    target_message_id = str(invocation.arguments.get("msg_id") or "").strip()
    set_quote = bool(invocation.arguments.get("set_quote", True))
    enable_reply_quote = bool(config_module.global_config.chat.enable_reply_quote)
    effective_set_quote = set_quote and enable_reply_quote

    if not target_message_id:
        return tool_ctx.build_failure_result(
            invocation.tool_name,
            "reply 工具需要提供有效的 `msg_id` 参数。",
        )

    target_message = tool_ctx.runtime._source_messages_by_id.get(target_message_id)
    if target_message is None:
        return tool_ctx.build_failure_result(
            invocation.tool_name,
            f"未找到要回复的目标消息，msg_id={target_message_id}",
        )

    try:
        replyer = replyer_manager.get_replyer(
            chat_stream=tool_ctx.runtime.chat_stream,
            request_type="maisaka_replyer",
            replyer_type="maisaka",
        )
    except Exception:
        logger.exception(
            f"{tool_ctx.runtime.log_prefix} 获取回复生成器时发生异常: 目标消息编号={target_message_id}"
        )
        logger.info(traceback.format_exc())
        return tool_ctx.build_failure_result(
            invocation.tool_name,
            "获取 Maisaka 回复生成器时发生异常。",
        )

    if replyer is None:
        logger.error(f"{tool_ctx.runtime.log_prefix} 获取 Maisaka 回复生成器失败")
        return tool_ctx.build_failure_result(
            invocation.tool_name,
            "Maisaka 回复生成器当前不可用。",
        )

    replyer_chat_history = list(tool_ctx.runtime._chat_history)

    try:
        success, reply_result = await replyer.generate_reply_with_context(
            reply_reason=latest_thought,
            reference_info=reference_info,
            stream_id=tool_ctx.runtime.session_id,
            reply_message=target_message,
            chat_history=replyer_chat_history,
            sub_agent_runner=lambda system_prompt: _run_expression_selector(
                tool_ctx,
                system_prompt,
            ),
            log_reply=False,
        )
    except Exception as exc:
        logger.exception(
            f"{tool_ctx.runtime.log_prefix} 回复生成器执行异常: 目标消息编号={target_message_id} 异常={exc}"
        )
        return tool_ctx.build_failure_result(
            invocation.tool_name,
            "生成可见回复时发生异常。",
        )

    reply_metadata = _build_monitor_metadata(reply_result)
    reply_text = reply_result.completion.response_text.strip() if success else ""
    if not reply_text:
        logger.warning(
            f"{tool_ctx.runtime.log_prefix} 回复生成器返回空文本: "
            f"目标消息编号={target_message_id} 错误信息={reply_result.error_message!r}"
        )
        return tool_ctx.build_failure_result(
            invocation.tool_name,
            "生成可见回复失败。",
            metadata=reply_metadata,
        )

    reply_sequences = tool_ctx.post_process_reply_message_sequences(reply_text)
    reply_segments = [build_visible_text_from_sequence(sequence) for sequence in reply_sequences]
    combined_reply_text = "".join(reply_segments)
    sent_message_ids: list[str] = []
    send_results: list[dict[str, Any]] = []
    try:
        sent = False
        if tool_ctx.runtime.chat_stream.platform == CLI_PLATFORM_NAME:
            for index, segment in enumerate(reply_segments):
                render_cli_message(segment)
                send_results.append(
                    _build_send_result(
                        index=index,
                        segment=segment,
                        set_quote=effective_set_quote if index == 0 else False,
                        success=True,
                    )
                )
            sent = True
        else:
            for index, reply_sequence in enumerate(reply_sequences):
                segment = reply_segments[index]
                segment_set_quote = effective_set_quote if index == 0 else False
                sent_message = await send_service._send_to_target_with_message(
                    message_sequence=reply_sequence,
                    stream_id=tool_ctx.runtime.session_id,
                    processed_plain_text=segment,
                    set_reply=segment_set_quote,
                    reply_message=target_message if segment_set_quote else None,
                    selected_expressions=reply_result.selected_expression_ids or None,
                    typing=index > 0,
                    sync_to_maisaka_history=True,
                    maisaka_source_kind="guided_reply",
                )
                sent = sent_message is not None
                if not sent:
                    send_results.append(
                        _build_send_result(
                            index=index,
                            segment=segment,
                            set_quote=segment_set_quote,
                            success=False,
                        )
                    )
                    break
                sent_message_id = str(getattr(sent_message, "message_id", "") or "").strip()
                if sent_message_id:
                    sent_message_ids.append(sent_message_id)
                send_results.append(
                    _build_send_result(
                        index=index,
                        segment=segment,
                        set_quote=segment_set_quote,
                        success=True,
                        message_id=sent_message_id,
                    )
                )
    except Exception:
        logger.exception(
            f"{tool_ctx.runtime.log_prefix} 发送文字消息时发生异常，目标消息编号={target_message_id}"
        )
        return tool_ctx.build_failure_result(
            invocation.tool_name,
            "发送可见回复时发生异常。",
            metadata=reply_metadata,
        )

    if not sent:
        return tool_ctx.build_failure_result(
            invocation.tool_name,
            "可见回复生成成功，但发送失败。",
            structured_content={
                "msg_id": target_message_id,
                "set_quote": set_quote,
                "effective_set_quote": effective_set_quote,
                "reply_segments": reply_segments,
                "send_results": send_results,
            },
            metadata=reply_metadata,
        )

    target_user_info = target_message.message_info.user_info
    target_user_name = target_user_info.user_cardname or target_user_info.user_nickname or target_user_info.user_id

    if tool_ctx.runtime.chat_stream.platform == CLI_PLATFORM_NAME:
        tool_ctx.append_guided_reply_to_chat_history(combined_reply_text)
    tool_ctx.runtime._record_reply_sent()
    reply_metadata["sent_message_ids"] = sent_message_ids
    reply_metadata["send_results"] = send_results
    track_reply_effect = getattr(tool_ctx.runtime, "track_reply_effect", None)
    if track_reply_effect is not None:
        await track_reply_effect(
            tool_call_id=invocation.call_id,
            target_message=target_message,
            set_quote=effective_set_quote,
            reply_text=combined_reply_text,
            reply_segments=reply_segments,
            planner_reasoning=latest_thought,
            reference_info=reference_info,
            tool_context={
                "tool_name": invocation.tool_name,
                "call_id": invocation.call_id,
                "arguments": dict(invocation.arguments or {}),
                "reasoning": latest_thought,
            },
            send_results=send_results,
            reply_metadata=reply_metadata,
            replyer_context_messages=replyer_chat_history,
        )
    return tool_ctx.build_success_result(
        invocation.tool_name,
        "回复已生成并发送。",
        structured_content={
            "msg_id": target_message_id,
            "set_quote": set_quote,
            "effective_set_quote": effective_set_quote,
            "reply_text": combined_reply_text,
            "reply_segments": reply_segments,
            "send_results": send_results,
            "target_user_name": target_user_name,
        },
        metadata=reply_metadata,
    )
