import re
from decimal import Decimal, InvalidOperation

from app.core.exceptions import ValidationError

USERNAME_RE = re.compile(r"^[A-Za-z][A-Za-z0-9_]{4,31}$")
TON_ADDRESS_RE = re.compile(r"^[A-Za-z0-9_-]{48}$")
TRON_ADDRESS_RE = re.compile(r"^T[1-9A-HJ-NP-Za-km-z]{33}$")


def clean_username(value: str) -> str:
    """Normalise `@user`, `t.me/user`, `https://t.me/user` to a bare username."""
    text = (value or "").strip()
    text = re.sub(r"^https?://", "", text, flags=re.IGNORECASE)
    text = re.sub(r"^(www\.)?t(elegram)?\.me/", "", text, flags=re.IGNORECASE)
    text = text.lstrip("@").strip().split("?")[0].split("/")[0]
    if not USERNAME_RE.match(text):
        raise ValidationError("invalid_username")
    return text


def clean_channel(value: str) -> str:
    text = (value or "").strip()
    if text.startswith("https://t.me/+") or text.startswith("t.me/+"):
        raise ValidationError("invalid_channel")
    return clean_username(text)


def clean_ton_address(value: str) -> str:
    text = (value or "").strip()
    if not TON_ADDRESS_RE.match(text):
        raise ValidationError("invalid_ton_address")
    return text


def clean_tron_address(value: str) -> str:
    text = (value or "").strip()
    if not TRON_ADDRESS_RE.match(text):
        raise ValidationError("invalid_tron_address")
    return text


def clean_amount(value: object, minimum: Decimal | None = None, maximum: Decimal | None = None) -> Decimal:
    try:
        amount = Decimal(str(value).strip().replace(",", ""))
    except (InvalidOperation, AttributeError, TypeError) as exc:
        raise ValidationError("invalid_amount") from exc
    if amount <= 0:
        raise ValidationError("invalid_amount")
    if minimum is not None and amount < minimum:
        raise ValidationError("amount_too_small")
    if maximum is not None and amount > maximum:
        raise ValidationError("amount_too_large")
    return amount


def clean_int(value: object, minimum: int = 1, maximum: int | None = None) -> int:
    try:
        number = int(str(value).strip().replace(",", ""))
    except (ValueError, TypeError) as exc:
        raise ValidationError("invalid_number") from exc
    if number < minimum:
        raise ValidationError("number_too_small")
    if maximum is not None and number > maximum:
        raise ValidationError("number_too_large")
    return number


def clean_text(value: str, max_length: int = 200) -> str:
    text = (value or "").strip()
    if not text:
        raise ValidationError("empty_text")
    if len(text) > max_length:
        raise ValidationError("text_too_long")
    return text
