from fabric.api import task, runs_once, env, serial, puts, settings
from fabric.utils import fastprint
from fabric.colors import blue, red, white

from output import notify
from packages import PackageDescriptor
from output import unsquelched
from hosts import exemplar
from ssh_tunnel import setup_tunnel
from packages import PackageInfo


@task
@runs_once
def from_exemplar(**tags):
    """
    Cache the set of packages installed on one host from the specified tags.

    """
    host_string = setup_tunnel([exemplar(**tags)])[0]
    with settings(host_string=host_string):
        installed_packages()


@task
@runs_once
def limit_prefix(*prefix_list):
    """
    Limits cached packages to those that
    match one or more prefix strings
    """
    env.package_descriptors = filter(
        lambda pkg: any(pkg.name.startswith(prefix)
                        for prefix in prefix_list), env.package_descriptors)


@task(default=True)
@runs_once
def installed_packages(prefix=None):
    """
    Cache the set of packages installed on the selected host.
    """
    pkg_info = PackageInfo()
    env.package_descriptors = [
        package for package in pkg_info.installed_packages()
        if prefix is None or package.name.startswith(prefix)
    ]


@task
@runs_once
def from_strings(**pkg_revs):
    """
    Cache packages based on strings, that can be either checked with confirm
    or deployed with deploy.

    Each named argument specifies a package by name, and the revision of
    the package to deploy
    """
    packages = []
    for pkg_name, pkg_rev in pkg_revs.items():
        packages.append(PackageDescriptor(pkg_name, pkg_rev))

    env.package_descriptors = packages

    notify(env.package_descriptors)


@task
@runs_once
def from_stdin(prefix=None):
    """
    Cache a list of packages from stdin.
    Package names must start with prefix, if specified (any that don't
    will be skipped). Package names and revisions should be separated
    by = signs, and should be one per line.
    """

    if prefix:
        prefix_msg = white('pkg_name', bold=True) + white(
            ' must start with ') + blue(prefix)
    else:
        prefix_msg = ''
    fastprint('\n')
    fastprint('\n'.join([
        white('Please enter pkg_name=pkg_rev, one per line\n', bold=True),
        white('pkg_rev', bold=True) + white(' is a git revision hash'),
        prefix_msg,
        white('Complete your selections by entering a blank line.'),
    ]))
    fastprint('\n\n')

    packages = {}
    while True:
        line = raw_input("> ")
        if not line:
            break

        if '=' not in line:
            fastprint(red("Expected = in '{line}'. Skipping...".format(
                          line=line)) + white('\n'))
            continue

        pkg_name, _, pkg_rev = line.partition('=')
        pkg_name = pkg_name.strip()
        pkg_rev = pkg_rev.strip()

        if prefix and not pkg_name.startswith(prefix):
            fastprint(red("'{0}' does not start with '{1}'".format(
                          pkg_name, prefix)) + white('\n'))
            continue

        packages[pkg_name] = pkg_rev

    from_strings(**packages)


@task
@serial
@runs_once
def prompt(*pkg_names):
    packages = {}
    with unsquelched():
        puts("Please supply git revisions to "
             "deploy for the following packages:")
        for pkg in pkg_names:
            packages[pkg] = raw_input("{pkg} = ".format(pkg=pkg)).strip()

        from_strings(**packages)