"""
Funzioni di utilità per DataProfiler.

Contiene helper per inferenza tipi, formattazione numeri
e altre operazioni comuni usate dai vari moduli.
"""

from datetime import datetime


def is_numeric(value) -> bool:
    """Verifica se un valore è convertibile a float.

    Args:
        value: Il valore da verificare.

    Returns:
        True se il valore è numerico, False altrimenti.
    """
    if value is None:
        return False
    try:
        float(value)
        return True
    except (ValueError, TypeError):
        return False


def is_boolean(value) -> bool:
    """Verifica se un valore rappresenta un booleano.

    Riconosce: True, False, true, false, 1, 0, sì, no, si, yes, no.

    Args:
        value: Il valore da verificare.

    Returns:
        True se il valore è interpretabile come booleano.
    """
    if value is None:
        return False
    normalized = str(value).strip().lower()
    return normalized in {
        "true", "false", "1", "0",
        "sì", "si", "no", "yes",
        "vero", "falso",
    }


# Formati data comuni da provare in ordine
DATE_FORMATS = [
    "%Y-%m-%d",
    "%d/%m/%Y",
    "%d-%m-%Y",
    "%Y/%m/%d",
    "%d.%m.%Y",
    "%Y-%m-%d %H:%M:%S",
    "%d/%m/%Y %H:%M:%S",
    "%d/%m/%Y %H:%M",
    "%Y-%m-%dT%H:%M:%S",
    "%d %B %Y",
    "%d %b %Y",
]


def parse_date(value: str) -> datetime | None:
    """Prova a parsare una stringa come data usando formati comuni.

    Args:
        value: La stringa da parsare.

    Returns:
        Un oggetto datetime se il parsing ha successo, None altrimenti.
    """
    if value is None:
        return None
    text = str(value).strip()
    if not text:
        return None

    for fmt in DATE_FORMATS:
        try:
            return datetime.strptime(text, fmt)
        except ValueError:
            continue
    return None


def is_datetime(value) -> bool:
    """Verifica se un valore è parsabile come data.

    Args:
        value: Il valore da verificare.

    Returns:
        True se il valore è una data riconoscibile.
    """
    return parse_date(value) is not None


def is_empty(value) -> bool:
    """Verifica se un valore è vuoto o mancante.

    Args:
        value: Il valore da verificare.

    Returns:
        True se il valore è None, stringa vuota o solo spazi.
    """
    if value is None:
        return True
    return str(value).strip() == ""


def format_number(value: float, decimals: int = 2) -> str:
    """Formatta un numero con separatore delle migliaia e decimali.

    Args:
        value: Il numero da formattare.
        decimals: Numero di cifre decimali.

    Returns:
        Stringa formattata (es: "1,234.56").
    """
    return f"{value:,.{decimals}f}"


def format_bytes(size_bytes: int) -> str:
    """Formatta una dimensione in byte in formato leggibile.

    Args:
        size_bytes: Dimensione in byte.

    Returns:
        Stringa formattata (es: "1.5 MB").
    """
    if size_bytes < 1024:
        return f"{size_bytes} B"
    elif size_bytes < 1024 ** 2:
        return f"{size_bytes / 1024:.1f} KB"
    elif size_bytes < 1024 ** 3:
        return f"{size_bytes / 1024 ** 2:.1f} MB"
    else:
        return f"{size_bytes / 1024 ** 3:.1f} GB"


def truncate(text: str, max_length: int = 50) -> str:
    """Tronca una stringa aggiungendo '...' se troppo lunga.

    Args:
        text: La stringa da troncare.
        max_length: Lunghezza massima.

    Returns:
        Stringa troncata se necessario.
    """
    if len(text) <= max_length:
        return text
    return text[:max_length - 3] + "..."
