import traceback
import numpy as np
import httpx
from aiohttp_retry import ExponentialRetry, RetryClient

from nos import Client
from nos.transport import Transport

from hamunafs.backends.base import BackendBase
from aiofile import AIOFile, Writer, async_open

class WYSF(BackendBase):
    def __init__(self, cfg):
        key, secret, domain, default_bucket = cfg['key'], cfg['secret'], cfg['domain'], cfg['default_bucket']
        self.client = Client(key, secret, Transport)
        self.domain = domain
        self.default_bucket = default_bucket

    def get_token(self, filename):
        return self.auth.upload_token(self.default_bucket, filename)
    
    def geturl(self, entrypoint):
        bucket, bucket_name = entrypoint.split('/')
        return 'http://{}/{}_{}'.format(self.domain, bucket, bucket_name)

    def put(self, file, bucket, bucket_name, tmp=True):
        try:
            if tmp:
                _bucket = 'tmp_file_' + bucket
            else:
                _bucket = bucket
            b_name = '{}_{}'.format(_bucket, bucket_name)
            print('uploading...')
            # ret, info = put_file(token, b_name, file, version='v2')
            with open(file, 'rb') as f:
                resp = self.client.put_object(self.default_bucket, b_name, f)
            if resp.get('etag', None):
                print('upload success.')
                return True, '{}/{}'.format(_bucket, bucket_name)
            print('upload failed.')
            return False, '上传失败'
        except Exception as e:
            traceback.print_exc()
            return False, str(e)
    
    async def put_async(self, file, bucket, bucket_name, tmp=True):
        try:
            if tmp:
                _bucket = 'tmp_file_' + bucket
            else:
                _bucket = bucket
            b_name = '{}_{}'.format(_bucket, bucket_name)
            print('uploading...')
            async with async_open(file, 'rb') as f:
                resp = self.client.put_object(self.default_bucket, b_name, f)
            if resp.get('etag', None):
                print('upload success.')
                return True, '{}/{}'.format(_bucket, bucket_name)
            print('upload failed.')
            return False, '上传失败'
        except Exception as e:
            return False, str(e)

    def put_buffer(self, buffer, bucket, bucket_name):
        try:
            b_name = '{}_{}'.format(bucket, bucket_name)
            token = self.auth.upload_token(self.default_bucket, b_name)
            ret, info = put_data(token, b_name, buffer)
            if ret is not None:
                return True, '{}/{}'.format(bucket, bucket_name)
            return False, '上传失败'
        except Exception as e:
            return False, str(e)

    async def put_buffer_async(self, buffer, bucket, bucket_name):
        try:
            b_name = '{}_{}'.format(bucket, bucket_name)
            bucket = await self.cow.get_bucket(self.default_bucket)
            ret, info = await bucket.put_data(key=b_name, data=buffer)
            if ret is not None:
                return True, '{}/{}'.format(bucket, bucket_name)
            return False, '上传失败'
        except Exception as e:
            return False, str(e)

    def get(self, download_path, bucket, bucket_name, tries=0):
        try:
            if tries >= 3:
                return False, '下载出错'
            else:
                url = 'http://{}/{}'.format(self.domain, '{}_{}'.format(bucket, bucket_name))
                print(url)
                with httpx.stream('GET', url) as response:
                    if response.status_code == 200:
                        with open(download_path, mode='wb') as f:
                            for chunk in response.iter_bytes():
                                f.write(chunk)
                    else:
                        raise ValueError()
                return True, download_path
        except Exception as e:
            if tries >= 3:
                return False, str(e)
            else:
                return self.get(download_path, bucket, bucket_name, tries+1)

    async def get_async(self, download_path, bucket, bucket_name):
        try:
            url = 'http://{}/{}'.format(self.domain, '{}_{}'.format(bucket, bucket_name))
            print(url)
            retry_opts = ExponentialRetry(attempts=3)
            async with RetryClient(retry_options=retry_opts) as session:
                async with session.get(url) as response:
                    if response.status == 200:
                        with open(download_path, mode='wb') as f:
                            while True:
                                chunk = await response.content.read(4096)
                                if not chunk:
                                    break
                                f.write(chunk)
                    else:
                        raise ValueError()
            return True, download_path
        except Exception as e:
            return False, str(e)


            
