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

import json
import re
from typing import List, Optional

from pydantic import BaseModel, Field, ValidationError
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser, StrOutputParser
from foundation.ai.agent.generate.model_generate import generate_model_client


# ===== 1) 定义结构 =====
class PunctuationResult(BaseModel):
    original_text: str = Field(..., description="审查的规范原文，与输入完全一致")
    title_mark_status: bool = Field(..., description="书名号使用是否正确，true表示正确，false表示错误")
    bracket_status: Optional[bool] = Field(..., description="括号使用是否正确，true表示正确，false表示错误，null表示没有编号")


class PunctuationResults(BaseModel):
    items: List[PunctuationResult]


# ===== 1.5) 辅助函数：提取和验证编号 =====
def _extract_bracket_content_smart(text: str) -> tuple:
    """
    智能提取括号内容，处理嵌套括号情况
    
    返回: (括号内容, 是否成对, 括号后是否有额外字符)
    """
    # 统一括号类型
    text_normalized = text.replace('(', '（').replace(')', '）')
    
    # 找到书名号后的文本
    last_title_end = max(text_normalized.rfind('《'), text_normalized.rfind('》'))
    if last_title_end == -1:
        return None, False, True
    
    text_after_title = text_normalized[last_title_end + 1:]
    
    # 使用栈找到所有配对的括号
    stack = []
    pairs = []
    
    for i, char in enumerate(text_after_title):
        if char == '（':
            stack.append(i)
        elif char == '）':
            if stack:
                start = stack.pop()
                content = text_after_title[start + 1:i]
                pairs.append({
                    'content': content,
                    'start': start,
                    'end': i,
                    'full': text_after_title[start:i+1]
                })
    
    # 找到第一个配对的括号内容（最外层）
    if pairs:
        # 按起始位置排序，取第一个（最外层）
        pairs.sort(key=lambda x: x['start'])
        first_pair = pairs[0]
        
        # 检查括号后是否有多余字符（除了空白字符）
        after_bracket = text_after_title[first_pair['end'] + 1:]
        has_extra_chars = bool(after_bracket.strip())
        
        is_pair = len(stack) == 0
        return first_pair['content'], is_pair, has_extra_chars
    
    # 有左括号但没有配对的右括号
    unpaired_left = text_after_title.count('（') - text_after_title.count('）')
    if unpaired_left > 0:
        first_left = text_after_title.find('（')
        if first_left != -1:
            # 提取左括号后到右括号（或字符串结尾）的内容
            first_right = text_after_title.find('）', first_left + 1)
            if first_right != -1:
                content = text_after_title[first_left + 1:first_right]
            else:
                content = text_after_title[first_left + 1:]
            # 检查是否有多余字符（肯定有多余，因为没有配对的右括号）
            return content, False, True
    
    return None, False, True


def _is_valid_reference_number(number_text: str) -> bool:
    """
    验证编号是否符合规范要求：
    1. 接受版本号格式（如"2024版"、"2023年"）- 企业内部文件常用
    2. 接受标准编号格式（英文+数字，如GB50010-2010）
    3. 接受法规编号格式（中文+数字，如令第393号）
    
    有效示例：
    - GB50010-2010、Q/CR9230-2016（英文+数字）
    - 令第393号、第37号令（中文+数字）
    - 2024版、2023年（版本号格式，企业内部文件）
    - V1.0、Version 2.0（版本号格式）
    
    无效示例：
    - 纯空格、纯特殊字符
    - 无意义的数字组合
    """
    if not number_text or not number_text.strip():
        return False
    
    text = number_text.strip()
    
    # 检查是否包含英文字母
    has_english = bool(re.search(r'[a-zA-Z]', text))
    # 检查是否包含中文字符
    has_chinese = bool(re.search(r'[\u4e00-\u9fff]', text))
    # 检查是否包含数字
    has_digit = bool(re.search(r'\d', text))
    
    # 情况1: 标准编号格式（英文+数字）
    if has_english and has_digit:
        return True
    
    # 情况2: 法规编号格式（中文+数字，且不是纯数字后缀）
    if has_chinese and has_digit:
        # 排除纯数字+少量中文后缀的情况（如"2024版"不算中文+数字格式）
        chinese_pattern = r'^[\d\s]*[\u4e00-\u9fff]+[\d\s]*[\u4e00-\u9fff]*$'
        if re.search(chinese_pattern, text):
            return True
    
    # 情况3: 版本号格式（年份+版/年，企业内部文件常用）
    # 匹配：4位年份 + 版/年，如"2024版"、"2023年"、"2024 版"、"2023修订版"
    version_pattern = r'^\d{4}\s*[版年修订]+[版本]?$'
    if re.match(version_pattern, text):
        return True
    
    # 情况4: 版本号格式（V/VERSION + 数字）
    v_version_pattern = r'^[vV][\d\.]+|[Vv]ersion\s*[\d\.]+$'
    if re.match(v_version_pattern, text):
        return True
    
    # 有效编号：必须有数字，且满足上述任一格式
    return has_digit and (has_english or has_chinese)


# ===== 2) SYSTEM Prompt =====
SYSTEM = """
你是【编制依据格式检查专家】。

【任务】
检查编制依据中规范名称和编号的标点符号使用是否正确。

【判定规则】
1. title_mark_status：检查《》是否正确包裹规范名称
   - 规范名称必须完整被《》包裹，不能遗漏部分文字
   - 书名号必须成对出现

2. bracket_status：检查编号是否被括号正确包裹
   - 有编号且被括号（中文或英文）完整包裹 → true
   - 有编号但未被括号包裹 → false
   - 没有编号 → null

【编号说明】
编号可以是多种形式：
- 标准编号：GB50010-2010、GB/T50502、JGJ80-2016等
- 法规编号：令第393号、第37号令、国务院令第493号等
- 只要编号与规范名称匹配且真实存在即可

【输出要求】
- 为每个输入文本输出一个检查结果
- 确保输出数量与输入一致
- original_text 必须与输入完全一致
"""

HUMAN = """
请检查以下文本中书名号和括号的**内容是否全部被包裹**，以及是否有编号。

【判断原则】
- 仅检查包裹的**完整性**：书名号是否包裹了规范名称的全部内容；括号是否包裹了内容的全部内容
- 中文括号（）和英文括号()混用视为正常，不区分
- 若内容在符号外遗漏，或符号包裹了多余内容，则判定为false
- **<>、＜＞不是书名号**，是文本内容的一部分，忽略它们
- **括号内的任何内容都视为编号/版本信息**，不判断编号内容是否正确

【编号说明】
编号可以是多种形式，包括但不限于：
- GB50010-2010、GB/T50502（标准编号）
- 令第393号、第37号令（法规编号）
- 只要编号被括号包裹且与规范名称对应即可

【示例】
示例1：《建筑抗震设计规范》（GB 50011-2001）
- 书名号包裹完整 → title_mark_status=true
- 编号被括号包裹 → bracket_status=true

示例2：《建筑抗震设计规范》GB 50011-2001
- 书名号包裹完整 → title_mark_status=true
- 编号未被括号包裹 → bracket_status=false

示例3：《建筑抗震设计规范》（令第X号）
- 书名号包裹完整 → title_mark_status=true
- 编号被括号包裹 → bracket_status=true

示例4：《建筑抗震设》计规范 （GB 50011-2001）
- 规范名称是"建筑抗震设计规范"，但只有"建筑抗震设"被包裹 → title_mark_status=false
- 编号被包裹 → bracket_status=true

示例5：《关于实施<危险性较大的分部分项工程安全管理规定>有关问题的通知》（建办质〔2018〕31号）
- 书名号包裹完整（<>是内容的一部分，不是书名号）→ title_mark_status=true
- 编号被括号包裹 → bracket_status=true

示例6：《专项施工方案实施管理细则》（2024版）
- 书名号包裹完整 → title_mark_status=true
- 括号内有内容（2024版），视为编号 → bracket_status=true

【重要区分】
- "没有编号"（如只有《规范名称》）→ bracket_status = null
- "有编号但无括号"（如《规范名称》GB1234）→ bracket_status = false
- "编号被括号包裹"（如《规范名称》（GB1234））→ bracket_status = true

【待检查文本】
{items}

【输出格式要求】
{format_instructions}
/no_think
"""

# ===== 3) Output Parser =====
parser = PydanticOutputParser(pydantic_object=PunctuationResults)

# ===== 4) Prompt =====
prompt = ChatPromptTemplate.from_messages([
    ("system", SYSTEM),
    ("human", HUMAN)
])

# ===== 5) LLM Client =====
model_client = generate_model_client


# ===== 6) 提取第一个 JSON =====
def extract_first_json(text: str) -> dict:
    """从任意模型输出中提取第一个完整 JSON 对象 { ... }"""
    start = text.find("{")
    if start == -1:
        raise ValueError("未找到 JSON 起始 '{'")

    depth = 0
    for i in range(start, len(text)):
        ch = text[i]
        if ch == "{":
            depth += 1
        elif ch == "}":
            depth -= 1
            if depth == 0:
                return json.loads(text[start:i + 1])

    raise ValueError("JSON 花括号未闭合")


# ===== 7) 核心方法 =====
async def check_punctuation(items: List[str]) -> str:
    """
    检查规范文本中的书名号和括号使用是否正确
    
    逻辑：
    1. 检查书名号和括号的成对性
    2. 检查括号是否在书名号之后
    3. 不验证编号内容，只检查格式
    """
    # 进行成对预检
    pre_results = []  # 预填结果

    for text in items:
        # 书名号成对判定
        left_title = text.count("《")
        right_title = text.count("》")
        title_pair_ok = left_title == right_title and left_title > 0

        # 括号成对判定（中英文括号混用视为同类）
        left_br = text.count("(") + text.count("（")
        right_br = text.count(")") + text.count("）")
        bracket_pair_ok = left_br == right_br and left_br > 0
        
        # 情况1：都不存在 → 都为False
        if left_title == 0 and left_br == 0:
            pre_results.append({
                "original_text": text,
                "title_mark_status": False,
                "bracket_status": None
            })
            continue
        
        # 情况2：只有书名号，没有括号 → bracket_status为None
        if left_title > 0 and left_br == 0:
            pre_results.append({
                "original_text": text,
                "title_mark_status": bool(title_pair_ok),
                "bracket_status": None
            })
            continue
        
        # 情况3：只有括号，没有书名号 → title_mark_status为False
        if left_title == 0 and left_br > 0:
            pre_results.append({
                "original_text": text,
                "title_mark_status": False,
                "bracket_status": bool(bracket_pair_ok)
            })
            continue
        
        # 情况4：两者都存在，检查位置关系
        # 检查括号是否在书名号之后（找最后一个》之后的第一个括号）
        bracket_after_title = True
        if bracket_pair_ok and title_pair_ok:
            last_title_pos = max(text.rfind("《"), text.rfind("》"))
            # 从最后一个》之后开始找第一个括号（避免书名内的括号干扰）
            text_after_title = text[last_title_pos + 1:]
            first_bracket_pos = float('inf')
            if "(" in text_after_title:
                first_bracket_pos = text_after_title.find("(")
            if "（" in text_after_title:
                first_bracket_pos = min(first_bracket_pos, text_after_title.find("（"))
            # 检查是否在最后一个》之后找到了括号
            bracket_after_title = first_bracket_pos != float('inf')

        # 【修改】使用智能提取逻辑处理括号
        bracket_content, is_pair, has_extra_chars = _extract_bracket_content_smart(text)
        
        # 检查是否找到括号内容
        if bracket_content is None:
            # 没有括号
            pre_results.append({
                "original_text": text,
                "title_mark_status": bool(title_pair_ok),
                "bracket_status": None
            })
        elif not is_pair:
            # 括号不成对（缺少右括号或左括号）
            pre_results.append({
                "original_text": text,
                "title_mark_status": bool(title_pair_ok),
                "bracket_status": False
            })
        elif has_extra_chars:
            # 括号成对，但括号后有多余字符（如分号、数字等）
            pre_results.append({
                "original_text": text,
                "title_mark_status": bool(title_pair_ok),
                "bracket_status": False
            })
        elif not _is_valid_reference_number(bracket_content):
            # 括号成对且无多余字符，但编号格式不正确（纯数字或无效格式）
            pre_results.append({
                "original_text": text,
                "title_mark_status": bool(title_pair_ok),
                "bracket_status": False,
                "invalid_number_format": True,
                "invalid_number_content": bracket_content
            })
        else:
            # 括号成对、无多余字符、编号格式正确
            pre_results.append({
                "original_text": text,
                "title_mark_status": True,
                "bracket_status": True,
                "invalid_number_format": False
            })

    # 直接返回预检结果
    return json.dumps(pre_results, ensure_ascii=False, indent=2)


# ===== 8) 示例 =====
if __name__ == "__main__":
    import asyncio

    async def main():
        test_items = [
            "《建设工程安全生产管理条例》（国务院令第393号）",
            "《混凝土结构设计规范》GB50010-2010",
            "《建筑抗震设计规范》（GB 50011-2001）",
            "《建筑抗震设》计规范 （GB 50011-2001）",
        ]

        result = await check_punctuation(test_items)
        print(result)

    asyncio.run(main())
