"""Prompt normalization, yes/no detection, and OpenClaw reply value extraction."""

import re


def _normalize_prompt(text: str) -> str:
    return " ".join(str(text or "").split()).strip()


def _extract_reply_value(prompt: str) -> str:
    raw = str(prompt or "").strip()
    low = raw.lower()
    prefixes = ("ui confirmation:", "ui selection:", "ui input:")
    for p in prefixes:
        if low.startswith(p):
            return raw[len(p):].strip()
    return raw


def _is_yes(text: str) -> bool:
    return str(text or "").strip().lower() in {"y", "yes", "true", "1", "ok", "confirm"}


def _is_no(text: str) -> bool:
    return str(text or "").strip().lower() in {"n", "no", "false", "0", "cancel"}


def _is_openclaw_retry_confirm(text: str) -> bool:
    low = str(text or "").strip().lower()
    if low in {"retry", "try again", "again"}:
        return True
    return bool(re.match(r"^(retry|try again|again)\b", low))


def _is_openclaw_setup_yes(text: str) -> bool:
    low = str(text or "").strip().lower()
    if _is_yes(low) or _is_openclaw_retry_confirm(low):
        return True
    return bool(re.match(r"^(yes|yep|yeah|ok|okay|confirm|retry|proceed|go ahead)\b", low))


def _is_openclaw_setup_no(text: str) -> bool:
    low = str(text or "").strip().lower()
    if _is_no(low):
        return True
    return bool(re.match(r"^(no|nah|nope|cancel|abort|stop)\b", low))


def _is_openclaw_skip_reply(text: str) -> bool:
    low = str(text or "").strip().lower()
    if low in {"skip", "none", "n/a", "na", "-", "not applicable", "not-applicable"}:
        return True
    return bool(re.match(r"^(skip|none|n/?a|not[ -]?applicable)\b", low))


def _is_openclaw_cancel_reply(text: str) -> bool:
    low = str(text or "").strip().lower()
    if low in {"cancel", "abort", "stop", "nevermind", "never mind"}:
        return True
    return bool(re.match(r"^(cancel|abort|stop|nevermind|never mind)\b", low))


def _is_openclaw_clear_reply(text: str) -> bool:
    low = str(text or "").strip().lower()
    if low in {"clear", "remove", "reset"}:
        return True
    return bool(re.match(r"^(clear|remove|reset)\b", low))


def _looks_like_openclaw_compact_value(text: str) -> bool:
    raw = str(text or "").strip()
    if not raw:
        return False
    return bool(re.fullmatch(r"[A-Za-z0-9_./:@+\-]+(?:\s*,\s*[A-Za-z0-9_./:@+\-]+)*", raw))


def _parse_csv_values(text: str) -> list[str]:
    vals: list[str] = []
    for raw in str(text or "").split(","):
        token = raw.strip()
        if token and token not in vals:
            vals.append(token)
    return vals


def _is_valid_telegram_id(token: str) -> bool:
    return bool(re.fullmatch(r"-?\d{5,20}", str(token or "").strip()))


def _extract_openclaw_model_reply_value(text: str) -> str:
    raw = str(text or "").strip()
    if not raw:
        return ""
    if _is_openclaw_skip_reply(raw):
        return raw
    if _looks_like_openclaw_compact_value(raw):
        return raw
    patterns = (
        r"^(?:use|set|model(?:\s+name)?(?:\s+to)?|it(?:'s| is))\s+['\"]?([A-Za-z0-9_./:@+\-]+)['\"]?(?:\s+(?:please|pls|thanks|thank you))?$",
        r"^['\"]?([A-Za-z0-9_./:@+\-]+)['\"]?\s+(?:please|pls|thanks|thank you)$",
    )
    for pattern in patterns:
        match = re.match(pattern, raw, flags=re.IGNORECASE)
        if match:
            return str(match.group(1) or "").strip()
    return ""


def _extract_openclaw_provider_key_reply_value(text: str) -> str:
    raw = str(text or "").strip()
    if not raw:
        return ""
    if _is_openclaw_skip_reply(raw):
        return raw
    if _looks_like_openclaw_compact_value(raw):
        return raw
    assign = re.match(
        r"^(?:[A-Za-z_][A-Za-z0-9_]*\s*[:=]\s*)['\"]?([A-Za-z0-9_./:@+\-]+)['\"]?$",
        raw,
        flags=re.IGNORECASE,
    )
    if assign:
        return str(assign.group(1) or "").strip()
    patterns = (
        r"^(?:api\s+key|key|token|secret)(?:\s+(?:is|=|:))?\s+['\"]?([A-Za-z0-9_./:@+\-]+)['\"]?(?:\s+(?:please|pls|thanks|thank you))?$",
        r"^(?:here(?:'s| is)\s+)?(?:the\s+)?(?:api\s+)?key(?:\s+(?:is|=|:))?\s+['\"]?([A-Za-z0-9_./:@+\-]+)['\"]?(?:\s+(?:please|pls|thanks|thank you))?$",
    )
    for pattern in patterns:
        match = re.match(pattern, raw, flags=re.IGNORECASE)
        if match:
            return str(match.group(1) or "").strip()
    return ""


def _extract_openclaw_telegram_admin_reply_value(text: str) -> str:
    raw = str(text or "").strip()
    if not raw:
        return ""
    if _is_openclaw_skip_reply(raw) or _is_openclaw_clear_reply(raw):
        return raw
    if re.fullmatch(r"-?\d+(?:\s*,\s*-?\d+)*", raw):
        return ",".join(_parse_csv_values(raw))
    ids = re.findall(r"(?<!\d)-?\d{5,20}(?!\d)(?!:[A-Za-z0-9_])", raw)
    if not ids:
        return ""
    ordered_unique: list[str] = []
    seen: set[str] = set()
    for value in ids:
        token = str(value or "").strip()
        if not token or token in seen:
            continue
        seen.add(token)
        ordered_unique.append(token)
    return ",".join(ordered_unique)


def _extract_openclaw_alert_chat_reply_value(text: str) -> str:
    raw = str(text or "").strip()
    if not raw:
        return ""
    if _is_openclaw_skip_reply(raw) or _is_openclaw_clear_reply(raw):
        return raw
    if _is_valid_telegram_id(raw):
        return raw
    match = re.search(r"(?<!\d)-?\d{5,20}(?!\d)(?!:[A-Za-z0-9_])", raw)
    if match:
        return str(match.group(0) or "").strip()
    return ""


def _extract_openclaw_telegram_token_reply_value(text: str) -> str:
    raw = str(text or "").strip()
    if not raw:
        return ""
    if _is_openclaw_skip_reply(raw):
        return raw
    token_pattern = r"(?<![A-Za-z0-9_])\d{5,20}:[A-Za-z0-9_\-]{10,}(?![A-Za-z0-9_])"
    if re.fullmatch(token_pattern, raw):
        return raw
    match = re.search(token_pattern, raw)
    if match:
        return str(match.group(0) or "").strip()
    return ""


def _extract_openclaw_base_url_reply_value(text: str) -> str:
    raw = str(text or "").strip()
    if not raw:
        return ""
    if _is_openclaw_skip_reply(raw):
        return raw
    if re.fullmatch(r"https?://[^\s'\"<>]+", raw):
        return raw
    if _looks_like_openclaw_compact_value(raw) and ("://" in raw or ":" in raw):
        return raw
    url_match = re.search(r"https?://[^\s'\"<>]+", raw)
    if url_match:
        return str(url_match.group(0) or "").strip()
    host_match = re.search(
        r"(?<![A-Za-z0-9_./:@+\-])(?:localhost|127\.0\.0\.1|[A-Za-z0-9.-]+):\d{2,5}(?:/[^\s'\"<>]*)?",
        raw,
        flags=re.IGNORECASE,
    )
    if host_match:
        return str(host_match.group(0) or "").strip()
    return ""


def _extract_openclaw_armory_skills_reply_value(text: str) -> str:
    raw = str(text or "").strip()
    if not raw:
        return ""
    if _is_openclaw_skip_reply(raw) or _is_openclaw_clear_reply(raw):
        return raw
    if _looks_like_openclaw_compact_value(raw):
        return raw
    patterns = (
        r"^(?:armory\s+skills?|skills?)(?:\s+(?:are|is|=|:|to))?\s+(.+)$",
        r"^(?:use|set|add|inject)(?:\s+armory)?\s+skills?(?:\s+to)?\s+(.+)$",
    )
    candidate = ""
    for pattern in patterns:
        match = re.match(pattern, raw, flags=re.IGNORECASE)
        if match:
            candidate = str(match.group(1) or "").strip()
            break
    if not candidate:
        return ""
    cleaned: list[str] = []
    for part in _parse_csv_values(candidate):
        token = re.sub(r"^(?:and|the)\s+", "", str(part).strip(), flags=re.IGNORECASE)
        token = re.sub(r"\s+(?:please|pls|thanks|thank you)$", "", token, flags=re.IGNORECASE).strip()
        if not token:
            continue
        if not _looks_like_openclaw_compact_value(token):
            return ""
        cleaned.append(token)
    return ",".join(cleaned) if cleaned else ""


def _extract_openclaw_skill_paths_reply_value(text: str) -> str:
    raw = str(text or "").strip()
    if not raw:
        return ""
    if _is_openclaw_skip_reply(raw) or _is_openclaw_clear_reply(raw):
        return raw
    if _looks_like_openclaw_compact_value(raw):
        return raw
    patterns = (
        r"^(?:skill\s+(?:directories|dirs|paths?)|skills?\s+(?:directories|dirs|paths?)|paths?)(?:\s+(?:are|is|=|:|to))?\s+(.+)$",
        r"^(?:use|set|add)(?:\s+skill\s+(?:directories|dirs|paths?))?(?:\s+to)?\s+(.+)$",
    )
    candidate = ""
    for pattern in patterns:
        match = re.match(pattern, raw, flags=re.IGNORECASE)
        if match:
            candidate = str(match.group(1) or "").strip()
            break
    if not candidate:
        return ""
    cleaned: list[str] = []
    for part in _parse_csv_values(candidate):
        token = re.sub(r"^(?:and|the)\s+", "", str(part).strip(), flags=re.IGNORECASE)
        token = re.sub(r"\s+(?:please|pls|thanks|thank you)$", "", token, flags=re.IGNORECASE).strip()
        if not token:
            continue
        if not _looks_like_openclaw_compact_value(token):
            return ""
        cleaned.append(token)
    return ",".join(cleaned) if cleaned else ""
