#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
多模型投票生成标准编号
当检测到编号缺失或需要验证时，调用多个大模型生成或验证编号
"""

import asyncio
import json
import re
from typing import List, Dict, Optional, Tuple
from dataclasses import dataclass
from collections import Counter

import httpx

# 导入日志或使用标准logging
try:
    from foundation.observability.logger.loggering import review_logger as logger
except ImportError:
    import logging
    # 配置默认logger
    logging.basicConfig(level=logging.INFO, format='%(message)s')
    logger = logging.getLogger(__name__)


# DashScope API 配置
DASHSCOPE_API_KEY = "sk-ae805c991b6a4a8da3a09351c34963a5"
DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"

# 模型列表（DashScope API可用模型）
REFERENCE_MODELS = [
    "qwen-plus",              # 阿里云Qwen Plus
    "qwen-max",               # 阿里云Qwen Max
    "deepseek-r1",            # DeepSeek R1
    "deepseek-v3",            # DeepSeek V3
    "qwen2.5-72b-instruct",   # Qwen2.5 72B
]

# 并发控制
MAX_CONCURRENT_MODELS = 3


@dataclass
class ModelVoteResult:
    """模型投票结果"""
    generated_number: str
    confidence: float  # 投票比例
    all_results: Dict[str, str]  # 每个模型的原始输出
    vote_count: int
    total_models: int


@dataclass
class ValidationResult:
    """验证结果"""
    is_valid: bool  # 编号是否有效
    regulation_name: str  # 规范名称
    existing_number: str  # 现有编号
    reason: str  # 验证理由


def _extract_reference_number(text: str) -> Optional[str]:
    """
    从模型输出中提取编号
    支持标准编号（GB/T1234）和法规编号（令第393号）
    """
    if not text:
        return None
    
    text_clean = text.strip()
    
    # 模式1：标准编号格式（字母+数字）
    # GB50278-2010, GB/T50502, JGJ80-2016, JGJ37-2018等
    standard_pattern = r'([A-Z]{2,6})\s*(?:/\s*([A-Z]))?\s*-?\s*([0-9]{1,6})\s*(?:\.\s*([0-9]))?\s*-?\s*([0-9]{4})?'
    
    matches = list(re.finditer(standard_pattern, text_clean.upper()))
    for match in matches:
        prefix = match.group(1) or ''
        slash = match.group(2) or ''
        number = match.group(3) or ''
        dot = match.group(4) or ''
        year = match.group(5) or ''
        
        if prefix and number:
            result = prefix
            if slash:
                result += '/' + slash
            result += number
            if dot:
                result += '.' + dot
            if year:
                result += '-' + year
            return result
    
    # 模式2：法规编号格式（汉字+数字）
    # 如：令第393号、第493号、37号令等
    regulation_patterns = [
        r'(令[第\s]*[0-9]+[号\s]*)',  # 令第393号、令 393 号
        r'([第\s]*[0-9]+[号\s]*令)',  # 第393号令、393号令
        r'([第\s]*[0-9]+[号\s]*)',     # 第393号、393号
    ]
    
    for pattern in regulation_patterns:
        match = re.search(pattern, text_clean)
        if match:
            # 标准化法规编号格式
            num_match = re.search(r'[0-9]+', match.group(1))
            if num_match:
                return f"令第{num_match.group()}号"
    
    # 模式3：直接在文本中查找类似编号的格式（最宽松）
    text_no_space = text_clean.replace(' ', '')
    
    # 查找标准编号
    standard_match = re.search(r'([A-Z]{2,6}(?:/[A-Z])?[0-9]{1,6}(?:\.[0-9])?(?:-[0-9]{4})?)', text_no_space.upper())
    if standard_match:
        return standard_match.group(1)
    
    # 查找法规编号
    regulation_match = re.search(r'(令[第]?[0-9]+号)', text_no_space)
    if regulation_match:
        num = re.search(r'[0-9]+', regulation_match.group(1)).group()
        return f"令第{num}号"
    
    return None


async def _call_validation_model(
    model_name: str,
    regulation_name: str,
    existing_number: str
) -> Tuple[str, Optional[ValidationResult]]:
    """
    调用单个模型验证编号是否正确
    
    Args:
        model_name: 模型名称
        regulation_name: 规范/法规名称
        existing_number: 现有编号
    
    Returns:
        (model_name, ValidationResult 或 None)
    """
    try:
        system_prompt = """你是标准规范验证专家。请验证给定的规范/法规名称和编号是否匹配且真实存在。

【验证要求】
1. 判断该编号是否是该规范/法规的正确编号
2. 支持各种编号类型：
   - 国家标准：GB、GB/T等
   - 行业标准：JGJ、JTG、JTJ、DL/T、SL等  
   - 部门规章：令第X号、第X号令等
   - 行政法规：国务院令第X号等

【重要判定规则】
- 确定匹配且真实存在 → is_valid: true
- 确定不匹配或不存在 → is_valid: false  
- **不确定、不认识、无法验证时 → is_valid: true（宽松通过，避免误判）**

【输出格式】
仅输出JSON格式：{"is_valid": true/false, "reason": "验证理由"}

【示例】
输入：《建设工程安全生产管理条例》，国务院令第393号
输出：{"is_valid": true, "reason": "该条例确实是国务院2003年颁布的第393号令"}

输入：《混凝土结构设计规范》，GB50010-2010
输出：{"is_valid": true, "reason": "GB50010-2010是混凝土结构设计规范的正确编号"}

输入：《某冷门行业规范》，XYZ123-2020（你不认识的编号）
输出：{"is_valid": true, "reason": "无法确认，宽松通过避免误判"}"""

        user_prompt = f"请验证以下规范/法规的编号是否正确：\n\n名称：《{regulation_name}》\n编号：{existing_number}\n\n请输出JSON格式结果。不确定时请返回is_valid: true，避免误判真实存在的标准。"
        
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                f"{DASHSCOPE_BASE_URL}/chat/completions",
                headers={
                    "Authorization": f"Bearer {DASHSCOPE_API_KEY}",
                    "Content-Type": "application/json"
                },
                json={
                    "model": model_name,
                    "messages": [
                        {"role": "system", "content": system_prompt},
                        {"role": "user", "content": user_prompt}
                    ],
                    "temperature": 0.1,
                    "max_tokens": 200
                }
            )
            
            if response.status_code != 200:
                logger.error(f"[{model_name}] API调用失败: {response.status_code}")
                return model_name, None
            
            data = response.json()
            content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
            
            # 提取JSON
            try:
                # 查找JSON内容
                json_match = re.search(r'\{[^}]*"is_valid"[^}]*\}', content, re.DOTALL)
                if json_match:
                    result = json.loads(json_match.group())
                    is_valid = result.get("is_valid", False)
                    reason = result.get("reason", "")
                    return model_name, ValidationResult(
                        is_valid=is_valid,
                        regulation_name=regulation_name,
                        existing_number=existing_number,
                        reason=reason
                    )
            except (json.JSONDecodeError, Exception) as e:
                logger.debug(f"[{model_name}] JSON解析失败: {e}")
                
            return model_name, None
            
    except Exception as e:
        logger.error(f"[{model_name}] 调用异常: {str(e)[:50]}")
        return model_name, None


async def validate_reference_number(
    regulation_name: str,
    existing_number: str,
    models: List[str] = None
) -> Optional[ValidationResult]:
    """
    多模型投票验证现有编号是否正确
    
    Args:
        regulation_name: 规范/法规名称
        existing_number: 现有编号
        models: 要使用的模型列表
    
    Returns:
        ValidationResult 或 None（验证失败）
    """
    models = models or REFERENCE_MODELS[:3]  # 验证用3个模型即可
    
    logger.info(f"开始验证编号: 名称='{regulation_name}', 编号='{existing_number}'")
    
    # 创建信号量限制并发
    semaphore = asyncio.Semaphore(MAX_CONCURRENT_MODELS)
    
    async def call_with_semaphore(model: str) -> Tuple[str, Optional[ValidationResult]]:
        async with semaphore:
            return await _call_validation_model(model, regulation_name, existing_number)
    
    # 并发调用所有模型
    tasks = [call_with_semaphore(model) for model in models]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    
    # 处理结果
    valid_count = 0
    invalid_count = 0
    reasons = []
    
    for result in results:
        if isinstance(result, Exception):
            continue
        _, validation = result
        if validation:
            if validation.is_valid:
                valid_count += 1
            else:
                invalid_count += 1
            reasons.append(validation.reason)
    
    total = valid_count + invalid_count
    if total == 0:
        return None
    
    # 超过半数认为有效
    if valid_count > invalid_count:
        return ValidationResult(
            is_valid=True,
            regulation_name=regulation_name,
            existing_number=existing_number,
            reason=f"{valid_count}/{total}个模型验证通过"
        )
    else:
        return ValidationResult(
            is_valid=False,
            regulation_name=regulation_name,
            existing_number=existing_number,
            reason=f"验证未通过（{valid_count}/{total}）"
        )


async def _call_generation_model(
    model_name: str,
    regulation_name: str,
    existing_info: str = ""
) -> Tuple[str, Optional[str]]:
    """
    调用单个模型生成编号
    
    Args:
        model_name: 模型名称
        regulation_name: 规范/法规名称
        existing_info: 现有信息（如"住建部37号令"）
    
    Returns:
        (model_name, generated_number 或 None)
    """
    try:
        system_prompt = """你是标准规范编号专家。请根据给定的规范/法规名称，生成对应的国家标准编号。

【编号格式规则】
- 只能包含大写字母、数字、斜杠(/)和横杠(-)
- 常见格式：GB50010、GB/T50502、JGJ80-2016、DL/T1147-2009、JGJ37-2018等
- 年份用4位数字表示，如-2010
- 不要输出任何解释，只输出编号

【输出示例】
输入：《混凝土结构设计规范》
输出：GB50010-2010

输入：《建筑施工高处作业安全技术规范》
输出：JGJ80-2016

输入：《危险性较大的分部分项工程安全管理规定》（住建部37号令）
输出：JGJ37-2018"""

        user_prompt = f"请为标准/法规生成编号：\n\n名称：《{regulation_name}》"
        if existing_info:
            user_prompt += f"\n现有信息：{existing_info}"
        user_prompt += "\n\n请只输出标准编号（如GB50010-2010），不要其他内容。"
        
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                f"{DASHSCOPE_BASE_URL}/chat/completions",
                headers={
                    "Authorization": f"Bearer {DASHSCOPE_API_KEY}",
                    "Content-Type": "application/json"
                },
                json={
                    "model": model_name,
                    "messages": [
                        {"role": "system", "content": system_prompt},
                        {"role": "user", "content": user_prompt}
                    ],
                    "temperature": 0.1,
                    "max_tokens": 100
                }
            )
            
            if response.status_code != 200:
                logger.error(f"[{model_name}] API调用失败: {response.status_code}")
                return model_name, None
            
            data = response.json()
            content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
            
            # 清理输出
            content = content.strip().strip('`').strip()
            
            # 提取编号
            extracted = _extract_reference_number(content)
            if not extracted and content and content != "未知":
                # 如果没有匹配到模式，但内容不为空，尝试直接使用
                # 去除常见前缀和多余内容
                cleaned = re.sub(r'^(编号|标准号|文号)[：:\s]*', '', content)
                cleaned = cleaned.split('\n')[0].strip()
                if cleaned:
                    extracted = cleaned
            
            logger.debug(f"[{model_name}] 生成: '{content}' -> '{extracted}'")
            return model_name, extracted
            
    except Exception as e:
        logger.error(f"[{model_name}] 调用异常: {str(e)[:50]}")
        return model_name, None


async def generate_reference_number(
    regulation_name: str,
    existing_info: str = "",
    models: List[str] = None
) -> Optional[ModelVoteResult]:
    """
    多模型投票生成规范编号
    
    Args:
        regulation_name: 规范/法规名称
        existing_info: 现有信息（如"住建部37号令"）
        models: 要使用的模型列表
    
    Returns:
        ModelVoteResult 或 None（生成失败）
    """
    models = models or REFERENCE_MODELS
    
    logger.info(f"开始生成编号: 名称='{regulation_name}'")
    
    # 创建信号量限制并发
    semaphore = asyncio.Semaphore(MAX_CONCURRENT_MODELS)
    
    async def call_with_semaphore(model: str) -> Tuple[str, Optional[str]]:
        async with semaphore:
            return await _call_generation_model(model, regulation_name, existing_info)
    
    # 并发调用所有模型
    tasks = [call_with_semaphore(model) for model in models]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    
    # 处理结果
    model_results = {}
    for result in results:
        if isinstance(result, Exception):
            continue
        model_name, number = result
        model_results[model_name] = number
    
    logger.debug(f"模型调用完成: {len(model_results)}/{len(models)}")
    
    # 投票
    vote_result = _vote_for_number(model_results)
    return vote_result


def _vote_for_number(results: Dict[str, Optional[str]]) -> Optional[ModelVoteResult]:
    """
    对多个模型的生成结果进行投票
    """
    # 过滤有效结果
    valid_numbers = [(model, num) for model, num in results.items() if num and num != "未知"]
    
    if not valid_numbers:
        logger.warning("所有模型都未生成有效编号")
        return None
    
    total_valid = len(valid_numbers)
    
    # 标准化编号（用于投票统计）
    def normalize(num: str) -> str:
        # 统一为大写，移除空格，但保留斜杠和横杠
        return num.upper().replace(' ', '')
    
    # 统计投票
    normalized_votes = {}
    for model, num in valid_numbers:
        normalized = normalize(num)
        if normalized not in normalized_votes:
            normalized_votes[normalized] = []
        normalized_votes[normalized].append((model, num))
    
    # 找到得票最多的编号
    winner_normalized = None
    winner_votes = []
    max_count = 0
    
    for norm, votes in normalized_votes.items():
        if len(votes) > max_count:
            max_count = len(votes)
            winner_normalized = norm
            winner_votes = votes
    
    vote_count = len(winner_votes)
    confidence = vote_count / total_valid
    
    # 检查是否超过半数
    if confidence <= 0.5:
        logger.info(f"投票未过半数: '{winner_normalized}' 得票 {vote_count}/{total_valid}")
        return None
    
    # 使用第一个原始格式的获胜编号
    original_winner = winner_votes[0][1]
    
    logger.info(f"投票结果: '{original_winner}' 得票 {vote_count}/{total_valid} ({confidence:.0%})")
    
    return ModelVoteResult(
        generated_number=original_winner,
        confidence=confidence,
        all_results=results,
        vote_count=vote_count,
        total_models=len(results)
    )


# 便捷函数
def validate_reference_number_sync(
    regulation_name: str,
    existing_number: str
) -> Optional[ValidationResult]:
    """同步版本的验证函数"""
    return asyncio.run(validate_reference_number(regulation_name, existing_number))


def generate_reference_number_sync(
    regulation_name: str,
    existing_info: str = ""
) -> Optional[ModelVoteResult]:
    """同步版本的生成函数"""
    return asyncio.run(generate_reference_number(regulation_name, existing_info))


if __name__ == "__main__":
    # 测试
    async def test():
        # 测试验证
        print("=" * 60)
        print("测试编号验证")
        print("=" * 60)
        
        test_cases = [
            ("建设工程安全生产管理条例", "国务院令第393号"),
            ("混凝土结构设计规范", "GB50010-2010"),
            ("危险性较大的分部分项工程安全管理规定", "住建部令第37号"),
        ]
        
        for name, number in test_cases:
            print(f"\n验证: 《{name}》 {number}")
            result = await validate_reference_number(name, number)
            if result:
                print(f"  结果: {'✓ 有效' if result.is_valid else '✗ 无效'}")
                print(f"  理由: {result.reason}")
            else:
                print("  验证失败")
        
        # 测试生成
        print("\n" + "=" * 60)
        print("测试编号生成")
        print("=" * 60)
        
        generate_cases = [
            "建设工程安全生产管理条例",
            "起重机械安全规程",
            "中华人民共和国环境保护法",
        ]
        
        for name in generate_cases:
            print(f"\n生成: 《{name}》")
            result = await generate_reference_number(name)
            if result:
                print(f"  结果: {result.generated_number}")
                print(f"  置信度: {result.confidence:.0%}")
            else:
                print("  生成失败")
    
    asyncio.run(test())
