"""Sentence-boundary-aware truncation for knowledge extraction.

QR8 P8 T2: 31-80% of entries are truncated mid-word because extraction
uses hard character limits ([:200], [:500]) without sentence awareness.
"""
import re

_SENTENCE_SPLIT = re.compile(r'(?<=[.!?])\s+')


def truncate_at_sentence(text: str, max_chars: int = 500) -> str:
    """Truncate text at the last sentence boundary before max_chars.

    If no sentence boundary found, falls back to word boundary.
    Never returns less than 40 chars (avoids over-truncation).
    """
    if not text or len(text) <= max_chars:
        return text.strip()

    candidate = text[:max_chars]
    sentences = _SENTENCE_SPLIT.split(candidate)
    if len(sentences) > 1:
        result = ""
        for s in sentences:
            s_stripped = s.strip()
            # Only include complete sentences (end with terminal punctuation)
            if not s_stripped or not s_stripped[-1] in '.!?)':
                break
            if len(result) + len(s_stripped) + 1 <= max_chars:
                result = (result + " " + s_stripped).strip() if result else s_stripped
            else:
                break
        if len(result) >= 40:
            return result.strip()

    for i in range(min(len(text), max_chars) - 1, max(39, max_chars // 2), -1):
        if text[i] in ".!?)":
            return text[:i + 1].strip()

    space_idx = text.rfind(" ", 40, max_chars)
    if space_idx > 0:
        return text[:space_idx].strip()

    return text[:max_chars].strip()
