#!/usr/bin/env python3
"""
MCP Feedback Enhanced 服务器主模块

此模块提供 MCP (Model Context Protocol) 的增强反馈收集功能，
支持智能环境检测，自动使用 Web UI 界面。

主要功能：
- MCP 工具实现
- 界面选择（Web UI）
- 环境检测 (SSH Remote, WSL, Local)
- 国际化支持
- 图片处理与上传
- 项目目录管理

主要 MCP 工具：
- interactive_feedback: 收集用户互动反馈
- get_system_info: 获取系统环境信息

作者: Fábio Ferreira (原作者)
增强: Minidoracat (Web UI, 图片支持, 环境检测)
重构: 模块化设计
"""

import base64
import io
import json
import os
import sys
from typing import Annotated, Any

from fastmcp import FastMCP
from fastmcp.tools.tool import ToolResult
from fastmcp.utilities.types import Image as MCPImage
from mcp.types import TextContent
from pydantic import Field

# 导入统一的调试功能
from .debug import server_debug_log as debug_log

# 导入错误处理框架
from .utils.error_handler import ErrorHandler, ErrorType

# 导入资源管理器
from .utils.resource_manager import create_temp_file


# ===== 编码初始化 =====
def init_encoding():
    """初始化编码设置，确保正确处理中文字符"""
    try:
        # Windows 特殊处理
        if sys.platform == "win32":
            import msvcrt

            # 设置为二进制模式
            msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
            msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

            # 重新包装为 UTF-8 文本流，并禁用缓冲
            stdin_buffer = getattr(sys.stdin, "buffer", None)
            if stdin_buffer is None and hasattr(sys.stdin, "detach"):
                stdin_buffer = sys.stdin.detach()  # type: ignore[union-attr]

            stdout_buffer = getattr(sys.stdout, "buffer", None)
            if stdout_buffer is None and hasattr(sys.stdout, "detach"):
                stdout_buffer = sys.stdout.detach()  # type: ignore[union-attr]

            sys.stdin = io.TextIOWrapper(
                stdin_buffer, encoding="utf-8", errors="replace", newline=None  # type: ignore[arg-type]
            )
            sys.stdout = io.TextIOWrapper(
                stdout_buffer,  # type: ignore[arg-type]
                encoding="utf-8",
                errors="replace",
                newline="",
                write_through=True,  # 关键：禁用写入缓冲
            )
        else:
            # 非 Windows 系统的标准设置
            if hasattr(sys.stdout, "reconfigure"):
                sys.stdout.reconfigure(encoding="utf-8", errors="replace")  # type: ignore[union-attr]
            if hasattr(sys.stdin, "reconfigure"):
                sys.stdin.reconfigure(encoding="utf-8", errors="replace")  # type: ignore[union-attr]

        # 设置 stderr 编码（用于调试信息）
        if hasattr(sys.stderr, "reconfigure"):
            sys.stderr.reconfigure(encoding="utf-8", errors="replace")  # type: ignore[union-attr]

        return True
    except Exception:
        # 如果编码设置失败，尝试基本设置
        try:
            if hasattr(sys.stdout, "reconfigure"):
                sys.stdout.reconfigure(encoding="utf-8", errors="replace")  # type: ignore[union-attr]
            if hasattr(sys.stdin, "reconfigure"):
                sys.stdin.reconfigure(encoding="utf-8", errors="replace")  # type: ignore[union-attr]
            if hasattr(sys.stderr, "reconfigure"):
                sys.stderr.reconfigure(encoding="utf-8", errors="replace")  # type: ignore[union-attr]
        except:
            pass
        return False


# 初始化编码（在导入时就执行）
_encoding_initialized = init_encoding()

# ===== 常量定义 =====
SERVER_NAME = "互动式反馈收集 MCP"
SSH_ENV_VARS = ["SSH_CONNECTION", "SSH_CLIENT", "SSH_TTY"]
REMOTE_ENV_VARS = ["REMOTE_CONTAINERS", "CODESPACES"]


# 初始化 MCP 服务器
from . import __version__


# 确保 log_level 设定为正确的大写格式
fastmcp_settings = {}

# 检查环境变量并设定正确的 log_level
env_log_level = os.getenv("FASTMCP_LOG_LEVEL", "").upper()
if env_log_level in ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"):
    fastmcp_settings["log_level"] = env_log_level
else:
    # 默认使用 INFO 等级
    fastmcp_settings["log_level"] = "INFO"

mcp: Any = FastMCP(SERVER_NAME)


# ===== 工具函数 =====
def is_wsl_environment() -> bool:
    """
    检测是否在 WSL (Windows Subsystem for Linux) 环境中运行

    Returns:
        bool: True 表示 WSL 环境，False 表示其他环境
    """
    try:
        # 检查 /proc/version 文件是否包含 WSL 标识
        if os.path.exists("/proc/version"):
            with open("/proc/version") as f:
                version_info = f.read().lower()
                if "microsoft" in version_info or "wsl" in version_info:
                    debug_log("检测到 WSL 环境（通过 /proc/version）")
                    return True

        # 检查 WSL 相关环境变量
        wsl_env_vars = ["WSL_DISTRO_NAME", "WSL_INTEROP", "WSLENV"]
        for env_var in wsl_env_vars:
            if os.getenv(env_var):
                debug_log(f"检测到 WSL 环境变量: {env_var}")
                return True

        # 检查是否存在 WSL 特有的路径
        wsl_paths = ["/mnt/c", "/mnt/d", "/proc/sys/fs/binfmt_misc/WSLInterop"]
        for path in wsl_paths:
            if os.path.exists(path):
                debug_log(f"检测到 WSL 特有路径: {path}")
                return True

    except Exception as e:
        debug_log(f"WSL 检测过程中发生错误: {e}")

    return False


def is_remote_environment() -> bool:
    """
    检测是否在远程环境中运行

    Returns:
        bool: True 表示远程环境，False 表示本地环境
    """
    # WSL 不应被视为远程环境，因为它可以访问 Windows 浏览器
    if is_wsl_environment():
        debug_log("WSL 环境不被视为远程环境")
        return False

    # 检查 SSH 连接指标
    for env_var in SSH_ENV_VARS:
        if os.getenv(env_var):
            debug_log(f"检测到 SSH 环境变量: {env_var}")
            return True

    # 检查远程开发环境
    for env_var in REMOTE_ENV_VARS:
        if os.getenv(env_var):
            debug_log(f"检测到远程开发环境: {env_var}")
            return True

    # 检查 Docker 容器
    if os.path.exists("/.dockerenv"):
        debug_log("检测到 Docker 容器环境")
        return True

    # Windows 远程桌面检查
    if sys.platform == "win32":
        session_name = os.getenv("SESSIONNAME", "")
        if session_name and "RDP" in session_name:
            debug_log(f"检测到 Windows 远程桌面: {session_name}")
            return True

    # Linux 无显示环境检查（但排除 WSL）
    if (
        sys.platform.startswith("linux")
        and not os.getenv("DISPLAY")
        and not is_wsl_environment()
    ):
        debug_log("检测到 Linux 无显示环境")
        return True

    return False


def save_feedback_to_file(feedback_data: dict, file_path: str | None = None) -> str:
    """
    将反馈数据保存到 JSON 文件

    Args:
        feedback_data: 反馈数据字典
        file_path: 保存路径，若为 None 则自动生成临时文件

    Returns:
        str: 保存的文件路径
    """
    if file_path is None:
        # 使用资源管理器创建临时文件
        file_path = create_temp_file(suffix=".json", prefix="feedback_")

    # 确保目录存在
    directory = os.path.dirname(file_path)
    if directory and not os.path.exists(directory):
        os.makedirs(directory, exist_ok=True)

    # 复制数据以避免修改原始数据
    json_data = feedback_data.copy()

    # 处理图片数据：将 bytes 转换为 base64 字符串以便 JSON 序列化
    if "images" in json_data and isinstance(json_data["images"], list):
        processed_images = []
        for img in json_data["images"]:
            if isinstance(img, dict) and "data" in img:
                processed_img = img.copy()
                # 如果 data 是 bytes，转换为 base64 字符串
                if isinstance(img["data"], bytes):
                    processed_img["data"] = base64.b64encode(img["data"]).decode(
                        "utf-8"
                    )
                    processed_img["data_type"] = "base64"
                processed_images.append(processed_img)
            else:
                processed_images.append(img)
        json_data["images"] = processed_images

    # 保存数据
    with open(file_path, "w", encoding="utf-8") as f:
        json.dump(json_data, f, ensure_ascii=False, indent=2)

    debug_log(f"反馈数据已保存至: {file_path}")
    return file_path


def create_feedback_text(feedback_data: dict) -> str:
    """
    创建格式化的反馈文本

    Args:
        feedback_data: 反馈数据字典

    Returns:
        str: 格式化后的反馈文本
    """
    text_parts = []

    # 基本反馈内容
    if feedback_data.get("interactive_feedback"):
        text_parts.append(f"=== 用户反馈 ===\n{feedback_data['interactive_feedback']}")

    # 图片直接通过 MCPImage 传递，不在文本中包含 base64（节省 token）
    if feedback_data.get("images"):
        images = feedback_data["images"]
        debug_log(f"共 {len(images)} 张图片将通过 MCPImage 传递")

    return "\n\n".join(text_parts) if text_parts else "用户未提供任何反馈内容。"


def process_images(images_data: list[dict]) -> list[MCPImage]:
    """
    处理图片数据，转换为 MCP 图片对象

    Args:
        images_data: 图片数据列表

    Returns:
        List[MCPImage]: MCP 图片对象列表
    """
    mcp_images = []

    for i, img in enumerate(images_data, 1):
        try:
            if not img.get("data"):
                debug_log(f"图片 {i} 没有数据，跳过")
                continue

            # 检查数据类型并相应处理
            if isinstance(img["data"], bytes):
                image_bytes = img["data"]
                debug_log(
                    f"图片 {i} 使用原始 bytes 数据，大小: {len(image_bytes)} bytes"
                )
            elif isinstance(img["data"], str):
                image_bytes = base64.b64decode(img["data"])
                debug_log(f"图片 {i} 从 base64 解码，大小: {len(image_bytes)} bytes")
            else:
                debug_log(f"图片 {i} 数据类型不支持: {type(img['data'])}")
                continue

            if len(image_bytes) == 0:
                debug_log(f"图片 {i} 数据为空，跳过")
                continue

            # 根据文件名推断格式
            file_name = img.get("name", "image.png")
            if file_name.lower().endswith((".jpg", ".jpeg")):
                image_format = "jpeg"
            elif file_name.lower().endswith(".gif"):
                image_format = "gif"
            else:
                image_format = "png"

            # 创建 MCPImage 对象
            mcp_image = MCPImage(data=image_bytes, format=image_format)
            mcp_images.append(mcp_image)

            debug_log(f"图片 {i} ({file_name}) 处理成功，格式: {image_format}")

        except Exception as e:
            error_id = ErrorHandler.log_error_with_context(
                e,
                context={"operation": "图片处理", "image_index": i},
                error_type=ErrorType.FILE_IO,
            )
            debug_log(f"图片 {i} 处理失败 [错误ID: {error_id}]: {e}")

    debug_log(f"共处理 {len(mcp_images)} 张图片")
    return mcp_images


# ===== MCP Prompt 定义 =====
@mcp.prompt(name="chat")
def gl_mcp_feedback() -> str:
    """通过 Web UI 与用户开始互动反馈对话。

    使用此 prompt 快速启动反馈交流。
    AI 会调用 interactive_feedback 工具打开 Web UI，
    通过浏览器界面与用户进行互动。
    """
    return (
        "请立即调用 gl-mcp-feedback 的 interactive_feedback 工具与用户开始互动反馈对话。"
        "在 summary 参数中总结你目前已完成的工作内容。\n\n"
        "工具参数（直接使用，无需查阅 schema）：\n"
        "- project_directory (string, 默认 \".\"): 项目目录路径\n"
        "- summary (string, 默认 \"我已完成了您请求的任务。\"): AI 工作完成的摘要说明\n"
        "- timeout (integer, 默认 600): 等待用户反馈的超时时间（秒）"
    )


# ===== MCP 工具定义 =====
@mcp.tool()
async def interactive_feedback(
    project_directory: Annotated[str, Field(description="项目目录路径")] = ".",
    summary: Annotated[
        str, Field(description="AI 工作完成的摘要说明")
    ] = "我已完成了您请求的任务。",
    timeout: Annotated[int, Field(description="等待用户反馈的超时时间（秒）")] = 600,
) -> ToolResult:
    """Interactive feedback collection tool for LLM agents.

    USAGE RULES:
    1. During any process, task, or conversation, whether asking, replying, or completing phased tasks, you must call this tool to ask for feedback.
    2. Unless receiving termination instructions, all steps must repeatedly call this tool.
    3. Whenever user feedback is received, if the feedback content is not empty, you must call this tool again and adjust behavior based on the feedback content.
    4. Only when the user explicitly indicates "end" or "no more interaction needed" can you stop calling this tool, and the process is considered complete.
    5. You should summarize what have done, and provide project directory through args to let user know what you have done to provide feedback for next step.

    Args:
        project_directory: Project directory path for context
        summary: Summary of AI work completed for user review
        timeout: Timeout in seconds for waiting user feedback (default: 600 seconds)

    Returns:
        ToolResult: ToolResult containing TextContent and MCPImage objects representing user feedback
    """
    # 环境检测
    is_remote = is_remote_environment()
    is_wsl = is_wsl_environment()

    debug_log(f"环境检测结果 - 远程: {is_remote}, WSL: {is_wsl}")
    debug_log("使用界面: Web UI")

    try:
        # 确保项目目录存在
        if not os.path.exists(project_directory):
            project_directory = os.getcwd()
        project_directory = os.path.abspath(project_directory)

        # 使用 Web 模式
        debug_log("反馈模式: web")

        result = await launch_web_feedback_ui(project_directory, summary, timeout)

        # 处理取消情况
        if not result:
            return ToolResult(content=[TextContent(type="text", text="用户取消了反馈。")])

        # 保存详细结果
        save_feedback_to_file(result)

        # 创建反馈项目列表
        feedback_items = []

        # 添加文字反馈（图片通过 MCPImage 单独传递，不包含在文本中）
        if result.get("interactive_feedback"):
            feedback_text = create_feedback_text(result)
            feedback_items.append(TextContent(type="text", text=feedback_text))
            debug_log("文字反馈已添加")

        # 添加图片反馈
        if result.get("images"):
            mcp_images = process_images(result["images"])
            feedback_items.extend(mcp_images)
            debug_log(f"已添加 {len(mcp_images)} 张图片")

        # 确保至少有一个反馈项目
        if not feedback_items:
            feedback_items.append(
                TextContent(type="text", text="用户未提供任何反馈内容。")
            )

        debug_log(f"反馈收集完成，共 {len(feedback_items)} 个项目")
        return ToolResult(content=feedback_items)

    except Exception as e:
        error_id = ErrorHandler.log_error_with_context(
            e,
            context={"operation": "反馈收集", "project_dir": project_directory},
            error_type=ErrorType.SYSTEM,
        )

        # 生成用户友好的错误信息
        user_error_msg = ErrorHandler.format_user_error(e, include_technical=False)
        debug_log(f"反馈收集错误 [错误ID: {error_id}]: {e!s}")

        return ToolResult(content=[TextContent(type="text", text=user_error_msg)])


async def launch_web_feedback_ui(project_dir: str, summary: str, timeout: int) -> dict:
    """
    启动 Web UI 收集反馈，支持自定义超时时间

    Args:
        project_dir: 项目目录路径
        summary: AI 工作摘要
        timeout: 超时时间（秒）

    Returns:
        dict: 收集到的反馈数据
    """
    debug_log(f"启动 Web UI 界面，超时时间: {timeout} 秒")

    try:
        from .web import launch_web_feedback_ui as web_launch

        return await web_launch(project_dir, summary, timeout)
    except ImportError as e:
        error_id = ErrorHandler.log_error_with_context(
            e,
            context={"operation": "Web UI 模块导入", "module": "web"},
            error_type=ErrorType.DEPENDENCY,
        )
        user_error_msg = ErrorHandler.format_user_error(
            e, ErrorType.DEPENDENCY, include_technical=False
        )
        debug_log(f"Web UI 模块导入失败 [错误ID: {error_id}]: {e}")

        return {
            "interactive_feedback": user_error_msg,
            "images": [],
        }


@mcp.tool()
def get_system_info() -> str:
    """
    获取系统环境信息

    Returns:
        str: JSON 格式的系统信息
    """
    is_remote = is_remote_environment()
    is_wsl = is_wsl_environment()

    system_info = {
        "平台": sys.platform,
        "Python 版本": sys.version.split()[0],
        "WSL 环境": is_wsl,
        "远程环境": is_remote,
        "界面类型": "Web UI",
        "环境变量": {
            "SSH_CONNECTION": os.getenv("SSH_CONNECTION"),
            "SSH_CLIENT": os.getenv("SSH_CLIENT"),
            "DISPLAY": os.getenv("DISPLAY"),
            "VSCODE_INJECTION": os.getenv("VSCODE_INJECTION"),
            "SESSIONNAME": os.getenv("SESSIONNAME"),
            "WSL_DISTRO_NAME": os.getenv("WSL_DISTRO_NAME"),
            "WSL_INTEROP": os.getenv("WSL_INTEROP"),
            "WSLENV": os.getenv("WSLENV"),
        },
    }

    return json.dumps(system_info, ensure_ascii=False, indent=2)


# ===== 主程序入口 =====
def main():
    """主要入口点，用于包执行
    收集用户的互动反馈，支持文字和图片
    此工具使用 Web UI 界面收集用户反馈，支持智能环境检测。

    用户可以：
    1. 提供文字反馈
    2. 上传图片作为反馈
    3. 查看 AI 的工作摘要

    调试模式：
    - 设置环境变量 MCP_DEBUG=true 可启用详细调试输出
    - 生产环境建议关闭调试模式以避免输出干扰
    """
    debug_enabled = os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on")

    if debug_enabled:
        debug_log("🚀 启动互动式反馈收集 MCP 服务器")
        debug_log(f"   服务器名称: {SERVER_NAME}")
        debug_log(f"   版本: {__version__}")
        debug_log(f"   平台: {sys.platform}")
        debug_log(f"   编码初始化: {'成功' if _encoding_initialized else '失败'}")
        debug_log(f"   远程环境: {is_remote_environment()}")
        debug_log(f"   WSL 环境: {is_wsl_environment()}")
        debug_log("   界面类型: Web UI")
        debug_log("   等待来自 AI 助手的调用...")
        debug_log("准备启动 MCP 服务器...")
        debug_log("调用 mcp.run()...")

    try:
        mcp.run()
    except KeyboardInterrupt:
        if debug_enabled:
            debug_log("收到中断信号，正常退出")
        sys.exit(0)
    except Exception as e:
        if debug_enabled:
            debug_log(f"MCP 服务器启动失败: {e}")
            import traceback

            debug_log(f"详细错误: {traceback.format_exc()}")
        sys.exit(1)


if __name__ == "__main__":
    main()
