#!/usr/bin/env python3
"""
Проверка .docx на остаточные артефакты шаблона:
- Жёлтый маркер (highlight) — забыли снять после заполнения
- Плейсхолдеры: НОМЕР, датА, @__________.xx, _____________
- Незаполненные SDT-контенты (structured document tags)

Использование:
    python check_template_artifacts.py <file.docx>

Вывод: JSON со списком найденных проблем.
"""

import json
import re
import sys
import zipfile
import xml.etree.ElementTree as ET
from pathlib import Path

WML = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"


def check_highlights(doc_root):
    """Поиск маркеров (highlight) — остатков шаблона."""
    issues = []
    highlight_counts = {}

    for rPr in doc_root.iter(f"{{{WML}}}rPr"):
        hl = rPr.find(f"{{{WML}}}highlight")
        if hl is not None:
            color = hl.get(f"{{{WML}}}val", "unknown")
            # Собираем текст из родительского run
            parent_r = rPr.getparent() if hasattr(rPr, 'getparent') else None
            highlight_counts[color] = highlight_counts.get(color, 0) + 1

    for color, count in highlight_counts.items():
        issues.append({
            "type": "highlight_marker",
            "severity": "warning",
            "detail": f"Маркер (highlight) цвета «{color}» — {count} шт. "
                      f"Вероятно, остался от шаблона.",
            "fix": f"Удалить все <w:highlight w:val=\"{color}\"/> из <w:rPr>"
        })
    return issues


def check_placeholders(doc_root):
    """Поиск типичных незаполненных плейсхолдеров."""
    issues = []
    # Паттерны плейсхолдеров из шаблонов Примавера
    placeholder_patterns = [
        (r'\bНОМЕР\b', 'Незаполненный номер договора «НОМЕР»'),
        (r'\bдатА\b', 'Незаполненная дата «датА»'),
        (r'@_{3,}', 'Незаполненный домен (@_______)'),
        (r'_{10,}\s*/\s*_{10,}', 'Незаполненное поле подписи (____/____)')
    ]

    # Собираем весь текст
    full_text = []
    for t_elem in doc_root.iter(f"{{{WML}}}t"):
        if t_elem.text:
            full_text.append(t_elem.text)
    text = ' '.join(full_text)

    for pattern, description in placeholder_patterns:
        matches = re.findall(pattern, text)
        if matches:
            issues.append({
                "type": "unfilled_placeholder",
                "severity": "warning",
                "detail": f"{description} — найдено {len(matches)} шт.",
                "fix": "Заполнить значение или удалить плейсхолдер"
            })

    return issues


def check_font_consistency(doc_root):
    """Проверка консистентности шрифтов — все текстовые run'ы должны использовать один шрифт."""
    issues = []
    from collections import Counter

    # Collect ascii/hAnsi fonts from all runs with visible text
    font_counter = Counter()
    runs_without_font = 0
    total_runs = 0

    for r in doc_root.iter(f"{{{WML}}}r"):
        t = r.find(f"{{{WML}}}t")
        if t is None or not t.text or not t.text.strip():
            continue
        total_runs += 1

        rPr = r.find(f"{{{WML}}}rPr")
        ascii_font = None
        if rPr is not None:
            rFonts = rPr.find(f"{{{WML}}}rFonts")
            if rFonts is not None:
                ascii_font = rFonts.get(f"{{{WML}}}ascii") or rFonts.get(f"{{{WML}}}hAnsi")

        if ascii_font:
            font_counter[ascii_font] += 1
        else:
            runs_without_font += 1

    if len(font_counter) > 1:
        detail_parts = [f"{font}: {count} run" for font, count in font_counter.most_common()]
        issues.append({
            "type": "font_inconsistency",
            "severity": "warning",
            "detail": f"Несколько шрифтов в документе: {', '.join(detail_parts)}. "
                      f"Ещё {runs_without_font} run без явного шрифта (наследуют из стиля).",
            "fix": "Выровнять все run'ы под единый шрифт документа (обычно Arial)"
        })

    # Отсутствие прямого w:rFonts у run не является артефактом: Word штатно
    # наследует шрифт от стиля абзаца/символов и docDefaults. Проверка выше
    # по-прежнему сообщает реальную смесь нескольких явно заданных шрифтов.

    return issues


def check_empty_sdt(doc_root):
    """Поиск пустых SDT (structured document tags) — незаполненных полей."""
    issues = []
    empty_count = 0

    for sdt in doc_root.iter(f"{{{WML}}}sdt"):
        content = sdt.find(f"{{{WML}}}sdtContent")
        if content is not None:
            texts = []
            for t in content.iter(f"{{{WML}}}t"):
                if t.text and t.text.strip():
                    texts.append(t.text.strip())
            if not texts:
                empty_count += 1

    if empty_count > 0:
        issues.append({
            "type": "empty_sdt",
            "severity": "info",
            "detail": f"Пустых полей (SDT) — {empty_count} шт. "
                      f"Возможно, не все поля шаблона заполнены.",
            "fix": "Проверить и заполнить все поля шаблона"
        })

    return issues


def main():
    if len(sys.argv) < 2:
        print("Использование: python check_template_artifacts.py <file.docx>")
        sys.exit(1)

    filepath = Path(sys.argv[1])
    if not filepath.exists():
        print(f"Файл не найден: {filepath}", file=sys.stderr)
        sys.exit(1)

    try:
        with zipfile.ZipFile(filepath) as z:
            doc_xml = z.read("word/document.xml")
            doc_root = ET.fromstring(doc_xml)

        all_issues = []
        all_issues.extend(check_highlights(doc_root))
        all_issues.extend(check_placeholders(doc_root))
        all_issues.extend(check_font_consistency(doc_root))
        all_issues.extend(check_empty_sdt(doc_root))

        result = {
            "file": filepath.name,
            "total_issues": len(all_issues),
            "issues": all_issues
        }

        print(json.dumps(result, ensure_ascii=False, indent=2))

        if result["total_issues"] == 0:
            print("\n✓ Артефактов шаблона не найдено", file=sys.stderr)
        else:
            print(f"\n⚠ Найдено артефактов: {result['total_issues']}", file=sys.stderr)
            for issue in result["issues"]:
                icon = "⚠" if issue["severity"] == "warning" else "ℹ"
                print(f"  {icon} {issue['detail']}", file=sys.stderr)

    except Exception as e:
        print(f"Ошибка: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()
