import os
import uuid
import json
from datetime import date, datetime
from dataclasses import dataclass
from docx import Document
from docxtpl import DocxTemplate


TEMPLATE_DIR = os.path.join(
    os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))),
    "docs", "执法助手需求", "综合执法助手", "执法文书模板", "docx",
)

OUTPUT_DIR = os.path.join(
    os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))),
    "files", "documents",
)


def list_all_templates() -> list[dict]:
    templates = []
    if not os.path.isdir(TEMPLATE_DIR):
        return templates
    for filename in sorted(os.listdir(TEMPLATE_DIR)):
        if not filename.endswith(".docx"):
            continue
        full_path = os.path.join(TEMPLATE_DIR, filename)
        file_id = filename.replace(".docx", "")
        display_name = file_id.split("-", 1)[-1] if "-" in file_id else file_id
        category = "投诉举报" if file_id.startswith("TS-") else "行政处罚"
        fields = _extract_template_fields(full_path)
        templates.append({
            "id": str(uuid.uuid4())[:8],
            "file_id": file_id,
            "name": display_name,
            "category": category,
            "filename": filename,
            "file_path": full_path,
            "fields": fields,
        })
    return templates


def _extract_template_fields(file_path: str) -> list[str]:
    fields = set()
    try:
        if not os.path.exists(file_path):
            return list(fields)
        src = file_path

        doc = DocxTemplate(src)
        from docx.opc.constants import RELATIONSHIP_TYPE as RT
        parts_rels = {}
        for rel in doc.part.rels.values():
            parts_rels[rel.rId] = rel.target_part
        for rel_id, part in parts_rels.items():
            if hasattr(part, '_element'):
                xml = part._element.xml
                import re
                for match in re.finditer(r'\{\{(.+?)\}\}', xml):
                    field = match.group(1).strip()
                    if field and not field.startswith("#") and not field.startswith("@"):
                        fields.add(field)
    except Exception:
        try:
            doc = Document(file_path)
            for p in doc.paragraphs:
                import re
                for match in re.finditer(r'\{\{(.+?)\}\}', p.text):
                    field = match.group(1).strip()
                    if field and not field.startswith("#") and not field.startswith("@"):
                        fields.add(field)
            for table in doc.tables:
                for row in table.rows:
                    for cell in row.cells:
                        import re
                        for match in re.finditer(r'\{\{(.+?)\}\}', cell.text):
                            field = match.group(1).strip()
                            if field and not field.startswith("#") and not field.startswith("@"):
                                fields.add(field)
        except Exception:
            pass

    return sorted(fields) if fields else ["通用文档模板（无特殊字段）"]


class SilentUndefined:
    """Jinja2 undefined that silently returns empty string instead of raising errors."""
    _undefined_name = ''

    def __init__(self, *args, **kwargs):
        pass

    def __str__(self):
        return ''

    def __iter__(self):
        return iter([])

    def __bool__(self):
        return False

    def __getattr__(self, name):
        if name.startswith('_'):
            raise AttributeError(name)
        return self

    def __repr__(self):
        return ''

    def __eq__(self, other):
        return isinstance(other, SilentUndefined)

    def __hash__(self):
        return 0


def generate_document(template_path: str, field_data: dict, output_dir: str = None) -> str:
    if output_dir is None:
        output_dir = OUTPUT_DIR

    today_dir = date.today().strftime("%Y-%m")
    file_dir = os.path.join(output_dir, today_dir)
    os.makedirs(file_dir, exist_ok=True)

    file_id = str(uuid.uuid4())[:8]
    output_path = os.path.join(file_dir, f"{file_id}.docx")

    doc = DocxTemplate(template_path)

    # 预扫描模板中所有占位符，将未提供的变量填充为空字符串，避免 UndefinedError
    undeclared = doc.get_undeclared_template_variables()
    for var_name in undeclared:
        if var_name not in field_data:
            field_data[var_name] = ''

    # 使用 jinja2 Environment 设置变量开始/结束标记，避免中文横杠被解析为减法
    from jinja2 import Environment
    env = Environment(variable_start_string='{{', variable_end_string='}}')

    doc.render(field_data, jinja_env=env)
    doc.save(output_path)

    return output_path


def extract_template_placeholders(file_path: str) -> list[str]:
    """从docx模板文件中提取所有{{占位符}}，返回占位符名称列表"""
    return _extract_template_fields(file_path)


def match_templates(context: str, templates: list[dict] = None) -> list[dict]:
    if templates is None:
        templates = list_all_templates()

    keywords_map = {
        "处罚": ["行政处罚决定书", "行政处罚告知书", "当场行政处罚决定书", "不予行政处罚决定书"],
        "立案": ["立案审批表"],
        "投诉": ["投诉登记表", "投诉受理决定书", "投诉不予受理决定书"],
        "举报": ["举报登记表"],
        "调查": ["案件调查终结报告", "询问笔录", "现场笔录"],
        "强制": ["实施行政强制措施决定书", "解除行政强制措施决定书"],
        "听证": ["行政处罚听证通知书", "听证笔录", "听证报告"],
        "催告": ["行政处罚决定履行催告书"],
        "责令": ["责令改正通知书", "责令退款通知书"],
        "结案": ["结案审批表", "涉案物品处理记录"],
        "移送": ["案件移送函", "涉嫌犯罪案件移送书"],
    }

    scores = {}
    for template in templates:
        score = 0
        name = template["name"]
        for keyword, matched_names in keywords_map.items():
            if keyword in context:
                for mn in matched_names:
                    if mn in name:
                        score += 3
                        break
            if any(mn in name for mn in matched_names if keyword in context):
                score += 2
        scores[template["id"]] = score

    scored = sorted(
        [(t, scores.get(t["id"], 0)) for t in templates],
        key=lambda x: -x[1],
    )

    result = []
    for t, s in scored[:3]:
        reason_parts = []
        name = t["name"]
        for keyword, matched_names in keywords_map.items():
            if keyword in context:
                for mn in matched_names:
                    if mn in name:
                        reason_parts.append(f"对话中提到「{keyword}」，匹配「{mn}」模板")
                        break
        result.append({
            **t,
            "match_score": s,
            "match_reason": "；".join(reason_parts) if reason_parts else "根据模板类别推荐",
        })

    return result