import json
import os
import uuid
from typing import Optional, List, Dict, Any
from datetime import datetime
from mcp.server.fastmcp import FastMCP

# 初始化 MCP 服务器
mcp = FastMCP("QuizMaster")

# 配置文件名
QUESTIONS_FILE = "questions.json"
STATE_FILE = "quiz_state.json"
HISTORY_FILE = "chat_history.json"

# 模式定义映射
VALID_MODES = {
    "1": "vocabulary",  # 单词模式
    "2": "scenario",    # 情景模式
    "3": "exam"         # 考试模式
}
MODE_NAMES = {
    "vocabulary": "单词模式",
    "scenario": "情景模式",
    "exam": "考试模式"
}

# --- 辅助函数 ---

def get_data_path(filename: str) -> str:
    """获取数据文件的绝对路径。"""
    if os.path.exists(filename):
        return filename
    script_dir = os.path.dirname(os.path.abspath(__file__))
    return os.path.join(script_dir, filename)

def load_questions() -> Dict[str, List[Dict[str, Any]]]:
    """
    加载题目数据。
    期望格式: {"vocabulary": [...], "scenario": [...], "exam": [...]}
    兼容旧版列表格式: [...] -> {"default": [...]}
    """
    path = get_data_path(QUESTIONS_FILE)
    if not os.path.exists(path):
        raise FileNotFoundError(f"题目文件未找到: {path}")
    
    with open(path, 'r', encoding='utf-8') as f:
        data = json.load(f)
        
    if isinstance(data, list):
        return {"default": data}
    
    if not isinstance(data, dict):
        raise ValueError("题目文件格式错误，应为 JSON 对象或列表。")
        
    return data

def load_state() -> Dict[str, Any]:
    """加载答题状态。"""
    path = get_data_path(STATE_FILE)
    if not os.path.exists(path):
        return {
            "current_index": 0, 
            "started": False, 
            "device_mac": None,
            "mode": None,
            "start_time": None
        }
    
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except json.JSONDecodeError:
        return {"current_index": 0, "started": False, "device_mac": None, "mode": None}

def save_state(state: Dict[str, Any]) -> None:
    """保存答题状态。"""
    path = get_data_path(STATE_FILE)
    with open(path, 'w', encoding='utf-8') as f:
        json.dump(state, f, ensure_ascii=False, indent=2)

# --- 历史记录管理 ---

def load_history() -> List[Dict[str, Any]]:
    path = get_data_path(HISTORY_FILE)
    if not os.path.exists(path):
        return []
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except json.JSONDecodeError:
        return []

def save_history(history: List[Dict[str, Any]]) -> None:
    path = get_data_path(HISTORY_FILE)
    with open(path, 'w', encoding='utf-8') as f:
        json.dump(history, f, ensure_ascii=False, indent=2)

def add_to_history(role: str, content: str, meta: Optional[Dict] = None) -> None:
    history = load_history()
    record = {
        "timestamp": datetime.now().isoformat(),
        "role": role,
        "content": content,
        "meta": meta or {}
    }
    history.append(record)
    save_history(history)

# --- 工具定义 ---

@mcp.tool()
def get_mac_address(mac: str = None) -> str:
    """
    调用此工具前，必须先调用self.get_device_status工具获取到mac地址
    获取设备的 MAC 地址。
    """
    try:
        if mac is not None and mac.strip():
            return mac.strip()
        else:
            # 生成一个随机的模拟 MAC 地址
            random_mac = "AA:BB:CC:DD:EE:" + ":".join([f"{uuid.uuid4().int >> (i*8) & 0xFF:02X}" for i in range(2)])
            return random_mac
    except Exception as e:
        return f"unknown-error-{str(e)}"

@mcp.tool()
def start_quiz(mac: str = None) -> str:
    """
    开始 KET 考试流程的第一步。
    1. 可选：传入或获取 MAC 地址。
    2. 初始化状态（此时尚未选择模式）。
    3. 引导用户调用 get_mac_address (如果需要) 和 select_mode。
    """
    # 如果传入了 mac，直接使用；如果没有，暂时不生成，等待用户显式调用 get_mac_address 或 select_mode 时处理
    # 这里我们只标记流程开始
    current_state = load_state()
    
    # 如果已经有 MAC 且状态正常，可以跳过部分提示，但为了流程清晰，我们每次都重置状态准备新考试
    device_mac = current_state.get("device_mac")
    
    if mac:
        device_mac = mac
    
    new_state = {
        "current_index": 0, 
        "started": True, 
        "device_mac": device_mac, # 可能为 None，直到 select_mode 确认
        "mode": None,
        "start_time": datetime.now().isoformat()
    }
    
    save_state(new_state)
    
    log_msg = "考试流程已启动。等待获取设备 MAC 及选择模式。"
    add_to_history("system", log_msg)
    
    response_text = "🔔 考试流程已启动！\n\n"
    
    if not device_mac:
        response_text += "1️⃣ **第一步**：请先调用 `get_mac_address` 获取设备标识 (或者在调用本工具时直接传入 mac 参数)。\n"
        response_text += "2️⃣ **第二步**：获取 MAC 后，调用 `select_mode` 选择考试模式。\n\n"
        response_text += "💡 提示：您可以直接调用 `get_mac_address()` 来获取一个设备 ID。"
    else:
        response_text += f"✅ 设备标识已确认：{device_mac}\n\n"
        response_text += "👉 **下一步**：请调用 `select_mode` 选择您要进行的模式：\n"
        response_text += "  - 输入 '1' 选择 [单词模式]\n"
        response_text += "  - 输入 '2' 选择 [情景模式]\n"
        response_text += "  - 输入 '3' 选择 [考试模式]"
    
    return response_text

@mcp.tool()
def select_mode(mode_choice: str) -> str:
    """
    选择考试模式。
    此步骤会确认 MAC 地址（如果之前未确认），并锁定题库模式。
    参数 mode_choice: '1', '2', 或 '3'。
    """
    state = load_state()
    
    if not state.get("started"):
        return "❌ 错误：请先调用 `start_quiz` 启动考试流程。"
    
    # 1. 确保有 MAC 地址
    device_mac = state.get("device_mac")
    if not device_mac:
        # 尝试自动获取一个
        device_mac = get_mac_address(None)
        state["device_mac"] = device_mac
        # 先保存更新的 MAC
        save_state(state)
    
    # 2. 检查是否已选过模式
    if state.get("mode"):
        return f"⚠️ 当前已处于 **{MODE_NAMES.get(state['mode'], '未知')}** 模式。\n如需切换，请先调用 `reset_progress`。"

    # 3. 验证模式选择
    mode_key = VALID_MODES.get(mode_choice)
    
    if not mode_key:
        return f"❌ 无效的选择 '{mode_choice}'。请输入 1, 2 或 3。"
    
    # 4. 验证题库数据
    try:
        all_questions = load_questions()
        
        # 检查目标模式是否存在
        if mode_key not in all_questions:
            if "default" in all_questions and len(all_questions) == 1:
                return f"❌ 错误：题目文件结构为旧版单列表，不支持分模式选择。请将题目整理为 vocabulary/scenario/exam 分类。"
            return f"❌ 错误：题目文件中未找到 '{MODE_NAMES[mode_key]}' 对应的题库。"
            
        questions_list = all_questions[mode_key]
        if not isinstance(questions_list, list):
            return f"❌ 错误：'{MODE_NAMES[mode_key]}' 的数据格式不正确（应为列表）。"
            
    except Exception as e:
        return f"❌ 加载题库失败：{str(e)}"

    # 5. 更新状态
    state["mode"] = mode_key
    state["current_index"] = 0
    save_state(state)
    
    msg = (
        f"✅ **设置成功！**\n"
        f"🆔 设备 MAC: {device_mac}\n"
        f"📚 模式：**{MODE_NAMES[mode_key]}**\n"
        f"📝 题目数量：{len(questions_list)} 题\n\n"
        f"🚀 准备好了吗？请调用 `get_next_question` 开始第 1 题！"
    )
    
    add_to_history("system", f"用户 ({device_mac}) 选择了模式：{MODE_NAMES[mode_key]}")
    return msg

# ... (原有的 import 和初始化代码保持不变) ...

# --- 新增/修改的工具定义 ---

@mcp.tool()
def switch_mode(mode_choice: str) -> str:
    """
    切换到指定的考试模式。
    此操作会改变当前的题库模式，并将答题进度重置为该模式的第 1 题。
    参数 mode_choice: '1' (单词), '2' (情景), '3' (考试)。
    """
    state = load_state()
    
    if not state.get("started"):
        return "❌ 错误：请先调用 `start_quiz` 启动考试流程。"

    # 1. 验证模式选择
    mode_key = VALID_MODES.get(mode_choice)
    if not mode_key:
        return f"❌ 无效的选择 '{mode_choice}'。请输入 1, 2 或 3。"

    # 2. 验证题库数据
    try:
        all_questions = load_questions()
        if mode_key not in all_questions:
            if "default" in all_questions and len(all_questions) == 1:
                return f"❌ 错误：题目文件结构为旧版单列表，不支持分模式切换。"
            return f"❌ 错误：题目文件中未找到 '{MODE_NAMES[mode_key]}' 对应的题库。"
            
        questions_list = all_questions[mode_key]
        if not isinstance(questions_list, list):
            return f"❌ 错误：'{MODE_NAMES[mode_key]}' 的数据格式不正确。"
            
    except Exception as e:
        return f"❌ 加载题库失败：{str(e)}"

    # 3. 检查是否已经是该模式
    if state.get("mode") == mode_key:
        return f"ℹ️ 您当前已经处于 **{MODE_NAMES[mode_key]}** 模式。无需切换。"

    # 4. 更新状态 (切换模式并重置进度)
    old_mode_name = MODE_NAMES.get(state.get("mode"), "未知")
    state["mode"] = mode_key
    state["current_index"] = 0  # 重置进度
    save_state(state)
    
    msg = (
        f"🔄 **模式切换成功！**\n"
        f"原模式：{old_mode_name} -> 新模式：**{MODE_NAMES[mode_key]}**\n"
        f"📝 新题库题目数量：{len(questions_list)} 题\n"
        f"⚠️ 答题进度已重置为新模式的第 1 题。\n\n"
        f"🚀 请调用 `get_next_question` 开始新模式的练习！"
    )
    
    add_to_history("system", f"用户 ({state.get('device_mac')}) 从 {old_mode_name} 切换到 {MODE_NAMES[mode_key]}")
    return msg

@mcp.tool()
def jump_to_question(question_number: int) -> str:
    """
    跳转到当前模式下的指定题目序号。
    参数 question_number: 题目序号（从 1 开始计数，例如 1 代表第 1 题）。
    """
    state = load_state()
    
    if not state.get("started"):
        return "❌ 考试尚未开始。请先调用 `start_quiz`。"
    
    if not state.get("mode"):
        return "⚠️ 尚未选择模式。请先调用 `select_mode` 或 `switch_mode`。"
    
    current_mode = state["mode"]
    
    # 加载题库以验证范围
    try:
        all_questions = load_questions()
        questions = []
        if current_mode in all_questions:
            questions = all_questions[current_mode]
        elif "default" in all_questions:
            questions = all_questions["default"]
        else:
            return "❌ 无法找到当前模式的题库数据。"
            
        total_questions = len(questions)
        
        if total_questions == 0:
            return "❌ 当前题库为空，无法跳转。"
            
        # 验证输入范围
        if question_number < 1:
            return f"❌ 题号必须大于 0。您输入的是：{question_number}"
        
        if question_number > total_questions:
            return f"❌ 题号超出范围！当前模式共有 {total_questions} 题，您请求的是第 {question_number} 题。"
            
        # 计算内部索引 (0-based)
        target_index = question_number - 1
        
        # 更新状态
        state["current_index"] = target_index
        save_state(state)
        
        q = questions[target_index]
        question_id = q.get('id', question_number)
        question_content = q.get('content', '无题目内容')
        
        # 构建返回文本 (直接显示跳转后的题目)
        header = f"【{MODE_NAMES.get(current_mode, '练习')} - 已跳转至第 {question_number} 题】"
        question_text = f"{header}\n\n{question_content}"
        
        if 'options' in q:
            opts = "\n".join([f"{i+1}. {opt}" for i, opt in enumerate(q['options'])])
            question_text += f"\n\n选项:\n{opts}"
            
        add_to_history("system", f"用户跳转到第 {question_number} 题")
        return question_text
        
    except Exception as e:
        return f"❌ 跳转失败：{str(e)}"

# ... (原有的 main 函数和其他代码保持不变) ...

@mcp.tool()
def get_next_question() -> str:
    """
    获取下一道题目。
    根据当前状态中的 'mode' 从对应题库中按顺序出题。
    """
    state = load_state()
    
    if not state.get("started"):
        return "❌ 考试尚未开始。请先调用 `start_quiz`。"
    
    if not state.get("mode"):
        return "⚠️ 尚未选择模式。请先调用 `select_mode` (输入 1, 2 或 3)。"
    
    current_mode = state["mode"]
    current_idx = state.get("current_index", 0)

    try:
        all_questions = load_questions()
        
        # 获取当前模式的题目列表
        if current_mode in all_questions:
            questions = all_questions[current_mode]
        elif "default" in all_questions:
            # 降级兼容：如果选了具体模式但只有 default，且 default 存在
            questions = all_questions["default"]
        else:
            questions = []
            
    except Exception as e:
        err_msg = f"❌ 错误：{str(e)}"
        add_to_history("system", err_msg)
        return err_msg

    if current_idx >= len(questions):
        msg = f"🎉 恭喜！**{MODE_NAMES.get(current_mode, '当前模式')}** 的所有题目已完成！\n输入 `reset_progress` 重新开始。"
        add_to_history("system", msg)
        return msg

    q = questions[current_idx]
    question_id = q.get('id', current_idx + 1)
    question_content = q.get('content', '无题目内容')
    
    # 构建题目文本
    header = f"【{MODE_NAMES.get(current_mode, '练习')} - 第 {question_id} 题】"
    question_text = f"{header}\n\n{question_content}"
    
    # 如果有选项，格式化显示
    if 'options' in q:
        opts = "\n".join([f"{i+1}. {opt}" for i, opt in enumerate(q['options'])])
        question_text += f"\n\n选项:\n{opts}"

    # 更新进度
    state["current_index"] = current_idx + 1
    save_state(state)

    add_to_history("system", f"发送题目 ({MODE_NAMES.get(current_mode)}): {question_id}")

    return question_text

@mcp.tool()
def reset_progress() -> str:
    """
    重置答题进度。
    保留设备 MAC 和当前选择的模式，将题目索引归零。
    """
    current_state = load_state()
    
    if not current_state.get("started"):
        return "ℹ️ 尚未开始过考试，无需重置。"

    mac_addr = current_state.get("device_mac", "unknown")
    current_mode = current_state.get("mode")
    
    new_state = {
        "current_index": 0, 
        "started": True,
        "device_mac": mac_addr,
        "mode": current_mode, 
        "start_time": datetime.now().isoformat()
    }
    
    save_state(new_state)
    
    mode_desc = f" ({MODE_NAMES.get(current_mode, '未知模式')})" if current_mode else ""
    msg = f"🔄 进度已重置{mode_desc}。\n\n准备好开始第 1 题了吗？请调用 `get_next_question`。"
    add_to_history("system", "进度已重置。")
    return msg

@mcp.tool()
def check_status() -> str:
    """查看当前的答题进度及设备信息。"""
    state = load_state()
    current_idx = state.get("current_index", 0)
    device_mac = state.get("device_mac", "未记录")
    current_mode = state.get("mode")
    
    mode_name = MODE_NAMES.get(current_mode, "未选择模式") if current_mode else "未选择模式"
    
    try:
        all_questions = load_questions()
        questions = []
        if current_mode and current_mode in all_questions:
            questions = all_questions[current_mode]
        elif "default" in all_questions:
            questions = all_questions["default"]
            
        total = len(questions)
        
        if not current_mode:
            status_msg = "已启动但**未选择模式**。"
        elif total == 0:
            status_msg = f"[{mode_name}] 题库为空。"
        elif current_idx == 0:
            status_msg = f"[{mode_name}] 尚未开始答题。"
        elif current_idx >= total:
            status_msg = f"[{mode_name}] 已全部完成！"
        else:
            status_msg = f"[{mode_name}] 进行中... 已完成 {current_idx}/{total} 题。"
            
        result = (
            f"📊 当前状态：{status_msg}\n"
            f"🆔 设备 MAC: {device_mac}\n"
            f"📂 当前模式：{mode_name}\n"
            f"(内部索引：{current_idx})"
        )
        return result
    except Exception as e:
        return f"📊 状态检查失败：{str(e)}"

@mcp.tool()
def submit_user_message(message: str) -> str:
    """用户提交答案或聊天信息。"""
    if not message or not message.strip():
        return "❌ 消息不能为空。"
    
    add_to_history("user", message)
    
    state = load_state()
    mode_context = f" ({MODE_NAMES.get(state.get('mode'), '通用')})" if state.get('mode') else ""
    
    response = f"✅ 已收到您的消息{mode_context}：\"{message}\"\n\n系统已记录。继续调用 `get_next_question` 获取下一题。"
    
    add_to_history("system", f"确认收到用户消息：{message}")
    return response

@mcp.tool()
def get_chat_history(limit: int = 10) -> str:
    """获取最近的交互历史记录。"""
    history = load_history()
    if not history:
        return "📭 暂无聊天记录。"
    
    recent_history = history[-limit:]
    output_lines = ["📜 **最近交互记录**："]
    for entry in recent_history:
        time_str = entry.get("timestamp", "").split("T")[1].split(".")[0] if "T" in entry.get("timestamp", "") else ""
        role = "👤 用户" if entry.get("role") == "user" else "🤖 系统"
        content = entry.get("content", "")
        output_lines.append(f"[{time_str}] {role}: {content}")
    
    return "\n".join(output_lines)

@mcp.tool()
def download_history_file() -> str:
    """获取完整的 chat_history.json 文件内容。"""
    path = get_data_path(HISTORY_FILE)
    if not os.path.exists(path):
        return "❌ 历史记录文件不存在。"
    
    try:
        with open(path, 'r', encoding='utf-8') as f:
            content = f.read()
        return f"📄 **chat_history.json 完整内容**:\n\n```json\n{content}\n```"
    except Exception as e:
        return f"❌ 读取文件失败：{str(e)}"

def main() -> None:
    mcp.run(transport='stdio')

if __name__ == "__main__":
    main()