import os
import boto3
from botocore.errorfactory import ClientError


def get_client(access_key_id=None, secret_key=None,
               endpoint_url='https://storage.yandexcloud.net'):
    access_key_id = access_key_id or os.environ.get('AWS_ACCESS_KEY_ID')
    secret_key = secret_key or os.environ.get('AWS_SECRET_ACCESS_KEY')
    assert access_key_id, 'Empty S3 access_key_id, set AWS_ACCESS_KEY_ID'
    assert secret_key, 'Empty S3 secret_key, set AWS_SECRET_ACESS_KEY'
    return boto3.client(
        's3',
        endpoint_url=endpoint_url,
        aws_access_key_id=access_key_id,
        aws_secret_access_key=secret_key,
    )


def _exists(client, bucket, path):
    try:
        client.head_object(Bucket=bucket, Key=path)
        return True
    except ClientError as ex:
        if ex.response['Error']['Code'] == '404':
            return False
    raise 'Can not get status {} {}'.format(bucket, path)


def folder_exists(client, bucket, path):
    path = path.rstrip('/') + '/'
    return _exists(client, bucket, path)


def file_exists(client, bucket, path):
    path = path.rstrip('/')
    return _exists(client, bucket, path)


def exists(client, bucket, path):
    return folder_exists(client, bucket, path) or file_exists(client, bucket, path)


def mkdir(client, bucket, path):
    client.put_object(Bucket=bucket, Key=path, Body='')


def list(client, bucket, path, absolute=False):
    path = path.rstrip('/') + '/'
    visited_items = set()
    for key in client.list_objects(Bucket=bucket, Prefix=path)['Contents']:
        item = key['Key']
        if not item.startswith(path):
            continue
        item = item[len(path):].split('/')[0]
        if not item:
            continue
        if item in visited_items:
            continue
        visited_items.add(item)
        if absolute:
            item = os.path.join(path, item)
        yield item


def upload(client, bucket, local_path, storage_path):
    client.upload_file(local_path, bucket, storage_path)


def download(client, bucket, storage_path, local_path):
    client.download_file(bucket, storage_path, local_path)
