"""Hotness score — combining access frequency and recency decay."""

import math
from datetime import datetime, timezone


def hotness_score(
    active_count: int,
    updated_at: datetime | None,
    now: datetime | None = None,
    half_life_days: float = 7.0,
) -> float:
    """Calcula un score de hotness combinando frecuencia de acceso y recencia.

    Formula: sigmoid(log1p(active_count)) * exp_decay(age_days, half_life_days)

    El componente de frecuencia mapea conteos enteros al rango (0, 1) via sigmoid(log1p).
    El componente de recencia decae exponencialmente con vida media configurable.

    Args:
        active_count: Numero de accesos o activaciones. Debe ser >= 0.
        updated_at: Timestamp de la ultima actualizacion. None retorna 0.0.
        now: Momento de referencia para calcular la edad. Si es None usa datetime.now(UTC).
        half_life_days: Dias para que la recencia se reduzca a la mitad. Default 7.

    Returns:
        float en [0.0, 1.0]. Valores mas cercanos a 1.0 indican mayor hotness.
    """
    if updated_at is None:
        return 0.0

    # Componente de frecuencia: sigmoid(log1p(count)) mapea 0..inf -> (0.5, 1.0)
    freq = 1.0 / (1.0 + math.exp(-math.log1p(active_count)))

    # Componente de recencia: decaimiento exponencial
    if now is None:
        now = datetime.now(timezone.utc)

    # Normalizar ambos timestamps a UTC para comparacion segura
    if updated_at.tzinfo is None:
        updated_at = updated_at.replace(tzinfo=timezone.utc)
    if now.tzinfo is None:
        now = now.replace(tzinfo=timezone.utc)

    age_days = max((now - updated_at).total_seconds() / 86400.0, 0.0)
    decay_rate = math.log(2) / half_life_days
    recency = math.exp(-decay_rate * age_days)

    return freq * recency
