#!/usr/bin/env python3
# pylint: disable=invalid-name,missing-docstring

import argparse
import getpass
import subprocess
import sys
from time import sleep

import jenkins


def parse_pass_lines(lines):
    for line in lines:
        if not line.startswith('#') and ': ' in line:
            yield line.split(': ', 1)


def get_pass(name):
    try:
        output = subprocess.check_output(['gopass', 'show', name], universal_newlines=True)
    except subprocess.CalledProcessError:
        return None, {}

    lines = output.splitlines()
    if not lines:
        return None, {}

    password = lines[0]
    extras = {key: value for key, value in parse_pass_lines(lines[1:])}
    return password, extras


def get_connection(url='https://ci.theforeman.org', username=None,
                   password=None, pass_name='theforeman/jenkins-token'):
    if not password or not username:
        pass_password, pass_extras = get_pass(pass_name)

        if not password:
            password = pass_password

        if not username:
            try:
                username = pass_extras['Username']
            except KeyError:
                username = getpass.getuser()

    return jenkins.Jenkins(url, username=username, password=password)


def get_last_build_number(server, job):
    job_info = server.get_job_info(job)
    return job_info['lastBuild']['number'] if job_info['lastBuild'] else -1


def build_job(job, parameters=None, wait=True):
    server = get_connection()

    # Get the last build so we can poll later
    build_number_before = get_last_build_number(server, job)

    server.build_job(job, parameters)

    while True:
        last_build_number = get_last_build_number(server, job)
        if last_build_number > build_number_before:
            break
        sleep(1)

    if wait:
        _poll(server, job, last_build_number)
    else:
        build_info = server.get_build_info(job, last_build_number)
        print(build_info['url'])


def _poll(server, job, build_number):
    build_info = server.get_build_info(job, build_number)
    print(build_info['url'])

    while build_info['building']:
        try:
            sleep(1)
            build_info = server.get_build_info(job, build_number)
        except KeyboardInterrupt:
            raise SystemExit('Aborted')

    print(build_info['result'])
    return build_info['result']


def ping(args):  # pylint: disable=unused-argument
    server = get_connection()
    user = server.get_whoami()
    version = server.get_version()
    print(f'Hello {user["fullName"]} from Jenkins {version}')


def poll(args):
    server = get_connection()

    job = args.job

    if args.build_number:
        build_number = args.build_number
    else:
        build_number = get_last_build_number(server, job)

    _poll(server, job, build_number)


def release(args):
    try:
        major, minor, _ = args.version.split('.', 2)
    except ValueError:
        raise argparse.ArgumentTypeError('Pass in the full version like 1.24.0-RC1')

    if args.type == 'packages':
        job = 'release_packages'
    else:
        job = 'tarballs-release'

    parameters = {
        'version': args.version,
        'major_version': f'{major}.{minor}',
    }

    build_job(job, parameters, args.wait)


def pipeline(args):
    try:
        major, minor = args.version.split('.', 2)[:2]
    except ValueError:
        msg = 'Pass in the version like 2.4.0-RC1 or 2.4, depending on the pipeline'
        raise argparse.ArgumentTypeError(msg)

    parameters = None

    if args.type == 'foreman':
        job = f'{args.type}-{major}.{minor}-release-pipeline'
        parameters = {
            'expected_version': args.version,
        }
    elif args.type == 'katello':
        job = f'{args.type}-{major}.{minor}-rpm-pipeline'
    else:
        job = f'foreman-{args.type}-{major}.{minor}-rpm-pipeline'

    build_job(job, parameters, args.wait)


def green(args):
    job = args.job

    print(f'Checking job {job}')

    server = get_connection()
    last_build_number = server.get_job_info(job)['lastCompletedBuild']['number']
    result = _poll(server, job, last_build_number)
    if result != 'SUCCESS':
        sys.exit(1)


def main():
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    ping_parser = subparsers.add_parser('ping', help='Ping the Jenkins server')
    ping_parser.set_defaults(func=ping)

    poll_parser = subparsers.add_parser('poll', help='Poll a running job till completion')
    poll_parser.set_defaults(func=poll)
    poll_parser.add_argument('job', help='The job to poll')
    poll_parser.add_argument('build_number', help='The specific job number to poll', nargs='?')

    release_parser = subparsers.add_parser('release', help='Run a release job')
    release_parser.set_defaults(func=release)
    release_parser.add_argument('type', choices=('tarballs', 'packages'))
    release_parser.add_argument('version', help='The full version like 1.24.0-RC1')
    release_parser.add_argument('--no-wait', action='store_false', dest='wait',
                                help="Don't wait for the job to complete and exit immediately")

    pipeline_parser = subparsers.add_parser('pipeline', help='Run a pipeline')
    pipeline_parser.set_defaults(func=pipeline)
    pipeline_parser.add_argument('type', choices=('foreman', 'client', 'plugins', 'katello'))
    pipeline_parser.add_argument('version', help='The full version like 1.24.0-RC1')
    pipeline_parser.add_argument('--no-wait', action='store_false', dest='wait',
                                 help="Don't wait for the job to complete and exit immediately")

    job_green_parser = subparsers.add_parser('green', help='Verify a job is green')
    job_green_parser.set_defaults(func=green)
    job_green_parser.add_argument('job', help='The job to poll')

    args = parser.parse_args()

    # Python 3.7 added required=True to add_subparsers but 3.6 is still common
    if not args.func:
        parser.exit(2, parser.format_help())
    args.func(args)


if __name__ == '__main__':
    main()
