import io
from datetime import timedelta
import urllib3
import minio

from blackant.utils.logging import get_logger
from storage.interface import StorageInterface


class MinioStorageManager(StorageInterface):
    __instance = None

    def __init__(self, url, bucket_id, access_key, secret_key, secure, logger=None):  # pylint: disable=too-many-arguments
        self.__bucket_id = "".join(letter for letter in bucket_id if letter.isalnum()).lower()
        self.__logger = logger or get_logger("minio_client")

        self.__logger.debug("Creating minio client for %s with user %s", url, access_key)
        if not secure:
            self.__logger.warning(
                "Minio client is not using SSL. "
                "If the client does NOT communicate via Docker inside network, "
                "it should NOT run in production."
            )

        timeout = timedelta(minutes=5).seconds
        http_client_pool_manager = urllib3.PoolManager(
            timeout=urllib3.util.Timeout(connect=timeout, read=timeout),
            maxsize=10,
            cert_reqs="CERT_NONE",
            retries=urllib3.Retry(
                total=5, backoff_factor=0.2, status_forcelist=[500, 502, 503, 504]
            ),
        )

        self.__client = minio.Minio(
            url,
            access_key=access_key,
            secret_key=secret_key,
            secure=secure,
            http_client=http_client_pool_manager,
        )

        try:
            if not self.__client.bucket_exists(self.__bucket_id):
                self.__client.make_bucket(self.__bucket_id)
        except minio.error.AccessDenied:
            self.__logger.critical(
                "Cannot access object storage. Access is denied. "
                "Object storage url: %s , "
                "Access key: %s",
                url,
                access_key,
            )

    def download_file(self, local_path, file_id):
        try:
            self.__client.fget_object(self.__bucket_id, file_id, local_path)
        except minio.error.AccessDenied:
            self.__logger.critical(
                "Cannot download file from object storage. Access is denied. "
                "Bucket: %s, remote: %s, local: %s",
                self.__bucket_id,
                file_id,
                local_path,
            )

    def upload_file(self, local_path, file_id):
        try:
            self.__client.fput_object(self.__bucket_id, file_id, local_path)
        except minio.error.AccessDenied:
            self.__logger.critical(
                "Cannot upload file to object storage. Access is denied. "
                "Bucket: %s, remote: %s, local: %s",
                self.__bucket_id,
                file_id,
                local_path,
            )

    def download_string(self, file_id):
        try:
            obj = self.__client.get_object(self.__bucket_id, file_id)
            return obj.data.decode("utf-8")
        except minio.error.AccessDenied:
            self.__logger.critical(
                "Cannot download file as string from object storage. Access is denied. "
                "Bucket: %s, remote: %s",
                self.__bucket_id,
                file_id,
            )
        return ""

    def upload_string(self, input_string, file_id):
        try:
            value_as_bytes = input_string.encode("utf-8")
            value_as_a_stream = io.BytesIO(value_as_bytes)
            self.__client.put_object(
                self.__bucket_id, file_id, value_as_a_stream, length=len(value_as_bytes)
            )
        except minio.error.AccessDenied:
            self.__logger.critical(
                "Cannot upload string to file to object storage. Access is denied. "
                "Bucket: %s, remote: %s, string: %s",
                self.__bucket_id,
                file_id,
                input_string,
            )
