import copy
import os
import logging
import io

import sandbox.common
import sandbox.common.upload
import sandbox.common.types.misc

from retry.api import retry

SANDBOX_URL = "https://sandbox.yandex-team.ru"
SANDBOX_PROXY = "http://proxy.sandbox.yandex-team.ru"

logging.getLogger("sandbox.common.upload").setLevel(logging.WARNING)

try:
    # Python 2: "file" is built-in
    file_types = file, io.IOBase
except NameError:
    # Python 3: "file" fully replaced with IOBase
    file_types = (io.IOBase,)


class SandboxUploader(object):

    def __init__(self):
        pass

    @retry(tries=5)
    def upload(self, filenames, resource_owner, sandbox_token, resource_type="OTHER_RESOURCE", root_name=None, ttl=365, resource_description=None, resource_arch="any", attributes={}):
        """
        Primitive sandbox upload routine (retrying).

        Args:
            filenames: list of tuples (local_path, name_to_be_registered_by_in_sandbox) for each each file.
            resource_owner: (string) owner's username.
            sandbox_token: (string) sandbox oauth token of resource_owner.
            resource_type: (string) sandbox resource type, default: OTHER_RESOURCE.
            root_name: file name in sandbox (name of root folder), default: "root".
            ttl: (int or "inf") ttl for uploaded resource in days, default: 14.
            resource_description: (string) resource description.
            resource_arch: (string) architecture, default: "any". Options: ["any", "osx", "darwin", "linux", "freebsd"].
            attributes: (dict) resource attributes

        Returns:
            proxy url for uploaded resource.
        """

        resource_arch_list = ["any", "osx", "darwin", "linux", "freebsd"]
        assert resource_arch in resource_arch_list, "Invalid resource_arch '{}'. Allowed: {}".format(
            resource_arch, resource_arch_list
        )

        if resource_description is None:
            resource_description = ""
        if resource_arch == "darwin":
            resource_arch = "osx"

        assert isinstance(ttl, int) or ttl == "inf", "Invalid ttl '{}'".format(ttl)

        created_resource_id = None
        created_task_id = None

        files = []
        for local_name, register_name in filenames:
            f = open(local_name, 'rb')
            f.seek(0, os.SEEK_END)
            if root_name is None and len(filenames) == 1:
                sandbox_path = register_name
            else:
                sandbox_path = os.path.join(root_name, register_name)
            file_meta = sandbox.common.upload.HTTPHandle.FileMeta(
                f, f.tell(), sandbox_path
            )
            f.seek(0)

            files.append(file_meta)

        attributes["ttl"] = ttl
        attrs = ",".join("{}={}".format(key, value) for key, value in attributes.items())

        try:
            upload_handle = sandbox.common.upload.HTTPHandle(
                sandbox.common.upload.HTTPHandle.ResourceMeta(
                    resource_type, resource_arch, resource_owner, resource_description, attrs, release_to_yd=False
                ),
                sandbox_token, SANDBOX_URL, SANDBOX_PROXY, 0, *files
            )

            upload_states = sandbox.common.types.misc.Upload
            state, last_state, last_state_copy, task_state = (None,) * 4

            for state in upload_handle():
                if last_state == state:
                    # Operation continues.
                    if isinstance(state, upload_states.Prepare):
                        if last_state_copy.task_id != state.task_id:
                            created_task_id = state.task_id
                            link = "{}/task/{}/view".format(SANDBOX_URL, created_task_id)
                            logging.info('Created task {}'.format(link))

                        if last_state_copy.resource_id != state.resource_id:
                            created_resource_id = state.resource_id
                            logging.info("Resource #{} registered".format(created_resource_id))

                    if isinstance(state, upload_states.DataTransfer):
                        logging.info("Uploaded {} / {}".format(state.done, state.total))

                    if isinstance(state, upload_states.Share) and task_state != state.task_state:
                        task_state = state.task_state
                        logging.info("Task #{}: {}".format(created_task_id, state.task_state))

                last_state = state
                last_state_copy = copy.deepcopy(state)
        finally:
            for file_handle in files:
                if isinstance(file_handle.handle, file_types):
                    file_handle.handle.close()

        self.created_resource_id = created_resource_id
        self.created_task_id = created_task_id
        self.download_url = os.path.join(SANDBOX_PROXY, str(created_resource_id))
        return (self.created_resource_id, self.created_task_id, self.download_url)
