#!/usr/bin/env python2

# This script downloads the latest version of Compass and installs it on
# non-windows platforms.

import os
import os.path as path
import shutil
import subprocess
import sys
import tempfile
import platform


def get_pkg_format():
    """Determine the package manager for this Linux distro."""
    with open(os.devnull, 'w') as fnull:
        try:
            subprocess.call(['apt-get', '--help'], stdout=fnull, stderr=fnull)
            return 'apt'
        except:
            pass

        try:
            subprocess.call(['dnf', '--help'], stdout=fnull, stderr=fnull)
            return 'dnf'
        except:
            pass

        try:
            subprocess.call(['yum', '--help'], stdout=fnull, stderr=fnull)
            return 'yum'
        except:
            pass

    return ''


def download_progress(count, block_size, total_size):
    """Log the download progress."""
    percent = int(count * block_size * 100 / total_size)
    sys.stdout.write("\rDownloading Compass... %d%%" % percent)
    sys.stdout.flush()


def download_pkg(link, pkg_format=''):
    """Download the package from link, logging progress. Returns the filename."""
    suf = ''
    if pkg_format == 'apt':
        suf = '.deb'
    elif pkg_format == 'yum' or pkg_format == 'dnf':
        suf = '.rpm'

    (_handle, filename) = tempfile.mkstemp(suffix=suf)
    try:
        subprocess.check_call(['curl', '--fail', '-L', '-o', filename, link],
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    except subprocess.CalledProcessError as error:
        print 'Unable to download MongoDB Compass, please check your internet' \
            ' connection. If the issue persists go to' \
            ' https://www.mongodb.com/download-center?jmp=hero#compass' \
            ' to download the compass installer for your platform.'

    try:
        out = subprocess.check_output(['file', filename]).decode('utf-8')
    except subprocess.CalledProcessError as error:
        print 'Got an unexpected error checking file type %s' % error
        sys.exit(1)

    if 'ASCII Text' in out:
        print 'Unknown package format downloaded. Appears there was an HTTP error.'
        sys.exit(1)

    return filename


def install_mac(dmg):
    """Use CLI tools to mount the dmg and extract all .apps into Applications."""
    tmp = tempfile.mkdtemp()
    with open(os.devnull, 'w') as fnull:
        try:
            subprocess.check_call(
                ['hdiutil', 'attach', '-nobrowse', '-noautoopen', '-mountpoint', tmp, dmg],
                stdout=fnull, stderr=fnull)
        except subprocess.CalledProcessError as error:
            print 'Problem running hdiutil: %s' % error

        try:
            apps = [f for f in os.listdir(tmp) if f.endswith('.app')]
            for app in apps:
                if path.isdir('/Applications/' + app):
                    print 'Old version found removing...'
                    shutil.rmtree('/Applications/' + app)
                print 'Copying %s to /Applications' % app
                shutil.copytree(path.join(tmp, app), '/Applications/' + app)
        # We don't really care about what errors come up here. Just log the failure
        # and use the finally to make sure we always unmount the dmg.
        except IOError:
            print 'Unknown error copying MongoDB Compass to /Applications/'
        finally:
            subprocess.check_call(['hdiutil', 'detach', tmp], stdout=fnull, stderr=fnull)

        if path.isdir('/Applications/MongoDB Compass.app'):
            subprocess.check_call(['open', '/Applications/MongoDB Compass.app'])
            return
        if path.isdir('/Applications/MongoDB Compass Community.app'):
            subprocess.check_call(['open', '/Applications/MongoDB Compass Community.app'])
            return


def install_linux(pkg_format, pkg_file):
    """Use the package manager indicated by pkg_format to install pkg_file."""
    if pkg_format == 'yum':
        install = ['yum', 'localinstall', '--assumeyes', pkg_file]
    elif pkg_format == 'apt':
        # dpkg returns an error code when it fails to install dependencies
        # so just run it and let apt-get tell us if something went wrong
        subprocess.call(['dpkg', '--install', pkg_file])
        install = ['apt-get', 'install', '-f', '--yes']
    elif pkg_format == 'dnf':
        install = ['dnf', 'install', '--assumeyes', pkg_file]
    else:
        print 'No available installation methods.'
        sys.exit(1)

    subprocess.check_call(install)


def is_supported_distro():
    distro_name, version_number, extra = platform.linux_distribution()

    if (distro_name == 'Ubuntu' and float(version_number) >= 14.04):
        return True

    if (('Red Hat Enterprise Linux' in distro_name or 'CentOS' in distro_name)
            and (float(version_number) >= 7.0)):
        return True

    return False


def is_supported_mac_version():
    v, _, _ = platform.mac_ver()
    # Get Mac OSX Major.Minor verson as a float
    ver_float = float('.'.join(v.split('.')[:2]))
    return ver_float >= 10.10


def download_and_install_compass():
    """Download and install compass for this platform."""
    os_type = sys.platform
    pkg_format = get_pkg_format()

    # Sometimes sys.platform gives us 'linux2' and we only want 'linux'
    if os_type.startswith('linux'):
        os_type = 'linux'
        if pkg_format == 'apt':
            os_type += '_deb'
        elif pkg_format == 'yum' or pkg_format == 'dnf':
            os_type += '_rpm'
    elif os_type == 'darwin':
        os_type = 'osx'

    if os_type.startswith('linux') and os.getuid() != 0:
        print 'You must run this script as root.'
        sys.exit(1)

    if os_type.startswith('linux') and not is_supported_distro():
        print 'You are using an unsupported Linux distribution.\n' \
            'Please visit: https://compass.mongodb.com/community-supported-platforms' \
            ' to view available community supported packages.'
        sys.exit(1)

    if os_type == 'osx' and not is_supported_mac_version():
        print 'You are using an unsupported Mac OSX version. Please upgrade\n' \
            'to at least version 10.10 (Yosemite) to install Compass.'
        sys.exit(1)

    if platform.machine() != 'x86_64':
        print 'Sorry, MongoDB Compass is only supported on 64 bit platforms.' \
            ' If you believe you\'re seeing this message in error please open a' \
            ' ticket on the SERVER project at https://jira.mongodb.org/'

    link = 'https://compass.mongodb.com/api/v2/download/latest/compass/stable/' + os_type
    print 'Downloading the package...'
    try:
        pkg = download_pkg(link, pkg_format=pkg_format)
    # This should not execute we are catching errors in all of these functions
    # but these are a catch all so we don't just dump Python stack traces to
    # the user in the case we have a new failure case we didn't think about.
    except Exception:
        print 'Unkown error downloading compass. Please open a ticket on the' \
            ' SERVER project at https://jira.mongodb.org/'

    print 'Installing the package...'
    try:
        if os_type == 'osx':
            install_mac(pkg)
        elif os_type.startswith('linux'):
            install_linux(pkg_format, pkg)
        else:
            print 'Unrecognized os_type: %s' % os_type
    except Exception:
        print 'Unkown error downloading compass. Please open a ticket on the' \
            ' SERVER project at https://jira.mongodb.org/'

    print 'Cleaning up...'
    os.remove(pkg)
    print 'Done!'


if __name__ == '__main__':
    download_and_install_compass()
