"""Encode and decode gaiia GlobalIDs.

A GlobalID looks like ``account_pwE84wz4Mw15LPX5YE8Z3o``: a snake-cased entity
type, an underscore, and a 22-char Flickr Base-58 encoding of the underlying
128-bit UUID. This matches the ``short-uuid`` library that gaiia uses on the
server side.
"""

from __future__ import annotations

import re
from uuid import UUID

_ALPHABET = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
_BASE = len(_ALPHABET)
_TARGET_LEN = 22
_TYPE_RE = re.compile(r"(?<!^)(?=[A-Z])")


def _snake_case(name: str) -> str:
    return _TYPE_RE.sub("_", name).lower()


def encode_global_id(type_name: str, uuid_str: str | UUID) -> str:
    """Build a GlobalID from a type name (e.g. ``Account``) and a UUID."""
    if isinstance(uuid_str, UUID):
        stripped = uuid_str.hex
    else:
        stripped = str(uuid_str).replace("-", "").lower()
        if len(stripped) != 32 or any(c not in "0123456789abcdef" for c in stripped):
            raise ValueError(f"not a valid uuid: {uuid_str!r}")

    num = int(stripped, 16)
    chars: list[str] = []
    if num == 0:
        chars.append(_ALPHABET[0])
    else:
        while num > 0:
            num, rem = divmod(num, _BASE)
            chars.append(_ALPHABET[rem])
    chars.reverse()
    short = "".join(chars).rjust(_TARGET_LEN, _ALPHABET[0])
    return f"{_snake_case(type_name)}_{short}"


def decode_global_id(global_id: str) -> tuple[str, str]:
    """Split a GlobalID into ``(type_snake_case, uuid_str)``."""
    if "_" not in global_id:
        raise ValueError(f"not a global id: {global_id!r}")
    type_part, _, short_id = global_id.rpartition("_")
    if not type_part or not short_id:
        raise ValueError(f"malformed global id: {global_id!r}")
    num = 0
    for ch in short_id:
        idx = _ALPHABET.find(ch)
        if idx < 0:
            raise ValueError(f"invalid base58 char {ch!r} in {global_id!r}")
        num = num * _BASE + idx
    hex_str = format(num, "x").rjust(32, "0")
    if len(hex_str) > 32:
        raise ValueError(f"global id decodes to oversized uuid: {global_id!r}")
    uuid_str = (
        f"{hex_str[0:8]}-{hex_str[8:12]}-{hex_str[12:16]}-{hex_str[16:20]}-{hex_str[20:32]}"
    )
    return type_part, uuid_str
