#!/usr/bin/env python3
"""
步骤5：用户反馈问题优先级判定与排序

基于《用户反馈问题优先级判断文档》，对步骤4归纳的问题簇进行优先级评估：
- 先按问题严重程度定基础优先级（P0-P3）
- 再按出现概率和影响范围动态调整
- 最终按优先级排序输出（P0 > P1 > P2 > P3）
"""

import re, sys, json
from typing import List, Dict, Tuple, Optional

# ============================================================
# 优先级判定规则（基于优先级判断文档）
# ============================================================

# P0 关键词：完全阻断使用、核心功能崩溃
P0_KEYWORDS = {
    "crash": r'闪退|崩溃|卡死|卡住|白屏|黑屏|花屏|无法启动|打不开|进不去|登不上|完全.*进|完全.*打|完全.*不能|无法.*进入|无法.*操作|无法.*解锁|解锁不了|不能解锁|无法.*点击|点击.*没反应|点击.*无效|无法.*使用|无.*反应|点了.*没.*反应|点.*没反应|界面.*卡|卡.*不动|卡.*界面|卡.*屏|强制.*关闭|只能.*强制|只能.*退出|退出.*还是.*闪退|还是.*闪退|重启.*还是|删.*重装|重装.*还是|无法.*退出|退出不了|不能退出|动不了|卡.*动不了|一直.*卡|更新.*卡|更新.*动不了',
    "core": r'(知识巩固|跟读|录音).*(进不去|没反应|无法|不工作|崩溃)|(核心功能|主线).*(崩溃|卡死|卡住|无法推进|完全.*卡)',
    "payment": r'(付费|购买|充值).*(无法|不到账|失败|不能用)|(钱|金额).*(不对|错误|问题)',
    "server": r'(服务器|全国|所有.*用户|全体).*(挂了|宕机|无法连接|登不上)',
    "data_loss": r'(数据.*(丢失|没记录|白学|全没|消失))|(没.*获取到.*(知识点|数据))',
}

# P1 关键词：功能明显异常、学习效果打折扣
P1_KEYWORDS = {
    "learning_func": r'(音频|声音|播放).*(异常|没声音|无声|不对|听不到|没有|无)|(听不到|不能听|无音|没音|没声)',
    "score_judge": r'(判分|打分|识别|评测).*(不对|不准|错误|异常)|(明明.*对.*系统.*(说|判断).*不对)',
    "progress": r'(进度|版本|课程.*序号|单元.*序号).*(显示|记录|保存).*(错|不对|失败|异常)',
    "content": r'(题目|选项|图片|显示).*(有问题|异常|错位|看不见|看不清)|(内容|关卡).*(异常|有问题)',
    "reward": r'(奖励|贴纸|成就).*(没发|没到账|异常|没有)',
    "data_error": r'(学习.*(数据|时长|记录)).*(不对|错误|异常|不准确)',
    "update": r'(更新|版本).*(失败|无法|出问题|异常)',
}

# P2 关键词：不影响核心功能，但体验有瑕疵
P2_KEYWORDS = {
    "ui_display": r'(界面|UI|显示|排版|文案).*(偶尔|小|异常|不对|错位|不美观|截断)|(偶尔.*(不显示|显示.*异常))',
    "animation": r'(动画|动效).*(不流畅|异常|缺失|卡顿)',
    "lag": r'(按钮|点击|操作).*(延迟|卡顿|等一下|反应慢|不灵敏)',
    "refresh_fix": r'(刷新|重启|重进).*就.*(好|恢复|正常)',
    "minor_order": r'(顺序).*(不对|怪|反了)',
}

# P3 关键词：几乎不影响使用
P3_KEYWORDS = {
    "cosmetic": r'(图标|颜色|色系|分辨率).*(模糊|不搭|略低|不统一)',
    "typo": r'(错别字|文字.*错误)',
    "perf_minor": r'(帧率|加载).*(慢.*秒|下降|略慢)',
    "audio_bg": r'(背景音乐|音量).*(不统一|异常)',
}


# 出现概率特征词
FREQUENCY_PATTERNS = {
    "必现": {
        "keywords": r'每次|必现|100%|稳定.*复现|总能|一直.*出现|每次.*都|始终',
        "weight": 1.0,
    },
    "高概率": {
        "keywords": r'经常|十.*次.*[89八]|频繁|大部分.*时候|经常.*出现',
        "weight": 0.8,
    },
    "中概率": {
        "keywords": r'(偶尔|有时候|有时|时不时)',
        "weight": 0.5,
    },
    "低概率": {
        "keywords": r'很少|偶尔.*一次|个别|不多|少见|十.*次.*[123]',
        "weight": 0.3,
    },
    "偶现": {
        "keywords": r'(偶现|特殊.*条件|特定.*机型|特定.*操作|说不清|不知道怎么.*触发|极.*少见)',
        "weight": 0.1,
    },
}

# 影响范围特征词
SCOPE_PATTERNS = {
    "全部用户": {
        "keywords": r'所有.*用户|每个.*用户|全部.*用户|全国|全体',
        "adjust": +1,
    },
    "大部分用户": {
        "keywords": r'大部分|多数.*用户|很多.*用户|普遍',
        "adjust": 0,
    },
    "部分用户": {
        "keywords": r'部分.*用户|有些|某种.*机型|某个.*平台|iOS.*端|Android.*端|手机',
        "adjust": 0,
    },
    "极少数用户": {
        "keywords": r'极少数|个例|个别.*用户|只有.*一个|就.*遇到|就.*发现',
        "adjust": -1,
    },
}


def determine_base_priority(text: str) -> int:
    """
    根据消息内容判定基础优先级（0=P0, 1=P1, 2=P2, 3=P3）
    返回: (priority_level, reason)
    """
    text_lower = text.lower()
    
    # 先按级别从高到低匹配
    for level_name, patterns in [("P0", P0_KEYWORDS), ("P1", P1_KEYWORDS)]:
        for category, pattern in patterns.items():
            if re.search(pattern, text_lower):
                return (0 if level_name == "P0" else 1), f"匹配{level_name}规则：{category}"
    
    for category, pattern in P2_KEYWORDS.items():
        if re.search(pattern, text_lower):
            return 2, f"匹配P2规则：{category}"
    
    for category, pattern in P3_KEYWORDS.items():
        if re.search(pattern, text_lower):
            return 3, f"匹配P3规则：{category}"
    
    # 默认：无法判定 → P2（一般问题）
    return 2, "无法精确匹配，默认归为P2"


def detect_frequency(text: str) -> Tuple[str, float]:
    """检测出现频率"""
    for freq_label, info in FREQUENCY_PATTERNS.items():
        if re.search(info["keywords"], text):
            return freq_label, info["weight"]
    return "未知", 0.5  # 默认中等


def detect_scope(text: str) -> Tuple[str, int]:
    """检测影响范围"""
    for scope_label, info in SCOPE_PATTERNS.items():
        if re.search(info["keywords"], text):
            return scope_label, info["adjust"]
    return "未知", 0  # 默认不调整


def compute_final_priority(cluster_msgs: List) -> Dict:
    """
    综合判定优先级
    
    输入: cluster_msgs - 问题簇的消息列表 [message_id, sender_name, msg_type, content, ...]
    输出: {
        "priority": "P0" | "P1" | "P2" | "P3",
        "base_priority": int,  # 0-3
        "frequency": str,
        "scope": str,
        "reasoning": str,  # 判定理由
        "emoji": str,
        "deadline": str,
    }
    """
    # 收集所有消息文本
    all_text = " ".join(str(m[3]) or "" for m in cluster_msgs)
    all_text = re.sub(r'\[Image:[^\]]+\]', '', all_text)
    
    # 1. 基础优先级
    base_level, base_reason = determine_base_priority(all_text)
    
    # 2. 频率判定
    freq_label, freq_weight = detect_frequency(all_text)
    
    # 3. 影响范围
    scope_label, scope_adjust = detect_scope(all_text)
    
    # 4. 综合调整
    # 4a. 频率调整：必现→ 升级；偶现→ 降级（最多降一级）
    freq_adjust = 0
    if freq_weight >= 0.8:  # 必现或高概率
        if base_level > 0:
            freq_adjust = -1  # 升级（level 数减小）
    elif freq_weight <= 0.3:  # 低概率或偶现
        if base_level < 3:
            freq_adjust = +1  # 降级（level 数增大，最多降一级）
    
    # 4b. 影响范围调整
    # scope_adjust 已经定义：全部用户+1级（升级），极少数用户-1级（降级）
    
    total_adjust = freq_adjust + scope_adjust
    final_level = base_level + total_adjust
    
    # 夹紧到 0-3 范围
    final_level = max(0, min(3, final_level))
    
    priority_labels = ["P0", "P1", "P2", "P3"]
    emojis = ["🔴", "🟠", "🟡", "🟢"]
    deadlines = [
        "2小时内处理，当天解决",
        "3天内修复上线",
        "当周排期修复，1周内解决",
        "不单独排期，有空再修",
    ]
    
    # 生成判定理由
    reasons = [f"基础优先级: {priority_labels[base_level]}（{base_reason}）"]
    if freq_label != "未知":
        reasons.append(f"出现频率: {freq_label}")
    if scope_label != "未知":
        reasons.append(f"影响范围: {scope_label}")
    if total_adjust < 0:
        reasons.append(f"动态调整: 升级 {abs(total_adjust)} 级")
    elif total_adjust > 0:
        reasons.append(f"动态调整: 降级 {total_adjust} 级")
    if total_adjust == 0 and base_level != final_level:
        reasons.append("动态调整: 维持原级")
    
    return {
        "priority": priority_labels[final_level],
        "base_priority": base_level,
        "base_label": priority_labels[base_level],
        "frequency": freq_label,
        "scope": scope_label,
        "reasoning": "；".join(reasons),
        "emoji": emojis[final_level],
        "deadline": deadlines[final_level],
    }


def sort_by_priority(clusters_with_priority: List[Dict]) -> List[Dict]:
    """
    按优先级排序：P0 > P1 > P2 > P3
    
    同优先级内按最早消息时间排序
    """
    return sorted(clusters_with_priority, key=lambda x: (
        x.get("priority_info", {}).get("base_priority", 2),  # 先按优先级
        x.get("earliest_time", "9999"),  # 再按时间
    ))


# ============================================================
# 自测
# ============================================================
if __name__ == "__main__":
    # 测试用例
    test_cases = [
        {
            "name": "P0: App崩溃",
            "msgs": [
                [None, "测试员", "text", "孩子刚打开App就闪退了，完全无法使用", None, None, None, None],
            ],
        },
        {
            "name": "P1: 音频问题",
            "msgs": [
                [None, "测试员", "text", "iOS端关卡内L1 3-2组件无音频，每次进都这样", None, None, None, None],
            ],
        },
        {
            "name": "P2: UI显示偶尔问题",
            "msgs": [
                [None, "测试员", "text", "大地图卡片偶尔不显示，刷新一下就好了", None, None, None, None],
            ],
        },
        {
            "name": "P1→P0: 必现的数据丢失",
            "msgs": [
                [None, "测试员", "text", "每次完成关卡后学习数据都不记录，100%复现，等于白学", None, None, None, None],
            ],
        },
        {
            "name": "P1→P2: 偶现的音频问题",
            "msgs": [
                [None, "测试员", "text", "偶现跟读音频播放异常，特定机型才出现，概率很低", None, None, None, None],
            ],
        },
    ]
    
    for tc in test_cases:
        result = compute_final_priority(tc["msgs"])
        print(f"\n{'='*60}")
        print(f"测试: {tc['name']}")
        print(f"结果: {result['emoji']} {result['priority']} | {result['reasoning']}")
        print(f"时限: {result['deadline']}")
