"""
Текстовые утилиты TelePush — спинтакс, классификация ссылок, тайминги.
"""
import re
import random
from typing import Optional
from urllib.parse import urlparse, parse_qs

INVITE_HASH_RE = re.compile(r"(?:joinchat/|\+)([A-Za-z0-9_-]{16,})")
ZWSP = "\u200B"
ZWNJ = "\u200C"
TRAILING_JUNK_RE = re.compile(r'[\s\)\]\.,;!…»›"\'`]+$')


def only_digits(s: str) -> str:
    return re.sub(r"\D+", "", str(s or ""))


def _sanitize_username(s: str) -> Optional[str]:
    s = s.strip().lstrip("@")
    s = s.replace(ZWSP, "").replace(ZWNJ, "")
    if not s:
        return None
    if not re.match(r"^[A-Za-z0-9_]{5,32}$", s):
        return None
    return s


def classify_link(raw: str) -> Optional[dict]:
    """Классифицирует ссылку: public, invite, bot_start, resolve."""
    s = str(raw or "").strip()
    if not s:
        return None
    s = s.split()[0]
    s = TRAILING_JUNK_RE.sub("", s)

    m = INVITE_HASH_RE.search(s)
    if m:
        return {"kind": "invite", "hash": m.group(1)}

    if s.startswith("tg:") and not s.startswith("tg://"):
        s = s.replace(":", "://", 1)
    if s.startswith("tg://"):
        u = urlparse(s)
        host = (u.netloc or "").lower()
        q = parse_qs(u.query or "")
        if host == "resolve":
            username = (q.get("domain") or [None])[0]
            return {"kind": "public", "username": username} if username else None
        if host in ("join", "privatejoin"):
            inv = (q.get("invite") or [None])[0]
            return {"kind": "invite", "hash": inv} if inv else None
        return None

    low = s.lower()
    if "t.me/" in low or "telegram.me/" in low:
        u = urlparse(s if "://" in s else "https://" + s)
        path = (u.path or "").strip("/")
        if not path:
            return None
        parts = path.split("/")
        first = parts[0]
        if first.startswith("+"):
            return {"kind": "invite", "hash": first[1:]}
        if first == "joinchat" and len(parts) > 1:
            return {"kind": "invite", "hash": parts[1]}
        uname = _sanitize_username(first)
        if uname:
            return {"kind": "public", "username": uname}

    if s.startswith("@"):
        uname = _sanitize_username(s)
        if uname:
            return {"kind": "public", "username": uname}

    return None


def canonical_key(link_info) -> str:
    """Canonical key for deduplication. Accepts dict or raw string."""
    if isinstance(link_info, str):
        t = classify_link(link_info)
        if not t:
            return f"bad:{link_info.strip().lower()}"
        link_info = t
    if not link_info:
        return ""
    if link_info["kind"] == "invite":
        return f"invite:{(link_info.get('hash') or '').lower()}"
    if link_info["kind"] == "public":
        return f"public:{(link_info.get('username') or '').lower()}"
    return ""


def spintax_expand(text: str) -> str:
    """Раскрывает спинтакс: {вариант1|вариант2|вариант3} → случайный."""
    def _replace(m):
        options = m.group(1).split("|")
        return random.choice(options) if options else ""
    return re.sub(r"\{([^{}]+)\}", _replace, str(text or ""))


def get_user_timing(owner_id: int, category: str, param: str, default: float,
                     phone: str = None) -> float:
    """Возвращает пользовательское значение таймингов."""
    try:
        import state
        ctx = state.owners.get(owner_id)
        if not ctx:
            if phone:
                return _region_fallback(phone, category, param, default)
            return default
        key = f"{category}_{param}"
        _phone = phone or ctx.active
        if _phone:
            acc_s = ctx.temp.get("acc_settings", {}).get(_phone, {})
            val = acc_s.get(key)
            if val is not None:
                return float(val)
        val = ctx.user_settings.get(key)
        if val is not None:
            return float(val)
        if _phone:
            return _region_fallback(_phone, category, param, default)
    except Exception:
        pass
    return default


def _region_fallback(phone: str, category: str, param: str, default: float) -> float:
    try:
        from config import get_region_preset
        preset = get_region_preset(phone)
        val = preset.get(f"{category}_{param}")
        if val is not None:
            return float(val)
    except Exception:
        pass
    return default


def validate_timings(owner_id: int, category: str) -> Optional[str]:
    """Проверяет тайминги — возвращает ошибку или None."""
    return None
