import os
import urllib
import zipfile

from parallels.core.logging import get_logger
from parallels.core import messages, MigrationError

logger = get_logger(__name__)


def download(url, path):
    """Download file from specified URL to specified filename

    :type url: str | unicode
    :type path: str | unicode
    :rtype: None
    :raises: parallels.core.MigrationError
    """
    logger.debug(messages.DEBUG_DOWNLOAD_FILE_TO.format(url=url, filename=path))
    _download_file(url, path)


def download_zip(url, path):
    """Download ZIP archive file from specified URL, and unpack it to specified path

    :type url: str | unicode
    :type path: str | unicode
    :rtype: None
    :raises: parallels.core.MigrationError
    """
    zip_path = '%s.zip' % path
    logger.debug(messages.DEBUG_DOWNLOAD_FILE_TO.format(url=url, filename=zip_path))
    _download_file(url, zip_path)
    logger.debug(messages.DEBUG_UNZIP_FILE_TO.format(archive=zip_path, path=path))
    with zipfile.ZipFile(zip_path) as zfp:
        zfp.extractall(path)
    os.remove(zip_path)


def _download_file(source_url, target_filename):
    """Download file from specified URL to specified filename

    Comparing to "urllib.urlretrieve" function, this function handles errors
    from the HTTP server like 404 (Not Found), and raises an exception on such errors.

    :type source_url: str | unicode
    :type target_filename: str | unicode
    :rtype: None
    :raises: parallels.core.MigrationError
    """
    class FancyURLopenerWithErrors(urllib.FancyURLopener):
        def http_error_default(self, url, fp, errcode, errmsg, headers):
            raise MigrationError(
                messages.FAILED_TO_DOWNLOAD_FILE.format(
                    url=source_url, file=os.path.abspath(target_filename), code=errcode, message=errmsg
                ))

    opener = FancyURLopenerWithErrors()
    opener.retrieve(source_url, target_filename)
