#!/usr/bin/env python3
"""
单元挑战-听力审校脚本
审校范围：听力-P1~P7（排除P3）
检查项：
1. jsonData 存在且可解析
2. first/second 块结构完整性（category, type, questionSetID, questionSet）
3. 题目集合ID 一致性（jsonData vs 字段）
4. explanation 非空
5. ability 非空、格式规范
6. answer 索引不出界
7. 题目1/题目2字段非空且与jsonData一致
8. questionAudio/optionsImage 等资源命名格式
"""

import json, subprocess, os, sys

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
WORKSPACE = os.path.dirname(SCRIPT_DIR)
BITABLE_SCRIPT = os.path.join(WORKSPACE, "business_production/_shared/lark_bitable_operate_as_bot/scripts/operate_bitable.sh")
APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf"

TABLES = {
    "听力-P1-图片选择题": "tbliZAhcc9C43B23",
    "听力-P2-表格填空题": "tblzTLNH7f13uWQN",
    "听力-P4-短对话选择题": "tblVmeDtBDKsAEfz",
    "听力-P5-信息匹配题": "tblDssVmhGzc3UKd",
    "听力-P6-听力选图": "tbloiMcD0sBtGSTq",
    "听力-P7-听力拖拽": "tbly9SvPEa44k3yX",
}

def fetch_records(table_id):
    """Fetch all records from a table"""
    result = subprocess.run(
        ["bash", BITABLE_SCRIPT, "list_records", APP_TOKEN, table_id, "200"],
        capture_output=True, text=True, timeout=60
    )
    try:
        data = json.loads(result.stdout)
        if data.get("code") == 0:
            return data["data"]["items"]
    except (json.JSONDecodeError, KeyError) as e:
        print(f"  ⚠️ Fetch error for {table_id}: {e}")
    return []

def check_json_data(record):
    """Check jsonData field"""
    issues = []
    jd = record.get("fields", {}).get("jsonData")
    if not jd:
        issues.append(("🔴", "jsonData为空"))
        return issues, None
    try:
        parsed = json.loads(jd)
    except json.JSONDecodeError as e:
        issues.append(("🔴", f"jsonData JSON解析失败: {e}"))
        return issues, None
    return issues, parsed

def check_block(block, block_name, qs_id):
    """Check a question set block (first/second)"""
    issues = []
    if not block:
        issues.append(("🔴", f"{block_name}块为空"))
        return issues
    
    # Required fields
    for field in ["category", "type", "questionSetID"]:
        if not block.get(field):
            issues.append(("🔴", f"{block_name}缺少必填字段 '{field}'"))
    
    # Check type consistency
    category = block.get("category", "")
    qtype = block.get("type", "")
    if category != "listening":
        issues.append(("🔴", f"{block_name} category应为'listening'，实际为'{category}'"))
    
    # questionSetID consistency
    bid_qsid = block.get("questionSetID", "")
    if qs_id and bid_qsid != qs_id:
        issues.append(("🔴", f"{block_name} questionSetID({bid_qsid})与字段'题目集合 ID'({qs_id})不一致"))
    
    # Check questionSet array
    qs = block.get("questionSet", [])
    if not isinstance(qs, list):
        issues.append(("🔴", f"{block_name} questionSet不是数组"))
        return issues
    
    if qs_id == "000001":
        return issues  # Skip 000001 dummy data
    
    for i, q in enumerate(qs):
        # Check explanation
        if not q.get("explanation") or q.get("explanation", "").strip() == "":
            issues.append(("🔴", f"{block_name}[{i}]: explanation为空"))
        
        # Check ability
        ability = q.get("ability", [])
        if not ability:
            issues.append(("🔴", f"{block_name}[{i}]: ability为空"))
        else:
            # Check for non-standard ability labels
            for a in ability:
                if not isinstance(a, str) or not a.strip():
                    issues.append(("🔴", f"{block_name}[{i}]: ability含空值"))
                    continue
                # Check for suspicious patterns in ability tags
                if "｜" in a and any(kw in a for kw in ["听觉", "听力", "听辨"]):
                    pass  # OK - standard format
                elif "-" in a and len(a.split("-")[0].strip()) < 10:
                    # Suspicious: tags with trailing dash content like "显性事实理解（单句信息点抓取）- listen"
                    issues.append(("🟡", f"{block_name}[{i}]: ability标签格式可疑 '{a}'"))
        
        # Check answer index bounds
        answer = q.get("answer", [])
        if isinstance(answer, list):
            for idx in answer:
                if not isinstance(idx, int):
                    issues.append(("🔴", f"{block_name}[{i}]: answer索引非数字"))
        
        # Check options/similar fields exist
        has_options = (
            q.get("options") or 
            q.get("optionsImage") or
            q.get("optionList") or
            q.get("questionAudio")
        )
        if not has_options:
            issues.append(("🔴", f"{block_name}[{i}]: 无题目选项/questionAudio"))
    
    # Check special fields per type
    qtype_val = block.get("type", "")
    if qtype_val == "listening_tableCloze":
        if not block.get("textDesc"):
            issues.append(("🟡", f"{block_name}: 缺少textDesc"))
        if not block.get("textAudio"):
            issues.append(("🔴", f"{block_name}: 缺少textAudio"))
        if not block.get("textBody"):
            issues.append(("🟡", f"{block_name}: 缺少textBody"))
    elif qtype_val == "listening_matchInfo":
        if not block.get("textDesc"):
            issues.append(("🟡", f"{block_name}: 缺少textDesc"))
        if not block.get("textAudio"):
            issues.append(("🔴", f"{block_name}: 缺少textAudio"))
        if not block.get("optionSetList"):
            issues.append(("🔴", f"{block_name}: 缺少optionSetList"))
        if block.get("answerSet") is None:
            issues.append(("🔴", f"{block_name}: 缺少answerSet"))
    elif qtype_val == "listening_drag":
        if not block.get("textDesc"):
            issues.append(("🟡", f"{block_name}: 缺少textDesc"))
        if not block.get("textAudio"):
            issues.append(("🔴", f"{block_name}: 缺少textAudio"))
        for i, q in enumerate(block.get("questionSet", [])):
            if not q.get("imageInfo") or not q["imageInfo"].get("questionImg"):
                issues.append(("🔴", f"{block_name}[{i}]: 缺少questionImg"))
            if not q.get("optionList"):
                issues.append(("🔴", f"{block_name}[{i}]: 缺少optionList"))
    
    return issues

def check_text_fields(record, table_name, parsed_json):
    """Check text fields are consistent with jsonData"""
    issues = []
    fields = record.get("fields", {})
    
    # Ambiguous fields depending on table
    text1_keys = ["题目1 完整配置", "题目1", "题目完整配置"]
    text2_keys = ["题目2 完整配置", "题目2"]
    
    text1 = None
    text2 = None
    for k in text1_keys:
        if fields.get(k):
            text1 = fields[k]
            break
    for k in text2_keys:
        if fields.get(k):
            text2 = fields[k]
            break
    
    if not text1:
        issues.append(("🔴", "题目1字段为空"))
    if not text2 and parsed_json and parsed_json.get("second") and parsed_json["second"].get("questionSet"):
        issues.append(("🟡", "题目2字段为空（但jsonData中有second块）"))
    
    return issues

def main():
    total_issues = {}
    total_records = 0
    filled_records = 0
    
    for table_name, table_id in TABLES.items():
        print(f"\n{'='*60}")
        print(f"📋 审校表: {table_name} ({table_id})")
        print(f"{'='*60}")
        
        records = fetch_records(table_id)
        table_issues = {}
        
        for rec in records:
            rid = rec.get("record_id", "unknown")
            fields = rec.get("fields", {})
            ds = fields.get("dataStatus")
            qs_id = fields.get("题目集合 ID")
            
            # Skip empty records
            if not ds or ds != "0" or not fields.get("jsonData"):
                continue
            
            total_records += 1
            issues = []
            
            # 1. Check jsonData
            jd_issues, parsed = check_json_data(rec)
            issues.extend(jd_issues)
            
            if parsed:
                filled_records += 1
                # 2. Check first block
                first = parsed.get("first", {})
                issues.extend(check_block(first, "first", qs_id))
                
                # 3. Check second block
                second = parsed.get("second", {})
                # Only check if second has content (not empty object {})
                if second and second.get("questionSet"):
                    issues.extend(check_block(second, "second", qs_id))
                
                # 4. Check text fields
                issues.extend(check_text_fields(rec, table_name, parsed))
            
            if issues:
                table_issues[rid] = issues
        
        # Summary for this table
        err_count = sum(1 for v in table_issues.values() for s,_ in v if s == "🔴")
        warn_count = sum(1 for v in table_issues.values() for s,_ in v if s == "🟡")
        
        print(f"\n  📊 记录数: {len(records)}, 有内容: {len([r for r in records if r['fields'].get('jsonData')])}")
        print(f"  🔴 严重错误: {err_count} | 🟡 警告: {warn_count}")
        
        if table_issues:
            for rid, iss in table_issues.items():
                print(f"\n  ┌─ Record: {rid}")
                for severity, msg in iss:
                    print(f"  │ {severity} {msg}")
                print(f"  └─")
        else:
            print(f"  ✅ 无问题")
        
        total_issues[table_name] = table_issues
    
    # Overall summary
    print(f"\n{'='*60}")
    print(f"📊 总体汇总")
    print(f"{'='*60}")
    all_err = sum(sum(1 for _, iss in t.values() for s,_ in iss if s == "🔴") for t in total_issues.values())
    all_warn = sum(sum(1 for _, iss in t.values() for s,_ in iss if s == "🟡") for t in total_issues.values())
    total_tables_with_issues = sum(1 for t in total_issues.values() if t)
    
    print(f"  有内容的记录数: {filled_records}")
    print(f"  有问题记录数: {sum(len(v) for v in total_issues.values())}")
    print(f"  🔴 严重错误: {all_err} | 🟡 警告: {all_warn}")
    
    if all_err == 0 and all_warn == 0:
        print(f"\n  ✅ 全部通过审校！")
    else:
        print(f"\n  ⚠️ 需要修复 {all_err} 个严重错误和 {all_warn} 个警告")

if __name__ == "__main__":
    main()
