"""
These system tests are testing the release of groovy+ catkin projects.
"""

from __future__ import print_function

import os
import sys

try:
    from vcstools.vcs_abstraction import get_vcs_client
except ImportError:
    print("vcstools was not detected, please install it.", file=sys.stderr)
    sys.exit(1)

from .common import create_release_repo

from ..utils.common import bloom_answer
from ..utils.common import change_directory
from ..utils.common import in_temporary_directory
from ..utils.common import user
from ..utils.package_version import change_upstream_version

from bloom.git import branch_exists
from bloom.git import inbranch

from bloom.util import code

from bloom.commands.git.patch import export_cmd
from bloom.commands.git.patch import import_cmd
from bloom.commands.git.patch import remove_cmd

from bloom.generators.debian.generator import sanitize_package_name


def create_upstream_repository(packages, directory=None):
    upstream_dir = 'upstream_repo_groovy'
    user('mkdir ' + upstream_dir)
    with change_directory(upstream_dir):
        user('git init .')
        user('echo "readme stuff" >> README.md')
        user('git add README.md')
        user('git commit -m "Initial commit" --allow-empty')
        user('git checkout -b groovy_devel')
        for package in packages:
            user('mkdir ' + package)
            with change_directory(package if len(packages) != 1 else '.'):
                package_xml = """\
<?xml version="1.0"?>
<package>
  <name>{0}</name>
  <version>0.1.0</version>
  <description>A catkin (groovy) ROS package called '{0}'</description>
  <maintainer email="bar@baz.com">Bar</maintainer>
  <license>BSD</license>

  <url type="bugtracker">https://github.com/ros/this/issues</url>
  <url type="repository">https://github.com/ros/this</url>

  <build_depend>catkin</build_depend>

  <run_depend>catkin</run_depend>
  <!-- required for messages generated by gencpp -->
  <run_depend>roscpp_core</run_depend>
</package>
""".format(package)
                with open('package.xml', 'w+') as f:
                    f.write(package_xml)
                user('touch .cproject')
                user('touch .project')
                user('touch white space.txt~')
                user('mkdir -p include/sym')
                user('touch include/{0}.h'.format(package))
                os.symlink('../{0}.h'.format(package), 'include/sym/{0}.h'.format(package))
                user('mkdir debian')
                user('touch debian/something.udev')
                user('git add package.xml .cproject .project include debian "white space.txt~"')
        user('git commit -m "Releasing version 0.1.0" --allow-empty')
        user('git tag 0.1.0 -m "Releasing version 0.1.0"')
        return os.getcwd()


def _test_unary_package_repository(release_dir, version, directory=None):
    print("Testing in {0} at version {1}".format(release_dir, version))
    with change_directory(release_dir):
        # First run everything
        with bloom_answer(bloom_answer.ASSERT_NO_QUESTION):
            cmd = 'git-bloom-release{0} groovy'
            if 'BLOOM_VERBOSE' not in os.environ:
                cmd = cmd.format(' --quiet')
            else:
                cmd = cmd.format('')
            user(cmd, silent=False)
        ###
        ### Import upstream
        ###
        # does the upstream branch exist?
        assert branch_exists('upstream', local_only=True), "no upstream branch"
        # does the upstrea/<version> tag exist?
        ret, out, err = user('git tag', return_io=True)
        assert out.count('upstream/' + version) == 1, "no upstream tag created"
        # Is the package.xml from upstream in the upstream branch now?
        with inbranch('upstream'):
            assert os.path.exists('package.xml'), \
                "upstream did not import: '" + os.getcwd() + "': " + \
                str(os.listdir(os.getcwd()))
            assert os.path.exists(os.path.join('debian', 'something.udev')), \
                "Lost the debian overlaid files in upstream branch"
            assert os.path.exists('white space.txt~'), \
                "Lost file with whitespace in name in upstream branch"
            with open('package.xml') as f:
                package_xml = f.read()
                assert package_xml.count(version), "not right file"

        ###
        ### Release generator
        ###
        # patch import should have reported OK
        assert ret == code.OK, "actually returned ({0})".format(ret)
        # do the proper branches exist?
        assert branch_exists('release/groovy/foo'), \
            "no release/groovy/foo branch"
        assert branch_exists('patches/release/groovy/foo'), \
            "no patches/release/groovy/foo branch"
        # was the release tag created?
        ret, out, err = user('git tag', return_io=True)
        expected = 'release/groovy/foo/' + version + '-0'
        assert out.count(expected) == 1, \
            "no release tag created, expected: '{0}'".format(expected)

        ###
        ### Make patch
        ###
        with inbranch('release/groovy/foo'):
            assert os.path.exists(os.path.join('debian', 'something.udev')), \
                "Lost the debian overlaid files in release branch"
            assert os.path.exists('white space.txt~'), \
                "Lost file with whitespace in name in release branch"
            assert os.path.islink('include/sym/foo.h'), "Symbolic link lost during pipeline"
            if os.path.exists('include/foo.h'):
                user('git rm include/foo.h')
            else:
                if not os.path.exists('include'):
                    os.makedirs('include')
                user('touch include/foo.h')
                user('git add include/foo.h')
            user('git commit -m "A release patch" --allow-empty')

        ###
        ### Test import and export
        ###
        with inbranch('release/groovy/foo'):
            export_cmd.export_patches()
            remove_cmd.remove_patches()
            import_cmd.import_patches()

        ###
        ### Release generator, again
        ###
        # patch import should have reported OK
        assert ret == code.OK, "actually returned ({0})".format(ret)
        # do the proper branches exist?
        assert branch_exists('release/groovy/foo'), \
            "no release/groovy/foo branch"
        assert branch_exists('patches/release/groovy/foo'), \
            "no patches/release/groovy/foo branch"
        # was the release tag created?
        ret, out, err = user('git tag', return_io=True)
        assert out.count('release/groovy/foo/' + version) == 1, \
            "no release tag created"


@in_temporary_directory
def test_unary_package_repository(directory=None):
    """
    Release a single package catkin (groovy) repository.
    """
    directory = directory if directory is not None else os.getcwd()
    # Setup
    upstream_dir = create_upstream_repository(['foo'], directory)
    upstream_url = 'file://' + upstream_dir
    release_url = create_release_repo(
        upstream_url,
        'git',
        'groovy_devel',
        'groovy')
    release_dir = os.path.join(directory, 'foo_release_clone')
    release_client = get_vcs_client('git', release_dir)
    assert release_client.checkout(release_url)
    versions = ['0.1.0', '0.1.1', '0.2.0']
    import bloom.commands.git.release
    for index in range(len(versions)):
        _test_unary_package_repository(release_dir, versions[index], directory)
        bloom.commands.git.release.upstream_repos = {}
        if index != len(versions) - 1:
            change_upstream_version(upstream_dir, versions[index + 1])


@in_temporary_directory
def test_multi_package_repository(directory=None):
    """
    Release a multi package catkin (groovy) repository.
    """
    directory = directory if directory is not None else os.getcwd()
    # Setup
    pkgs = ['foo', 'bar_ros', 'baz']
    upstream_dir = create_upstream_repository(pkgs, directory)
    upstream_url = 'file://' + upstream_dir
    release_url = create_release_repo(
        upstream_url,
        'git',
        'groovy_devel',
        'groovy')
    release_dir = os.path.join(directory, 'foo_release_clone')
    release_client = get_vcs_client('git', release_dir)
    assert release_client.checkout(release_url)
    with change_directory(release_dir):
        # First run everything
        with bloom_answer(bloom_answer.ASSERT_NO_QUESTION):
            cmd = 'git-bloom-release{0} groovy'
            if 'BLOOM_VERBOSE' not in os.environ:
                cmd = cmd.format(' --quiet')
            else:
                cmd = cmd.format('')
            user(cmd, silent=False)
        ###
        ### Import upstream
        ###
        # does the upstream branch exist?
        assert branch_exists('upstream', local_only=True), "no upstream branch"
        # does the upstrea/0.1.0 tag exist?
        ret, out, err = user('git tag', return_io=True)
        assert out.count('upstream/0.1.0') == 1, "no upstream tag created"
        # Is the package.xml from upstream in the upstream branch now?
        with inbranch('upstream'):
            for pkg in pkgs:
                with change_directory(pkg):
                    assert os.path.exists(
                        os.path.join('debian', 'something.udev')), \
                        "Lost the debian overlaid files in upstream branch"
                    assert os.path.exists('white space.txt~'), \
                        "Lost file with whitespace in name in upstream branch"
                    assert os.path.exists('package.xml'), \
                        "upstream did not import: " + os.listdir()
                    with open('package.xml') as f:
                        assert f.read().count('0.1.0'), "not right file"

        ###
        ### Release generator
        ###
        # Check the environment after the release generator
        ret, out, err = user('git tag', return_io=True)
        for pkg in pkgs:
            # Does the release/pkg branch exist?
            assert branch_exists('release/groovy/' + pkg), \
                "no release/groovy/" + pkg + " branch"
            # Does the patches/release/pkg branch exist?
            assert branch_exists('patches/release/groovy/' + pkg), \
                "no patches/release/groovy/" + pkg + " branch"
            # Did the release tag get created?
            assert out.count('release/groovy/' + pkg + '/0.1.0-0') == 1, \
                "no release tag created for " + pkg
            # Is there a package.xml in the top level?
            with inbranch('release/groovy/' + pkg):
                assert os.path.exists('package.xml'), "release branch invalid"
                # Is it the correct package.xml for this pkg?
                package_xml = open('package.xml', 'r').read()
                assert package_xml.count('<name>' + pkg + '</name>'), \
                    "incorrect package.xml for " + str(pkg)

        # Make a patch
        with inbranch('release/groovy/' + pkgs[0]):
            user('echo "This is a change" >> README.md')
            user('git add README.md')
            user('git commit -m "added a readme" --allow-empty')

        ###
        ### Release generator, again
        ###
        with bloom_answer(bloom_answer.ASSERT_NO_QUESTION):
            ret = user('git-bloom-generate -y rosrelease groovy -s upstream')
        # patch import should have reported OK
        assert ret == code.OK, "actually returned ({0})".format(ret)
        # Check the environment after the release generator
        ret, out, err = user('git tag', return_io=True)
        for pkg in pkgs:
            # Does the release/pkg branch exist?
            assert branch_exists('release/groovy/' + pkg), \
                "no release/groovy/" + pkg + " branch"
            # Does the patches/release/pkg branch exist?
            assert branch_exists('patches/release/groovy/' + pkg), \
                "no patches/release/groovy/" + pkg + " branch"
            # Did the release tag get created?
            assert out.count('release/groovy/' + pkg + '/0.1.0-0') == 1, \
                "no release tag created for " + pkg
            # Is there a package.xml in the top level?
            with inbranch('release/groovy/' + pkg):
                assert os.path.exists(os.path.join('debian', 'something.udev')), \
                    "Lost the debian overlaid files in release branch"
                assert os.path.exists('white space.txt~'), \
                    "Lost file with whitespace in name in release branch"
                assert os.path.exists('package.xml'), "release branch invalid"
                # Is it the correct package.xml for this pkg?
                with open('package.xml', 'r') as f:
                    assert f.read().count('<name>' + pkg + '</name>'), \
                        "incorrect package.xml for " + str(pkg)

        ###
        ### ROSDebian Generator
        ###
        # Check the environment after the release generator
        ret, out, err = user('git tag', return_io=True)
        for pkg in pkgs:
            for distro in ['oneiric', 'precise', 'quantal']:
                pkg_san = sanitize_package_name(pkg)
                # Does the debian/distro/pkg branch exist?
                assert branch_exists('debian/groovy/' + distro + '/' + pkg), \
                    "no debian/groovy/" + pkg + " branch"
                # Does the patches/debian/distro/pkg branch exist?
                patches_branch = 'patches/debian/groovy/' + distro + '/' + pkg
                assert branch_exists(patches_branch), \
                    "no " + patches_branch + " branch"
                # Did the debian tag get created?
                tag = 'debian/ros-groovy-' + pkg_san + '_0.1.0-0_' + distro
                assert out.count(tag) == 1, \
                    "no '" + tag + "'' tag created for '" + pkg + "': `\n" + \
                    out + "\n`"
            # Is there a package.xml in the top level?
            with inbranch('debian/groovy/' + distro + '/' + pkg):
                assert os.path.exists(
                    os.path.join('debian', 'something.udev')), \
                    "Lost the debian overlaid files in debian branch"
                assert os.path.exists('white space.txt~'), \
                    "Lost file with whitespace in name in debian branch"
                assert os.path.exists('package.xml'), "debian branch invalid"
                # Is there blank lins due to no Conflicts/Replaces?
                # See: https://github.com/ros-infrastructure/bloom/pull/329
                with open(os.path.join('debian', 'control'), 'r') as f:
                    assert f.read().count('\n\nHomepage:') == 0, \
                        "Extra blank line before Homepage detected."
                # Is it the correct package.xml for this pkg?
                with open('package.xml', 'r') as f:
                    assert f.read().count('<name>' + pkg + '</name>'), \
                        "incorrect package.xml for " + str(pkg)
