import re
from typing import NewType, cast

import ulid
from ulid.hints import Buffer

type JSONScalar = int | float | str | None
type JSON = JSONScalar | list["JSON"] | dict[str, "JSON"]
type JSONObject = dict[str, JSON]


class ULID(ulid.ULID):
    """Extended ULID type.

    Same as ulid.ULID, but allows initializing without a buffer, to make
    it easier to use the class as a standard factory.

    For more information about ULIDs, see https://github.com/ulid/spec.
    """

    _pattern = re.compile(r"^[0-9A-HJKMNP-TV-Z]{26}$")

    def __init__(self, buffer: Buffer | ulid.ULID | str | None = None):
        if isinstance(buffer, str):
            if not self._pattern.search(buffer):
                raise ValueError("Invalid ULID.")
            buffer = ulid.from_str(buffer)
            assert isinstance(buffer, ulid.ULID)

        if isinstance(buffer, ulid.ULID):
            buffer = cast(memoryview, buffer.memory)
        elif buffer is None:
            buffer = cast(memoryview, ulid.new().memory)

        super().__init__(buffer)


AwardId = NewType("AwardId", ULID)
GroupId = NewType("GroupId", ULID)
ImdbMovieId = NewType("ImdbMovieId", str)  # Pattern: ttXXXXXXXX
ImdbRating = NewType("ImdbRating", float)  # Value range: [1.0, 10.0]
ImdbUserId = NewType("ImdbUserId", str)  # Pattern: urXXXXXXXX
MovieId = NewType("MovieId", ULID)
MovieIdStr = NewType("MovieIdStr", str)
RatingId = NewType("RatingId", ULID)
Score100 = NewType("Score100", int)  # Value range: [0, 100]
UserId = NewType("UserId", ULID)
UserIdStr = NewType("UserIdStr", str)
