#!/usr/bin/env python3
import argparse
import json
import re
import sys
import os
from typing import List, Dict

# 角色名称库
CHARACTERS = ["Otis", "Ben", "Skylar", "Brainy", "Daisy", "Jim", "Paul", "Fred"]

# 单词库路径
WORD_LIST_PATH = os.path.join(os.path.dirname(__file__), "../references/l1_word_list.json")

# 难度对应规则
DIFFICULTY_RULES = {
    "A": {
        "visual": "强视觉特征，依赖大色块、明显大物品区分",
        "audio": "简单句，语速慢，SVO结构，关键词后置",
        "interference": "低，干扰组差异非常明显，仅需识别基础名词或颜色形容词"
    },
    "B": {
        "visual": "细节特征，依赖花纹、衣着款式、具体方位",
        "audio": "复合句，包含介词短语、连词and/but",
        "interference": "中，干扰组差异在于细节，需分辨修饰性短语"
    },
    "C": {
        "visual": "状态与动作，依赖面部表情、身体姿态、动作细节",
        "audio": "描述性语言，使用丰富的形容词、副词，语速正常",
        "interference": "高，干扰组动作极其相似，需分辨程度副词或具体形容词"
    },
    "D": {
        "visual": "情境推断，依赖抽象线索或因果状态",
        "audio": "隐含逻辑，包含推测、因果关系，语速较快",
        "interference": "极高，干扰组无法直接通过视觉匹配，需结合上下文逻辑推理排除"
    }
}

# 能力标签对应（与官方统一定义对齐）
ABILITY_TAGS = [
    "显性事实理解（关键词识别）",
    "显性事实理解（关键词识别）",
    "多信息整合",
    "干扰抑制 / 语用推断（否定与纠错）"
]

def parse_args():
    parser = argparse.ArgumentParser(description="Generate Listening Drag & Drop questions")
    parser.add_argument("--knowledge", required=True, help="知识点列表，分号分隔")
    parser.add_argument("--difficulty", required=True, choices=["A", "B", "C", "D"], help="难度等级")
    parser.add_argument("--names", required=True, help="人名列表，逗号分隔")
    parser.add_argument("--shuffled_names", required=True, help="打乱后的人名列表，逗号分隔")
    return parser.parse_args()

def load_word_list() -> List[str]:
    """加载L1单词库"""
    try:
        with open(WORD_LIST_PATH, "r", encoding="utf-8") as f:
            data = json.load(f)
            return [word["word"].lower() for word in data["words"]]
    except Exception as e:
        print(f"警告：加载单词库失败，跳过超纲校验：{str(e)}", file=sys.stderr)
        return []

def validate_knowledge_with_wordlist(knowledge: str, word_list: List[str]) -> bool:
    """校验知识点是否在单词库中"""
    if not word_list:
        return True
    knowledge_words = [item.split()[0].lower() for item in knowledge.split(";") if item.strip()]
    out_of_range = []
    for word in knowledge_words:
        if word not in word_list:
            out_of_range.append(word)
    if out_of_range:
        print(f"错误：以下知识点超纲，不在L1单词库中：{','.join(out_of_range)}", file=sys.stderr)
        return False
    return True

def validate_input(args) -> bool:
    """校验输入参数合法性"""
    names = args.names.split(",")
    shuffled_names = args.shuffled_names.split(",")
    if len(names) != 5 or len(shuffled_names) !=5:
        print("错误：人名列表必须包含5个名字", file=sys.stderr)
        return False
    if set(names) != set(shuffled_names):
        print("错误：打乱后的人名列表与原列表不一致", file=sys.stderr)
        return False
    if not args.knowledge.strip():
        print("错误：知识点不能为空", file=sys.stderr)
        return False
    # 超纲校验
    word_list = load_word_list()
    if not validate_knowledge_with_wordlist(args.knowledge, word_list):
        return False
    return True

def generate_prompt(args) -> str:
    """生成生产prompt"""
    difficulty_rule = DIFFICULTY_RULES[args.difficulty]
    return f"""
你是资深K12英语教研专家，严格按照以下要求生成听力拖拽题：
## 难度要求
{difficulty_rule}
## 输入参数
知识点：{args.knowledge}
人名列表：{args.names}
打乱后人名列表：{args.shuffled_names}
## 题目要求
1. 共4道小题，对应P1-P4，P5为干扰项
2. P3-P5为干扰组，共享一个核心特征，仅通过细节区分
3. Q4必须包含纠错逻辑（如No, sorry/I mean）或否定逻辑
4. 角色从{CHARACTERS}中选择2个不同的角色
5. 对话要求自然日常，避免统一使用祈使句，可使用陈述句、疑问句等多种形式，参考示例：
One - 
Girl: Look! The girl with the red shirt is happy.
Boy: Yes. That’s Julia.
Two - 
Girl: Who is the girl in the yellow skirt?
Boy: That’s Sue.
Three -
Girl: The girl with the blue watch is pretty.
Boy: That’s May.
Four -
Girl: Is the boy in the white shirt Dan?
Boy: No, sorry. That’s Peter.
## 输出格式
严格按照以下格式输出，不要添加任何额外内容：
【听力文本】
One - 
[角色1]: [对话指向P1]
[角色2]: Yes. That’s [Name1].
Two - 
[角色1]: [对话指向P2]
[角色2]: That’s [Name2].
Three -
[角色1]: [对话指向P3，从干扰组筛选]
[角色2]: That’s [Name3].
Four -
[角色1]: [对话指向P4，包含纠错逻辑]
[角色2]: Oh! That’s [Name4].
【图片描述】
风格: 卡通，简单线条
场景：[符合知识点的场景]
 [人物1描述] - [动作/特征] ；
 [人物2描述] - [动作/特征] ；
 [人物3描述] - [动作/特征] ；
 [人物4描述] - [动作/特征] ；
 [人物5描述] - [动作/特征] 。
【人物顺序】
[{args.names.split(',')}]
【选项排序】
[{args.shuffled_names.split(',')}]
【任务描述】
You will hear [角色1] and [角色2] talking about [场景相关内容]. Drag the names to the correct people.
【选项数量】
5
【答案】
[Name1在打乱列表中的索引,Name2索引,Name3索引,Name4索引]，例如03,01,00,04
【能力项】
题目1 - 显性事实理解（关键词识别）- [Q1考察的核心知识点关键词，例如：blue T-shirt]
题目2 - 显性事实理解（关键词识别）- [Q2考察的核心知识点关键词，例如：red dress]
题目3 - 多信息整合 - [Q3考察的组合知识点关键词，例如：pink hairband + white skirt]
题目4 - 干扰抑制 / 语用推断（否定与纠错）- [Q4考察的核心知识点关键词，例如：green cap]
"""

def call_llm(prompt: str) -> str:
    """调用大模型生成内容（此处留空，实际使用时替换为对应模型调用逻辑）"""
    # 实际生产环境替换为模型API调用
    print("请输入模型生成的内容：")
    return sys.stdin.read()

def validate_output(output: str, original_names: List[str], shuffled_names: List[str]) -> bool:
    """校验输出格式合法性"""
    # 检查必填字段是否存在
    required_sections = ["【听力文本】", "【图片描述】", "【人物顺序】", "【选项排序】", "【任务描述】", "【选项数量】", "【答案】", "【能力项】"]
    for section in required_sections:
        if section not in output:
            print(f"错误：缺少必填字段{section}", file=sys.stderr)
            return False
    # 校验人物顺序格式
    person_order_match = re.search(r"【人物顺序】\s*\[(.*)\]", output)
    if not person_order_match:
        print("错误：人物顺序格式不正确", file=sys.stderr)
        return False
    person_order = [name.strip().strip("'\"") for name in person_order_match.group(1).split(",")]
    if person_order != original_names:
        print(f"错误：人物顺序与输入人名列表不一致，预期：{original_names}，实际：{person_order}", file=sys.stderr)
        return False
    # 校验选项排序格式
    option_order_match = re.search(r"【选项排序】\s*\[(.*)\]", output)
    if not option_order_match:
        print("错误：选项排序格式不正确", file=sys.stderr)
        return False
    option_order = [name.strip().strip("'\"") for name in option_order_match.group(1).split(",")]
    if option_order != shuffled_names:
        print(f"错误：选项排序与输入打乱后的人名列表不一致，预期：{shuffled_names}，实际：{option_order}", file=sys.stderr)
        return False
    # 校验答案格式
    answer_match = re.search(r"【答案】\s*([\d,]+)", output)
    if not answer_match:
        print("错误：答案格式不正确", file=sys.stderr)
        return False
    answers = answer_match.group(1).split(",")
    if len(answers) !=4:
        print("错误：答案必须包含4个索引", file=sys.stderr)
        return False
    for ans in answers:
        if not ans.isdigit() or int(ans) <0 or int(ans)>=5:
            print(f"错误：答案索引{ans}不合法", file=sys.stderr)
            return False
    # 校验答案对应的人名是否在列表中
    for ans in answers:
        idx = int(ans)
        if idx >= len(shuffled_names):
            print(f"错误：答案索引{idx}超出范围", file=sys.stderr)
            return False
    # 校验能力项格式
    ability_pattern = r"【能力项】\s*题目1 - .* - .*\s*题目2 - .* - .*\s*题目3 - .* - .*\s*题目4 - .* - .*"
    if not re.search(ability_pattern, output, re.DOTALL):
        print("错误：能力项格式不正确，必须按以下格式分行列出：题目N - 能力标签 - 考察知识点", file=sys.stderr)
        return False
    return True

def main():
    args = parse_args()
    if not validate_input(args):
        sys.exit(1)
    prompt = generate_prompt(args)
    output = call_llm(prompt)
    original_names = args.names.split(",")
    shuffled_names = args.shuffled_names.split(",")
    if validate_output(output, original_names, shuffled_names):
        print(output)
        sys.exit(0)
    else:
        sys.exit(1)

if __name__ == "__main__":
    main()
