##########################################################################
#
#  Copyright (c) 2026 - Datatailr Inc.
#  All Rights Reserved.
#
#  This file is part of Datatailr and subject to the terms and conditions
#  defined in 'LICENSE.txt'. Unauthorized copying and/or distribution
#  of this file, in parts or full, via any medium is strictly prohibited.
##########################################################################

from __future__ import annotations

from typing import Optional

from datatailr.acl import ACL
from datatailr.wrapper import dt__Blob

# Datatailr Blob API Client
__client__ = dt__Blob()


class Blob:
    """Interface for Datatailr's binary large object (blob) storage.

    Blob storage lets you persist and retrieve arbitrary files and binary data.
    Each blob is identified by a unique name (key/path).

    Notes:
        This class wraps Datatailr blob operations (list/copy/read/write/delete).
        Large reads/writes use temporary files internally.

    Examples:

        Basic file copy to/from blob storage:

        >>> from datatailr import Blob
        >>> b = Blob()
        >>> _ = open("/tmp/data.txt", "wb").write(b"qwerty123")
        >>> b.put_file("mybucket/data.txt", "/tmp/data.txt")
        >>> b.get_file("mybucket/data.txt", "/tmp/data_copy.txt")

        Read/write blob contents as bytes:

        >>> payload = b"hello world"
        >>> b.put("mybucket/hello.bin", payload)
        >>> data = b.get("mybucket/hello.bin")
        >>> data == payload
        True

        List and delete:

        >>> sorted(b.ls("mybucket/"), key=lambda x: x["name"])
        [{'is_file': True, 'last_modified': ..., 'name': 'data.txt', 'size': 9}, {'is_file': True, 'last_modified': ..., 'name': 'hello.bin', 'size': 11}]
        >>> b.delete("mybucket/hello.bin")
        >>> b.exists("mybucket/hello.bin")
        False
    """

    def ls(self, prefix: str, recursive: bool = False) -> list[str]:
        """
        List files in the specified prefix. If recursive is True, list files in subdirectories recursively.

        Args:
            prefix: The prefix to list files from.
            recursive: Whether to list files in subdirectories recursively.

        Returns:
            list: A list of file names in the specified path. If recursive is True, the list will contain files in subdirectories recursively.
        """
        if not prefix.startswith("blob://"):
            prefix = f"blob://{prefix}"
        return __client__.ls(prefix, recursive=recursive)

    def get_file(self, name: str, path: str):
        """
        Copy a blob file to a local file.

        Args:
            name (str): The name of the blob to retrieve.
            path (str): The path to store the blob as a file.
        """
        return __client__.cp(f"blob://{name}", path)

    def put_file(self, name: str, path: str, acl: Optional[ACL] = None):
        """
        Copy a local file to a blob.

        Args:
            name (str): The name of the blob to create.
            path (str): The path of the local file to copy.
            acl (ACL): The access control list for the blob.
        """
        if acl is None:
            return __client__.cp(path, f"blob://{name}")
        return __client__.cp(path, f"blob://{name}", **acl.to_cli_command())

    def exists(self, name: str) -> bool:
        """
        Check if a blob exists.

        Args:
            name (str): The name of the blob to check.

        Returns:
            bool: True if the blob exists, False otherwise.
        """
        return __client__.exists(name)

    def delete(self, name: str):
        """
        Delete a blob.

        Args:
            name (str): The name of the blob to delete.
        """
        return __client__.rm(name)

    def get(self, name: str) -> bytes:
        """
        Get a blob object.

        Args:
            name (str): The name of the blob to retrieve.

        Returns:
            Blob: The blob object.
        """
        return __client__.get(f"blob://{name}", _raw_output=True)

    def put(self, name: str, blob: bytes | str, acl: Optional[ACL] = None):
        """
        Put a blob object into the blob storage.

        Args:
            name (str): The name of the blob to create.
            blob: The blob object to store.
            acl (ACL): The access control list for the blob.
        """
        if isinstance(blob, str):
            blob = blob.encode("utf-8")

        if acl is None:
            return __client__.put(f"blob://{name}", _stdin=blob)
        return __client__.put(f"blob://{name}", _stdin=blob, **acl.to_cli_command())

    def acls(self, path: str) -> ACL:
        """
        Retrieve the access control list (ACL) for a specific blob path.

        Args:
            path (str): The blob path for which to retrieve the ACL.

        Returns:
            ACL: The ACL for the blob path.
        """
        acl: dict[str, list[int]] = __client__.get_acl(path) or {}
        return ACL.from_dict(acl)

    def set_acls(self, path: str, acls: ACL) -> None:
        """
        Set the access control list (ACL) for a specific blob path.

        Args:
            path (str): The blob path for which to set the ACL.
            acls (ACL): The ACL to apply.
        """
        __client__.set_acl(path, **acls.to_cli_command())
        return None
