"""
Текстовый поисковый движок: поиск нескольких слов/фраз в извлечённом тексте
с построением контекстных фрагментов (аналог секции поиска и `$ContextChars`
в оригинальном скрипте).
"""
from __future__ import annotations

import re
from dataclasses import dataclass
from typing import Iterable, List

from app.config import MAX_MATCHES_PER_WORD_PER_FILE


@dataclass
class Match:
    word: str           # поисковый термин (как ввёл пользователь, напр. "охран*")
    matched_text: str   # реально найденная подстрока (напр. "охрана") — для подсветки
    context: str
    position: int


# Пропуск для wildcard '*': 0–30 букв/цифр/дефисов (аналог [\p{L}\p{N}\-]{0,30}
# из PowerShell-оригинала). \w в Python 3 — Unicode, покрывает кириллицу.
_WILDCARD_GAP = r"[\w\-]{0,30}"


def _build_pattern(word: str, case_sensitive: bool, whole_word: bool) -> re.Pattern:
    """Строит regex по термину с сохранением семантики оригинального ps1:

    * ``*`` — wildcard (0–30 букв/цифр/дефисов): «охран*» → охрана, охранник…
    * пробел — любой пробельный промежуток (``\\s+``): фразы через перенос строк;
    * ``ё`` и ``е`` взаимозаменяемы (частая проблема русских документов).
    """
    term = re.sub(r"\*+", "*", word.strip())  # схлопываем **, *** → *
    parts = term.split("*")

    escaped_parts = []
    for part in parts:
        esc = re.escape(part.strip())
        # re.escape в разных версиях Python по-разному трактует пробел —
        # приводим оба варианта к \s+ (пробел = любой пробельный промежуток)
        esc = esc.replace(r"\ ", r"\s+").replace(" ", r"\s+")
        escaped_parts.append(esc)

    pattern = _WILDCARD_GAP.join(escaped_parts)

    # Нормализация ё/е — только при регистронезависимом поиске (как в оригинале)
    if not case_sensitive:
        pattern = re.sub(r"[еёЕЁ]", "[её]", pattern)

    if whole_word:
        pattern = rf"\b{pattern}\b"

    flags = 0 if case_sensitive else re.IGNORECASE
    return re.compile(pattern, flags)


def find_matches(
    text: str,
    words: Iterable[str],
    case_sensitive: bool = False,
    whole_word: bool = False,
    context_chars: int = 150,
    max_matches_per_word: int = MAX_MATCHES_PER_WORD_PER_FILE,
) -> List[Match]:
    """Возвращает список найденных вхождений с контекстом вокруг каждого."""
    if not text:
        return []

    matches: List[Match] = []
    for word in words:
        word = word.strip()
        if not word:
            continue
        pattern = _build_pattern(word, case_sensitive, whole_word)
        count = 0
        for m in pattern.finditer(text):
            if count >= max_matches_per_word:
                break
            start = max(0, m.start() - context_chars)
            end = min(len(text), m.end() + context_chars)
            snippet = text[start:end].replace("\n", " ").strip()
            snippet = re.sub(r"\s+", " ", snippet)
            prefix = "…" if start > 0 else ""
            suffix = "…" if end < len(text) else ""
            matches.append(Match(
                word=word,
                matched_text=m.group(0),
                context=f"{prefix}{snippet}{suffix}",
                position=m.start(),
            ))
            count += 1
    return matches
