#!/usr/bin/env python3
"""
408考研学习 v7 - 思路核对版
先做后对：做习题→AI推理→思路核对→答案+思路都对才✅
支持Word(.docx)和文本(.txt)两种格式
"""
import os, json, time, urllib.request, re, subprocess
from datetime import datetime, timedelta

STATE_FILE = "/root/downloads/.study_state.json"
SENT_FILE  = "/root/downloads/.sent_questions.txt"
BASE_DIR   = "/root/downloads"
LAST_STUDY_FILE = "/root/downloads/.last_study_time.txt"
TAVILY_KEY = "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"
ZHENTI_PDF = f"{BASE_DIR}/09-24高清无水印无错误408真题合集.pdf"
MINIMAX_URL = "http://guizhouyun.site:2177/v1/chat/completions"
MINIMAX_KEY = "sk-QgA0QbvP3lkJ9Q5R2bCeC92f0bE14f30B84812677aFb37Db"

# 章节目录：(科目, 文件路径, 文件类型, ChTag, ChName, 习题关键词, 答案关键词)
# 文件类型: "docx" 或 "txt"
CHAPTERS = [
    # 计算机网络(Word)
    ("计算机网络", f"{BASE_DIR}/王道计网.docx", "docx",
     "1.1", "计算机网络概述", "1.1.7", "1.1.8"),
    ("计算机网络", f"{BASE_DIR}/王道计网.docx", "docx",
     "1.2", "计算机网络体系结构", "1.2.4", "1.2.5"),
    ("计算机网络", f"{BASE_DIR}/王道计网.docx", "docx",
     "2.1", "物理层", "2.1.4", "2.1.5"),
    # 计算机组成原理(Text)
    ("计算机组成原理", f"{BASE_DIR}/王道计组.txt", "txt",
     "1.1", "计算机系统概述", "习题精选", "答案"),
]

NOTE_FILES = {
    "计算机网络":    f"{BASE_DIR}/计算机网络_笔记.md",
    "数据结构":      f"{BASE_DIR}/数据结构_笔记.md",
    "操作系统":      f"{BASE_DIR}/操作系统_笔记.md",
    "计算机组成原理":f"{BASE_DIR}/计算机组成原理_笔记.md",
}
WRONG_FILES = {
    "计算机网络":    f"{BASE_DIR}/错题本_计算机网络.md",
    "计算机组成原理":f"{BASE_DIR}/错题本_计算机组成原理.md",
    "数据结构":      f"{BASE_DIR}/错题本_数据结构.md",
    "操作系统":      f"{BASE_DIR}/错题本_操作系统.md",
}

for f in list(NOTE_FILES.values()) + list(WRONG_FILES.values()):
    if not os.path.exists(f) or os.path.getsize(f) < 5:
        with open(f, "w", encoding="utf-8") as ff:
            ff.write(f"# {os.path.basename(f).replace('.md','')}\n\n")

def load_state():
    return json.load(open(STATE_FILE)) if os.path.exists(STATE_FILE) \
           else {"ch_idx": 0, "learned": {}}
def save_state(s): open(STATE_FILE,"w").write(json.dumps(s))

# ============== 文件读取 ==============
def read_docx_para(path, start, end):
    from docx import Document
    doc = Document(path)
    return "\n".join([p.text.strip() for p in doc.paragraphs[start:end] if p.text.strip()])

def read_txt_chapters(path, kw_start, kw_end=None):
    """txt文件：搜索关键词范围"""
    with open(path, encoding="utf-8") as f:
        lines = f.readlines()
    paras = [l.rstrip("\n") for l in lines]
    
    # 找起始位置
    start_idx = None
    for i, p in enumerate(paras):
        if kw_start in p and len(p) < 40:
            start_idx = i
            break
    if start_idx is None:
        return ""
    
    # 找结束位置
    if kw_end:
        end_idx = None
        for i, p in enumerate(paras[start_idx+1:], start_idx+1):
            if kw_end in p and len(p) < 40:
                end_idx = i
                break
        end_idx = end_idx or len(paras)
    else:
        end_idx = start_idx + 200
    
    return "\n".join(paras[start_idx:end_idx])

def find_para_indices_docx(path, kw):
    """docx: 找关键词所在段落索引"""
    from docx import Document
    doc = Document(path)
    for i, p in enumerate(doc.paragraphs):
        if kw in p.text and len(p.text) < 40:
            return i
    return None

def find_para_indices_txt(path, kw):
    """txt: 找关键词所在行索引"""
    with open(path, encoding="utf-8") as f:
        lines = f.readlines()
    for i, l in enumerate(lines):
        if kw in l and len(l.strip()) < 40:
            return i
    return None

# ============== MiniMax推理 ==============
def call_minimax(prompt, max_tokens=400):
    payload = json.dumps({
        "model": "MiniMax-M2.7",
        "messages": [{"role": "user", "content": prompt}],
        "max_tokens": max_tokens, "temperature": 0.3,
    }).encode()
    try:
        req = urllib.request.Request(MINIMAX_URL, data=payload,
            headers={"Authorization": f"Bearer {MINIMAX_KEY}", "Content-Type": "application/json"})
        with urllib.request.urlopen(req, timeout=20) as resp:
            result = json.loads(resp.read())
            msg = result["choices"][0]["message"]
            return msg.get("content","").strip() or msg.get("reasoning_content","").strip()[:300]
    except Exception as e:
        return f"[API错误: {e}]"

def reason_answer(question, knowledge):
    prompt = f"""知识点：{knowledge[:500]}
题目：{question[:200]}
请直接给出答案和关键推导（一句话）。
格式：答案：[X] 推导：..."""
    return call_minimax(prompt, 300)

def check_recent():
    """1小时内不重复学同一章节"""
    if os.path.exists(LAST_STUDY_FILE):
        try:
            with open(LAST_STUDY_FILE) as f:
                content = f.read().strip()
            if content:
                last = datetime.fromisoformat(content)
                if datetime.now() - last < timedelta(hours=1):
                    return True
        except (ValueError, OSError):
            pass
    return False

def mark_studied():
    with open(LAST_STUDY_FILE, "w") as f:
        f.write(datetime.now().isoformat())

def verify_reasoning(my_reasoning, ref_analysis, question, my_answer):
    import re
    # 提取参考答案中的关键短语
    # 格式："01.  B" 或 "答案：B"
    ref_ans_m = re.search(r'(?:^|\n)(?:\d+\.\s*)([A-D])', ref_analysis[:300])
    ref_ans = ref_ans_m.group(1) if ref_ans_m else None

    # 我的答案是否正确
    ans_ok = my_answer not in ['?', '', 'NONE'] and ref_ans and my_answer.upper().strip('.').replace(' ','') == ref_ans.upper()

    # 思路核对：提取参考答案前200字的核心关键词（去停用词）
    stop = {'的','是','在','和','了','有','与','为','用','对','以','也','就','这','那','都','而','但','或','及','等','当','将','可','能'}
    def get_words(text):
        return set(w for w in re.findall(r'[\u4e00-\u9fff]{2,6}', text) if w not in stop)
    my_words = get_words(my_reasoning)
    ref_words = get_words(ref_analysis[:300])
    overlap = len(my_words & ref_words)
    total = len(my_words | ref_words)
    sim = overlap / total if total > 0 else 0

    # 答案对+重叠词>=2个 → 思路正确
    reason_ok = ans_ok and sim >= 0.1 and overlap >= 2

    ans_sym = '✅' if ans_ok else '❌'
    reason_sym = '✅' if reason_ok else '❌'
    detail = '重叠' + str(overlap) + '词，相似度' + str(int(sim*100)) + '%'
    return '答案=' + ans_sym + ' 思路=' + reason_sym + ' | ' + detail



# ============== 搜索 ==============
def search_tavily(query):
    try:
        payload = json.dumps({"query": query, "search_depth": "advanced",
                             "max_results": 2, "include_answer": True}).encode()
        req = urllib.request.Request("https://api.tavily.com/search", data=payload,
            headers={"Authorization": f"Bearer {TAVILY_KEY}", "Content-Type": "application/json"})
        with urllib.request.urlopen(req, timeout=12) as resp:
            return json.loads(resp.read())
    except: return {}

def search_zhenti(keyword):
    try:
        txt = f"/tmp/zt{os.getpid()}.txt"
        subprocess.run(["pdftotext", "-f", "1", "-l", "40", "-search", keyword,
                       ZHENTI_PDF, txt], capture_output=True, timeout=15)
        if os.path.exists(txt) and os.path.getsize(txt) > 50:
            with open(txt) as f: r = f.read()
            os.unlink(txt)
            return r[:2500]
    except: pass
    return ""

# ============== 解析 ==============
def split_questions(text):
    if not text: return []
    parts = re.split(r'\n(?=\d{2}[.、])', text)
    result = []
    for p in parts:
        p = p.strip()
        if len(p) > 20:
            result.append(p)
    return result

def extract_ref_answers(text):
    # 匹配 "01.  B" 或 "答案：B" 格式
    ans = re.findall(r'(?:^|\n)(?:\d{2}[.、]\s*)([A-D][\.、]?)', text, re.I)
    return [a.replace('。','').replace('.','') for a in ans]

def parse_my_answer(text):
    m = re.search(r'答案[：:\s]*\[?([A-D])\]?', text, re.I)
    if m: return m.group(1).upper()
    m = re.search(r'\b([A-D])\b', text[:50])
    if m: return m.group(1).upper()
    return "?"

# ============== 追加文件 ==============
def append_note(subj, title, content):
    nf = NOTE_FILES.get(subj)
    ts = time.strftime("%Y-%m-%d %H:%M")
    with open(nf, "a", encoding="utf-8") as f:
        f.write(f"\n\n{'='*55}\n【{ts}】{subj} - {title}\n{'='*55}\n\n{content}")

def append_wrong(subj, title, content):
    nf = WRONG_FILES.get(subj)
    if not nf: return
    with open(nf, "a", encoding="utf-8") as f:
        f.write(f"\n\n{'='*55}\n【{time.strftime('%Y-%m-%d %H:%M')}】{subj} - {title}\n{'='*55}\n\n{content}")

# ============== 主逻辑 ==============
def main():
    # 防重复：1小时内不重复学同一章节
    if check_recent():
        print("[SKIP] 1小时内已学习过，跳过")
        return

    state = load_state()
    ch_idx = state.get("ch_idx", 0)
    if ch_idx >= len(CHAPTERS): ch_idx = 0

    subj, fpath, ftype, ch_tag, ch_name, ex_kw, ans_kw = CHAPTERS[ch_idx]

    print(f"\n[{time.strftime('%H:%M')}] {subj} {ch_tag} {ch_name}")

    # 1. 读取知识点
    if ftype == "docx":
        # 找知识点起始段落：ch_tag小节前一段
        idx_k = find_para_indices_docx(fpath, ch_tag)
        idx_ex = find_para_indices_docx(fpath, ex_kw)
        if idx_k and idx_ex:
            knowledge = read_docx_para(fpath, idx_k, idx_ex)
        else:
            knowledge = ""
        # 读习题和答案
        idx_ans = find_para_indices_docx(fpath, ans_kw)
        if idx_ex and idx_ans:
            exercises = read_docx_para(fpath, idx_ex, idx_ans)
            answers_raw = read_docx_para(fpath, idx_ans, idx_ans+300)
        else:
            exercises, answers_raw = "", ""
    else:
        # txt格式
        knowledge = read_txt_chapters(fpath, ch_tag, ex_kw)
        exercises = read_txt_chapters(fpath, ex_kw, ans_kw)
        answers_raw = read_txt_chapters(fpath, ans_kw)

    print(f"  [知识点] {len(knowledge)}字")
    print(f"  [习题] {len(exercises)}字")

    # 2. 拆分题目
    questions = split_questions(exercises)
    print(f"  [题目] 拆分出{len(questions)}道")

    # 3. 真题PDF加1道
    zhenti = search_zhenti(ch_name)
    zhenti_q = re.findall(r'(\d{2}[.、]\s*[\s\S]{20,300}?(?:A\.\s*[\s\S]{2,50}?){3})', zhenti)
    if zhenti_q:
        questions.append(f"[真题PDF] {zhenti_q[0]}")

    # 4. 解题技巧
    tips_r = search_tavily(f"{subj} {ch_name} 解题技巧 方法")
    tips = "\n".join([r.get("content","")[:300] for r in tips_r.get("results",[]) if r.get("content")])[:500]

    # 5. 每道题：推理→思路核对
    results = []
    ref_answers = extract_ref_answers(answers_raw)
    
    for i, q_text in enumerate(questions[:4], 1):
        if len(q_text) < 15: continue
        print(f"  [推理] 题目{i}...")
        my_text = reason_answer(q_text, knowledge)
        my_ans = parse_my_answer(my_text)
        print(f"  [我的答案] {my_ans}")

        # 思路核对（用参考答案的前300字）
        ref_segment = answers_raw[:500] if answers_raw else ""
        print(f"  [核对] 题目{i}思路...")
        verify = verify_reasoning(my_text, ref_segment, q_text, my_ans)
        print(f"  [核对结果] {verify[:80]}")
        results.append((q_text, my_text, my_ans, verify))
        print()

    # 6. 组装笔记
    note = f"## 📖 知识点\n{knowledge[:2000]}\n\n"
    if tips:
        note += f"## 💡 解题技巧\n{tips}\n\n"

    for i, (q_text, my_text, my_ans, verify) in enumerate(results, 1):
        note += f"## 📝 题目{i}\n```\n{q_text[:500]}\n```\n\n"
        note += f"### 我的推理\n{my_text[:400]}\n\n"

    note += f"## 🔍 参考答案\n{answers_raw[:800]}\n\n"
    note += "## ✅ 核对结果\n"
    for i, (q_text, my_text, my_ans, verify) in enumerate(results, 1):
        ref_a = ref_answers[i-1].upper().replace('。','').replace('.','')[0] if i-1 < len(ref_answers) and len(ref_answers[i-1])>0 else "?"
        # 从verify里提结果
        ans_ok = "✅" in verify[:50]
        reason_ok = "思路=✅" in verify
        note += f"- 题目{i}：答案={my_ans} {'✅' if ans_ok else '❌'} 思路={'✅' if reason_ok else '❌'}\n"
        note += f"  评委点评：{verify[:100]}\n"
        if not reason_ok:
            note += f"  → 思路有问题，记入错题本\n"

    # 7. 写错题本
    for i, (q_text, my_text, my_ans, verify) in enumerate(results, 1):
        ans_ok = "✅" in verify[:50]
        reason_ok = "思路=✅" in verify
        if not ans_ok or not reason_ok:
            wrong = f"## 题目{i}\n```\n{q_text[:400]}\n```\n## 我的推理\n{my_text[:300]}\n## 参考答案\n{answers_raw[:500]}\n## 评委点评\n{verify}\n## 正确思路\n（请根据参考答案补充）\n"
            append_wrong(subj, f"{ch_tag} {ch_name}", wrong)

    # 8. 保存
    state["ch_idx"] = ch_idx + 1
    state["learned"][subj] = state["learned"].get(subj, 0) + 1
    save_state(state)
    append_note(subj, f"{ch_tag} {ch_name}", note)
    mark_studied()

    correct = sum(1 for _,_,_,v in results if "✅" in v[:50])
    with open(f"{BASE_DIR}/.study_notify.txt", "w") as f:
        f.write(f"📚 **{subj}** {ch_tag}\n\n✅ 已学：{ch_name}\n📖 {len(knowledge)//100*100}字\n📝 {len(results)}道题\n✅ 思路全对：{correct}道\n💾 笔记已追加")
    print(f"\n完成！{len(results)}道题，{correct}道思路正确")

if __name__ == "__main__":
    main()
