#!/usr/bin/env python3

import contextlib
import subprocess


def exec_cmd(cmd, input=None):
    p = subprocess.Popen(cmd,
                         stdout=subprocess.PIPE,
                         stdin=(subprocess.PIPE if input else None))
    stdout, _ = p.communicate(input and input.encode())

    return str(stdout, 'utf-8')


@contextlib.contextmanager
def disable_show_signature():
    local_flag = exec_cmd(
        ["git", "config", "--local", "--get", "log.showSignature"]).strip()
    exec_cmd(["git", "config", "--local", "log.showSignature", "false"])
    yield
    if local_flag:
        exec_cmd(["git", "config", "--local", "log.showSignature", local_flag])
    else:
        exec_cmd(["git", "config", "--local", "--unset", "log.showSignature"])


def get_diff(tag):
    return [
        l for l in exec_cmd([
            "git", "log", "--pretty=%h %s", "--no-merges", "{}..HEAD".format(
                tag)
        ]).splitlines() if not l.startswith("gpg")
    ]


def get_last_tag():
    return max(
        list(map(int, tag.split('.')))
        for tag in exec_cmd(["git", "tag"]).splitlines())


def inc_tag(tag):
    tag = tag[:]
    tag[-1] += 1
    return tag


def str_tag(tag):
    return '.'.join(map(str, tag))


def get_info(version):
    last_tag = get_last_tag()
    new_tag = version or str_tag(inc_tag(last_tag))

    subject = "Version {}".format(new_tag)
    changes = '\n'.join('* {}'.format(line)
                        for line in get_diff(str_tag(last_tag)))

    annotation = """{subject}
{sep}

{changes}
""".format(subject=subject, sep='-' * len(subject), changes=changes)
    return new_tag, annotation


def push():
    exec_cmd(["git", "push", "origin"])
    exec_cmd(["git", "push", "origin", "--tags"])


def show(tag):
    return exec_cmd(["git", "show", tag])


def create_tag(tag, annotation, sign):
    cmd = ["git", "tag"]
    if sign:
        cmd.append("--sign")
    exec_cmd(cmd + ["-F-", tag], input=annotation)


def delete_tag(tag):
    exec_cmd(["git", "tag", "-d", tag])


def sdist(tag):
    create_tag(tag, 'sdist', False)
    exec_cmd(["python", "setup.py", "sdist"])
    delete_tag(tag)


def bump(tag, annotation, sign):
    sdist(tag)
    exec_cmd(["git", "add", "AUTHORS", "ChangeLog"])
    cmd = ["git", "commit"]
    if sign:
        cmd.append("--sign")
    exec_cmd(cmd + ["-m", "Bump version %s" % tag])
    sdist(tag)
    exec_cmd(["git", "add", "ChangeLog"])
    exec_cmd(cmd + ["--amend", "--no-edit"])
    create_tag(tag, annotation, sign)


def main(args):
    with disable_show_signature():
        tag, annotation = get_info(args.version)
    if args.dry_run:
        print(annotation)
    else:
        with disable_show_signature():
            bump(tag, annotation, args.sign)
        print(show(tag))

        a = input('Push new tag? [y/N]: ')
        if a.lower() in ('y', 'yes'):
            push()


if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('version',
                        nargs='?',
                        help="specify version, otherwise the current version "
                        "will be incremented by 1")
    parser.add_argument('-s',
                        '--sign',
                        action='store_true',
                        help="sign a tag and commits")
    parser.add_argument('-r',
                        '--dry-run',
                        action='store_true',
                        help="print tag's number and annotation")

    args = parser.parse_args()

    main(args)
