import requests

from logging import Logger
from requests import Response
from typing import Dict

from helper import retry, json_serialize

from application.model import UpdateType
from application.model.configuration import Configurations
from application.model.customexception import ErrorDownloadingLoaders
from application.util import ErrorResponseUtil, settings


class LoaderUpdaterAPIRepository(object):

    def __init__(self, logger, configs):
        # type: (Logger, Configurations) -> None

        self.logger = logger
        self.configs = configs
        self.update_type = UpdateType.loader
        self.local_configs = configs.updaters[self.update_type.name]
        self.response_util = ErrorResponseUtil()
        self.error_response = None

    def download_loaders(self, file_path, params):
        # type: (str, Dict) -> None

        url = self._get_download_url(params)
        headers = self._get_zip_headers()
        response = retry(
                logger=self.logger,
                method_name="download_loaders",
                n=3,
                sleep_seconds=5,
                trying_to_do=lambda: requests.request("GET", url=url, headers=headers, timeout=60),
                is_success=lambda resp: resp.status_code == 302,
                success=lambda resp: resp,
                failed=lambda resp: self._get_error_response(resp)
        )

        if response is None:
            raise ErrorDownloadingLoaders(self.error_response)

        package = self._download_package(response.content.strip('\"'))
        self._check_has_file(package)
        self._save_package(file_path, package)

    @staticmethod
    def _save_package(file_path, package):
        # type: (str, Response) -> None

        with open(file_path, "wb") as zip_object:
            for chunk in package.iter_content(chunk_size=128):
                zip_object.write(chunk)

    @staticmethod
    def _download_package(download_url):
        # type: (str) -> Response

        return requests.get(download_url, allow_redirects=True)

    @staticmethod
    def _get_url_params(params):
        # type: (Dict) -> str
        url = ""
        for key, value in params.items():
            if not url:
                url += "?"
            else:
                url += "&"
            url += "{}={}".format(key, value)
        return url

    def _get_download_url(self, params):
        # type: (Dict) -> str

        end_point_url = self.local_configs.endpoints.download_loaders.format(storeId=self.configs.store_id)
        params_url = self._get_url_params(params)
        return self.configs.base_url + end_point_url + params_url

    def _get_zip_headers(self):
        # type: () -> Dict[str, str]

        return {"X-Api-Key": self.configs.api_key,
                "Content-type": "application/zip"}

    def _get_error_response(self, resp):
        # type: (Response) -> None

        self.logger.error("Error downloading loaders: {}".format(json_serialize(resp)))
        self.error_response = self.response_util.get_error_response(resp, "$STORE_NOT_FOUND")

    @staticmethod
    def _check_has_file(package):
        # type: (Response) -> None

        if package is not None and package.status_code == 403:
            settings.set_update_control("loaders", "$LOADERS_NOT_FOUND")
            raise ErrorDownloadingLoaders("$LOADERS_NOT_FOUND")
