from datetime import UTC, datetime

from markupsafe import Markup, escape


_MONTHS = (
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
)


def _ensure_utc(dt: datetime) -> datetime:
    if dt.tzinfo is None:
        return dt.replace(tzinfo=UTC)
    return dt


def _format_date(then: datetime, now: datetime) -> str:
    month = _MONTHS[then.month - 1]
    if then.year == now.year:
        return f"{then.day} {month}"
    return f"{then.day} {month} {then.year}"


def _relative_label(seconds: int, past: bool) -> str:
    if seconds < 60:
        unit, n = "second", seconds
    elif seconds < 3600:
        unit, n = "minute", seconds // 60
    elif seconds < 86400:
        unit, n = "hour", seconds // 3600
    else:
        unit, n = "day", seconds // 86400
    plural = "" if n == 1 else "s"
    if past:
        return f"{n} {unit}{plural} ago"
    return f"in {n} {unit}{plural}"


def humanize_delta(then: datetime, now: datetime | None = None) -> str:
    if now is None:
        now = datetime.now(UTC)
    then = _ensure_utc(then)
    now = _ensure_utc(now)

    delta_seconds = int((now - then).total_seconds())
    abs_seconds = abs(delta_seconds)

    if abs_seconds == 0:
        return "now"

    if abs_seconds >= 7 * 86400:
        return _format_date(then, now)

    return _relative_label(abs_seconds, past=(delta_seconds > 0))


def format_time_html(then: datetime, now: datetime | None = None) -> Markup:
    if now is None:
        now = datetime.now(UTC)
    then_utc = _ensure_utc(then).astimezone(UTC)
    now = _ensure_utc(now)

    label = humanize_delta(then_utc, now=now)
    iso = then_utc.isoformat()
    title = then_utc.strftime("%Y-%m-%d %H:%M:%S UTC")
    return Markup(
        f'<time datetime="{escape(iso)}" title="{escape(title)}">'
        f"{escape(label)}</time>"
    )
