"""
Утилиты для работы с домашними заданиями.
"""
import re
import os
import logging
from django.core.exceptions import ValidationError

logger = logging.getLogger(__name__)

# Теги, разрешённые после конвертации Markdown + MathML (для санитизации)
ALLOWED_FEEDBACK_HTML_TAGS = [
    'p', 'br', 'div', 'span', 'strong', 'em', 'b', 'i', 'u', 's',
    'ul', 'ol', 'li', 'code', 'pre', 'blockquote', 'h1', 'h2', 'h3', 'h4',
    'a', 'sub', 'sup',
    # MathML (для формул из LaTeX)
    'math', 'mrow', 'mi', 'mo', 'mn', 'msup', 'msub', 'munder', 'mover',
    'mfrac', 'mtable', 'mtr', 'mtd', 'mstyle', 'mtext', 'annotation', 'semantics',
]


def feedback_to_html(raw: str) -> str:
    """
    Переводит ответ ИИ (markdown + LaTeX вроде $a$, $$x^2$$) в безопасный HTML.
    Используется для отображения ai_feedback и комментариев проверки без «сырых» символов $.
    """
    if not raw or not raw.strip():
        return ''
    try:
        import markdown
        import bleach
    except ImportError:
        logger.warning("markdown/bleach не установлены — возвращаем экранированный текст")
        return _escape_html(raw)

    # 1) Выносим LaTeX в placeholder'ы, чтобы markdown их не трогал
    block_maths = []
    inline_maths = []

    def block_replacer(m):
        block_maths.append(m.group(1).strip())
        return f'\x00MATH_BLOCK_{len(block_maths) - 1}\x00'

    def inline_replacer(m):
        inline_maths.append(m.group(1).strip())
        return f'\x00MATH_INLINE_{len(inline_maths) - 1}\x00'

    text = raw
    # Сначала блочные $$ ... $$
    text = re.sub(r'\$\$([^$]*?)\$\$', block_replacer, text, flags=re.DOTALL)
    # Потом инлайновые $ ... $ (не захватываем переносы в инлайне)
    text = re.sub(r'\$([^$\n]+?)\$', inline_replacer, text)

    # 2) Markdown → HTML
    html = markdown.markdown(
        text,
        extensions=['nl2br'],
        output_format='html5',
    )

    # 3) LaTeX → MathML и подставляем обратно
    try:
        from latex2mathml.converter import convert as latex_to_mathml
    except ImportError:
        latex_to_mathml = None

    def replace_math(placeholder_prefix, maths_list):
        for i, latex in enumerate(maths_list):
            token = f'\x00{placeholder_prefix}_{i}\x00'
            if latex_to_mathml and latex:
                try:
                    mathml = latex_to_mathml(latex)
                    # latex2mathml возвращает полный <math>...</math>
                    html_replacement = f'<span class="math-wrap">{mathml}</span>'
                except Exception as e:
                    logger.debug("LaTeX → MathML failed for %r: %s", latex[:50], e)
                    html_replacement = _escape_html(f'${latex}$')
            else:
                html_replacement = _escape_html(f'${latex}$')
            nonlocal html
            html = html.replace(token, html_replacement)

    replace_math('MATH_BLOCK', block_maths)
    replace_math('MATH_INLINE', inline_maths)

    # 4) Санитизация (разрешаем class, xmlns для формул и ссылок href)
    allowed_attrs = {'*': ['class'], 'a': ['href', 'title', 'rel'], 'math': ['xmlns'], 'span': ['class']}
    html = bleach.clean(html, tags=ALLOWED_FEEDBACK_HTML_TAGS, attributes=allowed_attrs, strip=True)
    return html.strip()


def _escape_html(s: str) -> str:
    """Экранирует HTML-сущности."""
    return (
        s.replace('&', '&amp;')
        .replace('<', '&lt;')
        .replace('>', '&gt;')
        .replace('"', '&quot;')
    )


def sanitize_filename(filename):
    """
    Очистка имени файла от опасных символов.
    
    Args:
        filename: Исходное имя файла
    
    Returns:
        str: Безопасное имя файла
    """
    # Удаляем путь, оставляем только имя файла
    filename = os.path.basename(filename)
    
    # Удаляем опасные символы
    filename = re.sub(r'[<>:"/\\|?*\x00-\x1f]', '', filename)
    
    # Удаляем ведущие/завершающие точки и пробелы
    filename = filename.strip('. ')
    
    # Ограничиваем длину
    if len(filename) > 255:
        name, ext = os.path.splitext(filename)
        filename = name[:250] + ext
    
    # Если имя пустое, возвращаем дефолтное
    if not filename:
        filename = 'file'
    
    return filename


def validate_file_type(filename, allowed_types):
    """
    Проверка типа файла.
    
    Args:
        filename: Имя файла
        allowed_types: Строка с разрешенными расширениями (например, ".pdf,.doc,.docx")
    
    Returns:
        bool: True если тип разрешен
    """
    if not allowed_types:
        return True
    
    # Получаем расширение файла
    ext = os.path.splitext(filename)[1].lower()
    
    # Нормализуем allowed_types
    allowed_list = [t.strip().lower() for t in allowed_types.split(',') if t.strip()]
    
    return ext in allowed_list


def validate_file_size(file_size, max_size):
    """
    Проверка размера файла.
    
    Args:
        file_size: Размер файла в байтах
        max_size: Максимальный размер в байтах
    
    Returns:
        bool: True если размер допустим
    """
    if max_size <= 0:
        return True
    
    return file_size <= max_size

