import re
from krrkt.rules.loader import load_patterns

try:
    import pymorphy3
    _morph = pymorphy3.MorphAnalyzer()
except ImportError:
    _morph = None


# Common short past participles that are part of passive voice constructions.
# These should be caught by phrase-level "было + participle" rules, not the
# automatic participle detector, to avoid double-flagging / wrong category.
PASSIVE_PARTICIPLES = {
    "принято", "принят", "принята", "приняты",
    "решено", "сделано", "получено",
    "установлено", "проведено", "создано", "обнаружено",
    "выполнено", "достигнуто", "завершено", "направлено",
    "основано", "организовано", "рассмотрено",
}


class FastLayer:
    def __init__(self):
        patterns = load_patterns()
        self.phrases = sorted(
            patterns.get("phrases", []),
            key=lambda p: len(p["pattern"]),
            reverse=True  # longest first
        )
        self.lemmas = patterns.get("lemmas", [])
        self.regex_patterns = [
            {**p, "_compiled": re.compile(p["pattern"], re.IGNORECASE | re.UNICODE)}
            for p in patterns.get("regex", [])
        ]

    def analyze(self, text: str) -> list[dict]:
        findings = []
        used_ranges = set()

        # Phase 1: Phrase matching (longest first, case-insensitive)
        text_lower = text.lower()
        for phrase_def in self.phrases:
            pattern = phrase_def["pattern"].lower()
            start = 0
            while True:
                idx = text_lower.find(pattern, start)
                if idx == -1:
                    break
                end = idx + len(pattern)
                if not self._overlaps(idx, end, used_ranges):
                    findings.append(self._make_finding(text, idx, end, phrase_def))
                    used_ranges.add((idx, end))
                start = idx + 1

        # Phase 2: Lemma matching via pymorphy3
        if _morph:
            for token_match in re.finditer(
                r'[A-Za-z\u0410-\u044f\u0401\u0451][\w-]*', text, re.UNICODE
            ):
                token = token_match.group()
                token_start = token_match.start()
                token_end = token_match.end()
                if self._overlaps(token_start, token_end, used_ranges):
                    continue
                parsed = _morph.parse(token)
                if not parsed:
                    continue
                lemma = parsed[0].normal_form
                for lemma_def in self.lemmas:
                    if lemma == lemma_def["lemma"].lower():
                        findings.append(self._make_finding(text, token_start, token_end, lemma_def))
                        used_ranges.add((token_start, token_end))
                        break

        # Phase 2.5: Detect participles and gerunds via morphology
        if _morph:
            for token_match in re.finditer(r'[А-Яа-яЁё][\w-]*', text, re.UNICODE):
                token = token_match.group()
                token_start = token_match.start()
                token_end = token_match.end()
                if self._overlaps(token_start, token_end, used_ranges):
                    continue
                parsed = _morph.parse(token)
                if not parsed:
                    continue
                tag = parsed[0].tag
                # Participle (причастие): PRTF (full) or PRTS (short)
                # Skip common short participles used in passive constructions
                if token.lower() in PASSIVE_PARTICIPLES:
                    continue
                if 'PRTF' in tag or 'PRTS' in tag:
                    findings.append(self._make_finding(text, token_start, token_end, {
                        "rule": "Возможно, причастие",
                        "section": "причастие",
                        "weight": 50,
                        "style": "blue",
                        "tab": "blue",
                        "description": "Постарайтесь заменить на глагол.",
                    }))
                    used_ranges.add((token_start, token_end))
                # Gerund (деепричастие): GRND
                elif 'GRND' in tag:
                    findings.append(self._make_finding(text, token_start, token_end, {
                        "rule": "Возможно, деепричастие",
                        "section": "деепричастие",
                        "weight": 50,
                        "style": "blue",
                        "tab": "blue",
                        "description": "Постарайтесь заменить на глагол.",
                    }))
                    used_ranges.add((token_start, token_end))

        # Phase 3: Regex patterns
        for rx_def in self.regex_patterns:
            for m in rx_def["_compiled"].finditer(text):
                s, e = m.start(), m.end()
                if not self._overlaps(s, e, used_ranges):
                    findings.append(self._make_finding(text, s, e, rx_def))
                    used_ranges.add((s, e))

        return findings

    def _overlaps(self, start: int, end: int, used: set) -> bool:
        for us, ue in used:
            if start < ue and end > us:
                return True
        return False

    def _make_finding(self, text: str, start: int, end: int, rule_def: dict) -> dict:
        return {
            "start": start,
            "end": end,
            "word": text[start:end],
            "rule": rule_def.get("rule", ""),
            "section": rule_def.get("section", ""),
            "description": rule_def.get("description", ""),
            "weight": rule_def.get("weight", 100),
            "style": rule_def.get("style", "red"),
            "tab": rule_def.get("tab", "red"),
            "source": "fast",
        }
