import asyncio
import concurrent
import os

from maps_adv.config_loader import Config
from maps_adv.common.third_party_clients.base.exceptions import ClientException
from maps_adv.common.third_party_clients.juggler import JugglerClient
from maps_adv.common.helpers.timing import async_time_diff

from sandbox.common.types.task import Status
from sandbox.common.types.misc import OSFamily, Upload
from sandbox.common.upload import HTTPHandle


class SandboxClientException(ClientException):
    pass


class SandboxClient:
    UPLOAD_TIMEOUT: int = 300

    __slots__ = ["_uploader", "_juggler_client"]

    def __init__(
        self,
        config: Config,
        attrs: dict,
        uploaded_dir: str,
        juggler_client: JugglerClient,
    ):
        self._uploader = HTTPHandle(
            HTTPHandle.ResourceMeta(
                type=config.SANDBOX_EXPORT_RESOURCE_TYPE,
                arch=OSFamily.ANY,
                owner=config.SANDBOX_USER,
                description="",
                attributes=attrs,
                release_to_yd=None,
            ),
            config.SANDBOX_TOKEN,
            config.SANDBOX_URL,
            config.SANDBOX_PROXY_URL,
            None,  # total_wait
            *[
                HTTPHandle.FileMeta(
                    handle=entry.path,
                    size=entry.stat().st_size,
                    name=os.path.join(os.path.basename(uploaded_dir), entry.name),
                )
                for entry in os.scandir(uploaded_dir)
                if entry.is_file()
            ],
        )

        self._juggler_client = juggler_client

    def _upload_real(self):
        resource_url = None
        try:
            for state in self._uploader():
                if (
                    isinstance(state, Upload.Share)
                    and state.task_state == Status.SUCCESS
                    and state.meta
                ):
                    resource_url = state.meta["http"]["proxy"]
        except Exception as e:
            raise SandboxClientException from e
        return resource_url

    @async_time_diff
    async def upload(self):
        with concurrent.futures.ThreadPoolExecutor() as pool:
            resource_url = await asyncio.wait_for(
                asyncio.get_running_loop().run_in_executor(pool, self._upload_real),
                timeout=self.UPLOAD_TIMEOUT,
            )

        await self._juggler_client(description=resource_url, service="sandbox_upload")
