import json
import logging
from .base import WeTransferBase
from .file import File

LOG = logging.getLogger("wetransfer")
LOG.addHandler(logging.NullHandler())
LOG.setLevel(logging.INFO)


class WeTransfer(WeTransferBase):
    WE_ENDPOINT_DEV = 'https://dev.wetransfer.com'

    def __finalize_transfer(self, transfer_id):
        """
        Finalize transfer.
        :param transfer_id: transfer id.
        :return: WeTransfer URL
        """
        _, body = self.put('transfers/%s/finalize' % transfer_id, status=200)
        return body['url']

    def __complete_file_upload(self, transfer_id, file_id, part_numbers):
        """
        Complete file upload.
        :param transfer_id: transfer id
        :param file_id: file id
        :param part_numbers: part numbers
        :return: None
        """
        data = {'part_numbers': part_numbers}
        LOG.debug(json.dumps(data, sort_keys=True, indent=2, separators=(',', ': ')))
        self.put('transfers/%s/files/%s/upload-complete' % (transfer_id, file_id),
                 data=json.dumps(data), status=200)

    def __request_upload_url(self, transfer_id, file_id, part_number):
        """
        Request special upload url, which is tailored for AWS S3
        :param transfer_id: transfer id
        :param file_id: file id
        :param part_number: part number
        :return: AWS S3 upload url
        """
        _, body = self.get('transfers/%s/files/%s/upload-url/%s' % (transfer_id, file_id, part_number), status=200)
        return body['url']

    def __create_transfer(self, message, files):
        """
        Create a new transfer.
        :param message: Message that goes with the transfer
        :param files: An array of files
        :return:
        """
        files_stream = [{'name': file.name, 'size': file.size} for file in files]
        data = {'message': message, 'files': files_stream}

        _, body = self.post('transfers', data=json.dumps(data), status=201)

        LOG.debug(json.dumps(body, sort_keys=True, indent=2, separators=(',', ': ')))

        files_info = body['files']
        for i in range(len(files_info)):
            file_info = files_info[i]
            multipart = file_info['multipart']

            file = files[i]
            file.id = file_info['id']
            file.part_numbers = multipart['part_numbers']
            file.chunk_size = multipart['chunk_size']

        return body['id']

    def upload_files(self, message, filepaths):
        """
        Main entrypoint for this class. Pass in a message and a list of filepaths to upload.
        :param message: Message to go with uploads
        :param filepaths: A list of filepaths of files to upload
        :return: The download URL generated by WeTransfer
        """
        files = [File(filepath) for filepath in filepaths]
        transfer_id = self.__create_transfer(message, files)
        for file in files:
            part_number = 1
            with open(file.path, 'rb') as fh:
                while True:
                    bytes_read = fh.read(file.chunk_size)
                    if not bytes_read:  # empty string?
                        break

                    url = self.__request_upload_url(transfer_id, file.id, part_number)
                    self.s3_file_upload(url, bytes_read)
                    part_number += 1

            self.__complete_file_upload(transfer_id, file.id, file.part_numbers)

        return self.__finalize_transfer(transfer_id)

    def upload_file(self, message, file_path):
        """
        Upload a single file.
        :param message: Message
        :param file_path: Path of file to upload
        :return: The download URL generated by WeTransfer
        """
        return self.upload_files(message, [file_path])
