import datetime
import os
import shutil
import time

import xlrd
from loguru import logger
from minio import Minio, ResponseError

from miniMinio.utils import get_attributes_minio, get_attributes_local, get_attributes_single_local

exclude = {".minio.sys"}


def create_connection(hostname,
                      access_key=None,
                      secret_key=None,
                      secure=True):
    """

    :param hostname: string 'hostname:port' for Minio File server OR 'local' for local file server
    :param access_key: access key
    :param secret_key:
    :param secure:
    :return:
    """
    if hostname == "local" or hostname == "remote":
        return hostname
    else:
        client = Minio(hostname,
                       access_key=access_key,
                       secret_key=secret_key,
                       secure=secure)

    return client


def get_buckets(client, remote_dir=None):
    """
    :param client: Minio connection object, or 'local' for local filesystem. If remote, add in directory
    :return:
    """

    store = []
    if client == "local" or client == "remote":
        if client is "local":
            remote_dir = os.getcwd()
        for dirpath, dirs, filenames in os.walk(remote_dir, topdown=True):
            dirs[:] = set(dirs) - exclude
            for f in filenames:
                if f[0] == ".":
                    continue
                else:
                    path = os.path.abspath(os.path.join(dirpath, f))
                    store.append((path, datetime.datetime.strptime(time.ctime(os.path.getctime(path)),
                                                                   "%a %b %d %H:%M:%S %Y")))
    else:
        all_buckets = client.list_buckets()
        for bucket in all_buckets:
            store.append((bucket.name, bucket.creation_date))
    return store


def get_all_object_details_in_bucket(client, bucket_name=None, attributes=["object_name", "last_modified"],
                                     filter_object=("file", "folder")):
    """
    :param attributes: list Desired attributes to return for every object
    :param client: Minio connection object
    :param bucket_name: str bucket name, or file path of root to start search from
    :param filter_object: str 'file', 'folder', 'excel'
    :return:
    """
    store = []
    if client == "local" or client == "remote":
        if client is "local":
            bucket_name = os.getcwd()
        for path, subdirs, files in os.walk(bucket_name, topdown=True):
            subdirs[:] = set(subdirs) - exclude
            if ("file" in filter_object) or ("excel" in filter_object):
                # file or excel
                store = get_attributes_local(files, path, store, filter_object)
            if "folder" in filter_object:
                store = get_attributes_local(subdirs, path, store, filter_object)
    else:
        all_objects = client.list_objects(bucket_name, prefix='', recursive=True)
        for s3_object in all_objects:
            # Check if folder
            if (s3_object.object_name[-1] == "/") and ("folder" in filter_object):
                store = get_attributes_minio(attributes, s3_object, store)
                continue
            # Check if file
            elif (s3_object.object_name[-1] != "/") and ("file" in filter_object):
                store = get_attributes_minio(attributes, s3_object, store)
                continue
            # Check if excel
            elif "excel" in filter_object:
                if (s3_object.object_name[-4:] == "xlsx") or (s3_object.object_name[-3:] == "xls"):
                    store = get_attributes_minio(attributes, s3_object, store)
                    continue
    return store


def get_object_details_in_bucket(client, object_name, bucket_name=None, attributes=["object_name", "last_modified"]):
    """

    :param client:
    :param object_name:
    :param bucket_name:
    :param attributes:
    :return:
    """
    # TODO: Test
    store = []
    if client == "local" or client == "remote":
        path = object_name
        store = get_attributes_single_local(path, attributes)

    else:
        all_objects = client.list_objects(bucket_name, prefix='', recursive=True)
        for s3_object in all_objects:
            # Check if folder
            if object_name.lower() not in s3_object.object_name.lower():
                continue
            else:
                store = get_attributes_minio(attributes, s3_object, store)

    return store[0]


def get_object_path(client, bucket_name, filename):
    # TODO: Test
    # TODO: local
    store = []
    store_error = []
    counter = [0, 0]
    output = []
    all_objects = client.list_objects(bucket_name, prefix='', recursive=True)
    for s3_object in all_objects:
        # Check if folder
        if filename.lower() not in s3_object.object_name.lower():
            counter[1] += 1
            store_error.append(getattr(s3_object, 'object_name'))
        else:
            counter[0] += 1
            store.append(getattr(s3_object, 'object_name'))
    output.extend([store, store_error, counter])
    return output


def get_single_object_from_bucket_to_file(client, object_name, dir_path, bucket_name=None):
    """
    :param client:
    :param bucket_name:
    :param object_name:
    :param dir_path: desired local path relative to the working directory
    :return: transfers files to dir_path
    """
    if client == "local" or client == "remote":
        shutil.copy2(object_name, dir_path)
    else:
        try:
            client.fget_object(bucket_name, object_name, dir_path)
        except ResponseError as err:
            logger.error(err)
    return


def get_single_object_content_bytes(client, object_name, bucket_name=None):
    """
    :param client:
    :param bucket_name:
    :param object_name:
    :return:
    """
    if client == "local" or client == "remote":
        in_file = open(object_name, "rb")
        data = in_file.read()
        return data
    else:
        result = []
        try:
            data = client.get_object(bucket_name, object_name)
            for d in data.stream(32 * 1024):
                result.append(d)
            return b''.join(result)
        except ResponseError as err:
            return err


def get_single_excel_file_content(client, excel_file_object, bucket_name=None):
    """
    :param client:
    :param bucket_name:
    :param excel_file_object:
    :return:
    """
    if client == "local" or client == "remote":
        return xlrd.open_workbook(excel_file_object)
    else:
        result = []
        try:
            data = client.get_object(bucket_name, excel_file_object)
            for d in data.stream(32 * 1024):
                result.append(d)
            return xlrd.open_workbook(file_contents=b''.join(result))
        except ResponseError as err:
            return err
