"""Shared opaque keyset-pagination cursor.

A cursor is base64(``<iso8601_sort_key>|<row_id>``). The sort key is whatever
DESC-ordered timestamp a feed paginates on (gallery: effective_date; posts:
COALESCE(post_date, downloaded_at); artists: created_at). `decode_cursor` rejects
a malformed cursor with ValueError, which the API layer maps to HTTP 400.

This was hand-rolled identically in gallery_service and post_feed_service (with
artist_service importing gallery's copy). Two divergent copies of a cursor format
silently break pagination in whichever feed drifts, so it lives once here now
(DRY pattern sweep 2026-06-10).
"""

import base64
from datetime import datetime

CURSOR_SEPARATOR = "|"


def encode_cursor(sort_key: datetime, row_id: int) -> str:
    raw = f"{sort_key.isoformat()}{CURSOR_SEPARATOR}{row_id}"
    return base64.urlsafe_b64encode(raw.encode()).decode()


def decode_cursor(cursor: str) -> tuple[datetime, int]:
    try:
        raw = base64.urlsafe_b64decode(cursor.encode()).decode()
        ts_part, id_part = raw.split(CURSOR_SEPARATOR, 1)
        return datetime.fromisoformat(ts_part), int(id_part)
    except Exception as exc:
        raise ValueError(f"invalid cursor: {cursor!r}") from exc
