"""LibreOffice Writer local article/document intents (0.2.2.63)."""

from __future__ import annotations

import re

_LIBREOFFICE_RE = re.compile(
    r"\b(?:libre\s*office(?:\s+writer)?|libreoffice(?:\s+writer)?|onlyoffice|writer)\b",
    re.IGNORECASE,
)
_ARTICLE_WRITE_RE = re.compile(
    r"\b(?:"
    r"напиш\w*|write|состав\w*|сгенерир\w*|generate"
    r")\b",
    re.IGNORECASE,
)
_ARTICLE_NOUN_RE = re.compile(r"\b(?:стать(?:ю|я|е|и)|article)\b", re.IGNORECASE)
_DOCUMENT_CONTEXT_RE = re.compile(
    r"\b(?:"
    r"документ|document|word|ворд|"
    r"в\s+документ|в\s+документе|open\s+document|открой\s+документ"
    r")\b",
    re.IGNORECASE,
)
_OPEN_DOCUMENT_RE = re.compile(
    r"\b(?:открой|open|создай|create)\s+(?:документ|document|новый\s+документ|new\s+document)\b",
    re.IGNORECASE,
)
_TOPIC_ABOUT_RE = re.compile(
    r"(?:"
    r"стать(?:ю|я)\s+(?:об|о|про)\s+([^,.!?;]+)|"
    r"article\s+about\s+([^,.!?;]+)|"
    r"напиш(?:и|)\s+(?:стать(?:ю|я)\s+)?(?:об|о|про)\s+([^,.!?;]+)"
    r")",
    re.IGNORECASE,
)
_MAX_CHARS_RE = re.compile(
    r"(?:"
    r"не\s+(?:более|больше)\s+(\d+)\s*(?:символ|symbol|char)|"
    r"не\s+(\d+)\s*(?:символ|symbol|char)|"
    r"(?:under|до|max(?:imum)?|не\s+более)\s+(\d+)\s*(?:символ|symbol|char|знак)|"
    r"(\d+)\s*(?:символ|symbol|char)\s+(?:max|макс)"
    r")",
    re.IGNORECASE,
)
_LENGTH_CONSTRAINT_TAIL_RE = re.compile(
    r"\s+(?:"
    r"не\s+(?:более|больше)\s+\d+\s*(?:символ\w*|symbol\w*|char\w*|знак\w*)|"
    r"не\s+\d+\s*(?:символ\w*|symbol\w*|char\w*)|"
    r"(?:under|до|max(?:imum)?)\s+\d+\s*(?:символ\w*|symbol\w*|char\w*)"
    r")\s*$",
    re.IGNORECASE,
)
_ARTICLE_FAILURE_RE = re.compile(
    r"(?:"
    r"стать(?:я|ю|и)\s+не\s+(?:написан\w*|создан\w*|готов\w*)|"
    r"не\s+написан\w*\s+стать\w*|"
    r"article\s+not\s+written|"
    r"document\s+not\s+written|"
    r"текст\s+не\s+(?:написан\w*|введ\w*)|"
    r"в\s+writer\s+пусто|"
    r"libre\s*office.*пуст"
    r")",
    re.IGNORECASE,
)
_META_DEV_CONTEXT_RE = re.compile(
    r"\b(?:"
    r"cursor\s+ide|cursor\s+agent|в\s+cursor|for\s+cursor|"
    r"задача\s+для\s+cursor|task\s+for\s+cursor|"
    r"код\s+в\s+репо|code\s+repo|github|gitlab|backend|облак|cloud\s+dev"
    r")\b",
    re.IGNORECASE,
)


def normalize_libreoffice_text(text: str) -> str:
    """Normalize spaced variants: «Libre Office» → «libreoffice»."""
    raw = str(text or "")
    if not raw.strip():
        return raw
    return _LIBREOFFICE_RE.sub("libreoffice", raw)


def mentions_libreoffice(text: str) -> bool:
    return bool(_LIBREOFFICE_RE.search(str(text or "")))


def extract_max_article_chars(text: str) -> int | None:
    match = _MAX_CHARS_RE.search(str(text or ""))
    if not match:
        return None
    for group in match.groups():
        if group:
            try:
                value = int(group)
            except ValueError:
                continue
            if value > 0:
                return value
    return None


def extract_article_topic(text: str, *, default: str = "тема") -> str:
    """Topic for article generation; strips length-constraint tails from capture."""
    raw = normalize_libreoffice_text(str(text or "").strip())
    if not raw:
        return default
    match = _TOPIC_ABOUT_RE.search(raw)
    if match:
        topic = str(next(g for g in match.groups() if g) or "").strip()
        topic = _LENGTH_CONSTRAINT_TAIL_RE.sub("", topic).strip(" ,.;—–-")
        if topic:
            return topic[:1].upper() + topic[1:] if len(topic) > 1 else topic.upper()
    lower = raw.lower()
    if "iuve" in lower and "agent" in lower:
        return "IuVe Agent Desktop"
    if "iuve" in lower:
        return "IuVe"
    return default


def is_libreoffice_article_intent(text: str) -> bool:
    """Write/generate an article in LibreOffice Writer — local device task."""
    raw = normalize_libreoffice_text(str(text or "").strip())
    if not raw:
        return False
    has_write = bool(_ARTICLE_WRITE_RE.search(raw))
    has_article = bool(_ARTICLE_NOUN_RE.search(raw))
    has_document = bool(_DOCUMENT_CONTEXT_RE.search(raw)) or bool(_OPEN_DOCUMENT_RE.search(raw))
    has_office = mentions_libreoffice(raw)
    if has_office and (has_write or has_article):
        return True
    if has_document and has_write and has_article:
        return True
    if has_document and has_write:
        return True
    return False


def is_document_article_failure_follow_up(text: str) -> bool:
    """User reports LibreOffice article step failed — retry locally, not web post."""
    return bool(_ARTICLE_FAILURE_RE.search(str(text or "").strip()))


def should_block_meta_external_for_local_document(text: str) -> bool:
    """Explicit LibreOffice/document article work must never hit Cursor/cloud refusal."""
    raw = str(text or "").strip()
    if not raw:
        return False
    if is_libreoffice_article_intent(raw):
        return True
    if is_document_article_failure_follow_up(raw):
        return True
    normalized = normalize_libreoffice_text(raw).lower()
    if mentions_libreoffice(raw) and bool(_ARTICLE_NOUN_RE.search(raw)):
        return True
    if bool(_DOCUMENT_CONTEXT_RE.search(raw)) and bool(_ARTICLE_WRITE_RE.search(raw)):
        return True
    if "разработк" in normalized and mentions_libreoffice(raw) and bool(_ARTICLE_NOUN_RE.search(raw)):
        return True
    return False


def is_misrouted_cloud_plan_for_libreoffice(text: str, plan: dict) -> bool:
    """True when cloud/LLM plan should be replaced by local LibreOffice article plan."""
    if not isinstance(plan, dict):
        return False
    if not (
        is_libreoffice_article_intent(text)
        or is_document_article_failure_follow_up(text)
        or should_block_meta_external_for_local_document(text)
    ):
        return False
    steps = plan.get("steps")
    if not isinstance(steps, list) or not steps:
        return True
    step_types = [
        str(step.get("command_type") or "").strip().lower()
        for step in steps
        if isinstance(step, dict)
    ]
    if not step_types:
        return True
    if step_types[0] == "browser_open_url" or "open_web_composer" in step_types:
        return True
    if "keyboard_type_text" in step_types:
        for step in steps:
            if not isinstance(step, dict):
                continue
            if str(step.get("command_type") or "").strip().lower() != "keyboard_type_text":
                continue
            payload = step.get("payload") if isinstance(step.get("payload"), dict) else {}
            target = str(payload.get("target_app") or payload.get("target_window") or "").strip().lower()
            if target in {"", "active", "cursor"}:
                return True
    if step_types == ["open_document_edit"]:
        return True
    if step_types == ["get_status"]:
        return True
    return False
