from typing import Union
import uuid
from typing import Optional
import struct


def timestamp_ns(
    s: Union[str, uuid.UUID, int],
    suppress_error=True,
) -> Optional[int]:
    """
    Recover the timestamp from a UUIDv7, passed in
    as a string, integer or a UUID object.

    If the UUID is not a version 7 UUID, either raise a ValueError
    or return None, depending on suppress_error.

    Usage:
    >>> uuid_to_datetime("1eb22fe4-3f0c-62b1-a88c-8dc55231702f")
    datetime.datetime(2020, 11, 10, 2, 41, 42, 182162)
    """
    if isinstance(s, uuid.UUID):
        x = s.bytes
    elif not s:
        x = b"\0" * 16
    elif isinstance(s, int):
        x = int.to_bytes(s, length=16, byteorder="big")
    else:  # String form that should look like a UUID
        int_uuid = int(str(s).replace("-", ""), base=16)
        x = int.to_bytes(int_uuid, length=16, byteorder="big")

    uuid_version = x[6] >> 4
    if uuid_version == 7:
        bits = struct.unpack(">IHHHHI", x)
        uuid_version = (bits[2] >> 12) & 0xF
        # uuid_variant = (bits[3] >> 62) & 0x3
        whole_secs = (bits[0] << 4) + (bits[1] >> 12)
        frac_binary = (
            ((bits[1] & 0x0FFF) << 26)
            + ((bits[2] & 0x0FFF) << 14)
            + ((bits[3] & 0x3FFF))
        )
        frac_ns, _ = divmod(frac_binary * 1_000_000_000, 1 << 38)
        ns_since_epoch = whole_secs * 1_000_000_000 + frac_ns
        return ns_since_epoch
    elif suppress_error:
        return None
    else:
        raise ValueError(
            f"{str(s)} is a version {uuid_version} UUID, \
not v7 so we cannot extract the timestamp."
        )