# Copyright (c) 2018-2026 Visual Collaboration Technologies Inc.
# All Rights Reserved.
"""Short UID generator for creating concise unique identifiers.

This module provides utilities for generating short, URL-safe unique identifiers
commonly used for temporary file/directory names, session IDs, or other scenarios
where brief, collision-resistant identifiers are needed.
"""

import base64
import hashlib
import secrets
import time

_MIN_LENGTH = 1
_MAX_LENGTH = 43  # SHA-256 (32 bytes) -> 43 usable base64 chars after stripping '='


class ShortUID:
    """Generate short, reasonably unique IDs typically used as a part of
    unique file or directory names.
    """

    __slots__ = ("entropy", "length")

    def __init__(self, entropy: str | None = None, length: int = 8):
        """Initialize the generator.

        Args:
            entropy: Optional base entropy seed.
            length: Number of characters in the generated ID (default: 8).

        Raises:
            TypeError: If *length* is not an integer or *entropy* is not
                a string/``None``.
            ValueError: If *length* is outside the range [1, 43].
        """
        if not isinstance(length, int) or isinstance(length, bool):
            raise TypeError(f"length must be an integer, got {type(length).__name__}")
        if length < _MIN_LENGTH or length > _MAX_LENGTH:
            raise ValueError(
                f"length must be between {_MIN_LENGTH} and {_MAX_LENGTH}, got {length}"
            )
        if entropy is not None and not isinstance(entropy, str):
            raise TypeError(f"entropy must be a string or None, got {type(entropy).__name__}")
        self.entropy = entropy or None
        self.length = length

    def generate(self, salt: str | None = None) -> str:
        """Generate a short, unique identifier.

        Args:
            salt: Additional salt or context string (e.g., filename).

        Raises:
            TypeError: If *salt* is not a string or ``None``.

        Returns:
            A short unique ID.
        """
        if salt is not None and not isinstance(salt, str):
            raise TypeError(f"salt must be a string or None, got {type(salt).__name__}")
        parts = (self.entropy or "", salt or "", str(time.time_ns()), secrets.token_hex(16))
        digest = hashlib.sha256("-".join(parts).encode()).digest()
        encoded = base64.urlsafe_b64encode(digest).decode("utf-8")
        return encoded.rstrip("=\n")[: self.length]

    @classmethod
    def quick(cls, length: int = 8) -> str:
        """Convenience method to quickly generate a short ID."""
        return cls(length=length).generate()

    def __repr__(self) -> str:
        return f"ShortUID(entropy={self.entropy!r}, length={self.length})"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, ShortUID):
            return NotImplemented
        return self.entropy == other.entropy and self.length == other.length

    def __hash__(self) -> int:
        return hash((self.entropy, self.length))
