import threading
from typing import Callable, Optional, TYPE_CHECKING

from django.conf import settings
from django.core.files.storage import FileSystemStorage
from threading import local

if TYPE_CHECKING:
    from sandbox.latchstring.models import User

_thread_locals = threading.local()


# ---------------------------------------------------------------
def normalize_ltree_path(path: str) -> str:
    """
    Redwood represents category codes by their OS path instead of
    ltree path (slash delimited instead of dot delimited). Users will
    frequently search with slash-delimited values, so convert them.

    Some legacy ACLs are hyphenated which is an illegal character,
    so replace hyphen with underscore, rather than crash query.

    >>> normalize_ltree_path('agriculture')
    agriculture

    >>> normalize_ltree_path('messaging/telegram')
    messaging.telegram

    >>> normalize_ltree_path('youtube-trailer')
    youtube_trailer
    """
    try:
        return path.replace('/', '.').replace('-', '_')
    except AttributeError:
        return path


# -------------------------------------------------------------------------
class LCFileSystemStorage(FileSystemStorage):
    def get_available_name(self, name: str, max_length: int | None = None) -> str:
        """
        Override to delete name if file already exists.
        The delete method performs the existence check.
        """
        self.delete(name)
        return name


# -------------------------------------------------------------------------
# *current_user functions based on https://github.com/PaesslerAG/django-currentuser
# -------------------------------------------------------------------------


# -------------------------------------------------------------------------
def do_set_current_user(user_fun: Callable) -> None:
    setattr(_thread_locals, settings.LOCAL_USER_ATTR_NAME, user_fun.__get__(user_fun, local))


# -------------------------------------------------------------------------
def set_current_user(user: Optional['User'] = None) -> None:
    """
    Sets current user in local thread.
    Can be used as a hook e.g. for shell jobs (when request object is not
    available).
    """
    do_set_current_user(lambda self: user)


# -------------------------------------------------------------------------
def get_current_user() -> Optional['User']:
    current_user = getattr(_thread_locals, settings.LOCAL_USER_ATTR_NAME, None)
    if callable(current_user):
        return current_user()
    return current_user


__all__ = (
    'LCFileSystemStorage',
    'set_current_user',
    'get_current_user',
    'normalize_ltree_path',
    'do_set_current_user',
)
