"""Best-effort extraction of a display name from mesh intro / prompt text (untrusted LAN peer)."""
from __future__ import annotations

import re
import unicodedata

MESH_NICKNAME_MAX_LEN = 80
MESH_INTRO_HINT_MAX_LEN = 96
INTRO_PARSE_MAX_INPUT_CHARS = 400
INTRO_CAPTURE_WINDOW_SECONDS = 300.0

_CONTROL_RE = re.compile(r"[\x00-\x1f\x7f-\x9f]")


def sanitize_mesh_nickname(raw: str) -> str:
    """Normalize whitespace; strip controls; clamp length for known_peers.json."""
    s = unicodedata.normalize("NFKC", (raw or "").strip())
    s = _CONTROL_RE.sub("", s)
    if len(s) > MESH_NICKNAME_MAX_LEN:
        s = s[:MESH_NICKNAME_MAX_LEN].rstrip()
    return s


def sanitize_mesh_intro_hint(raw: str) -> str:
    """Clamp and strip controls for machine-suggested intro line (display only)."""
    s = unicodedata.normalize("NFKC", (raw or "").strip())
    s = _CONTROL_RE.sub("", s).replace("\n", " ").replace("\r", " ")
    s = re.sub(r"\s+", " ", s).strip()
    if len(s) > MESH_INTRO_HINT_MAX_LEN:
        s = s[:MESH_INTRO_HINT_MAX_LEN].rstrip()
    return s


# Captures a short name token (letters, hyphen, apostrophe; 1–4 words).
_NAME_CHUNK = r"[A-Za-z][A-Za-z\-']*(?:\s+[A-Za-z][A-Za-z\-']*){0,3}"
_INTRO_PATTERNS = [
    re.compile(rf"(?i)\bI'm\s+({_NAME_CHUNK})\b"),
    re.compile(rf"(?i)\bI\s+am\s+({_NAME_CHUNK})\b"),
    re.compile(rf"(?i)\bmy\s+name\s+is\s+({_NAME_CHUNK})\b"),
    re.compile(rf"(?i)\bcall\s+me\s+({_NAME_CHUNK})\b"),
    re.compile(rf"(?i)\bthis\s+is\s+({_NAME_CHUNK})\b"),
]


def parse_intro_name_from_prompt(text: str) -> str | None:
    """Return a short display name if *text* looks like a self-introduction, else None."""
    if not text or not str(text).strip():
        return None
    chunk = str(text).strip()[:INTRO_PARSE_MAX_INPUT_CHARS]
    for rx in _INTRO_PATTERNS:
        m = rx.search(chunk)
        if not m:
            continue
        cand = (m.group(1) or "").strip()
        cand = sanitize_mesh_intro_hint(cand)
        if len(cand) < 2:
            continue
        return cand
    return None
