#!/usr/bin/env python

import argparse
import sys

sys.dont_write_bytecode = True

from common import testinfra

def trigger_image(github, opts):
    title = testinfra.ISSUE_TITLE_IMAGE_REFRESH.format(opts.image)
    if opts.pull:
        # Add a comment to the pull request with the right text and set the right label
        issue = github.get("issues/" + opts.pull)
        github.post("issues/" + opts.pull + "/comments", { "body": "bot: " + title })
        github.patch("issues/" + opts.pull, { "labels": issue['labels'] + [ "bot" ] })
    else:
        # Make a new issue with the right content.
        github.post("issues", { "title": title,
                                "labels": [ "bot" ],
                                "body": "bot: " + title })
    return 0

def trigger_pull(github, opts):
    pull = github.get("pulls/" + opts.pull)

    if not pull:
        sys.stderr.write("{0} is not a pull request.\n".format(opts.pull))
        return 1

    # triggering is manual, so don't prevent triggering a user that isn't on the whitelist
    # but issue a warning in case of an oversight
    login = pull["head"]["user"]["login"]
    if not opts.allow and not login in github.whitelist:
        sys.stderr.write("Pull request author '{0}' isn't whitelisted. Override with --allow.\n".format(login))
        return 1

    revision = pull['head']['sha']
    statuses = github.statuses(revision)
    if opts.context:
        contexts = [ opts.context ]
        all = False
    else:
        contexts = list(set(statuses.keys()) & set(testinfra.DEFAULT_VERIFY.keys()))
        all = True

    ret = 0
    for context in contexts:
        status = statuses.get(context, { })
        current_status = status.get("state", all and "unknown" or "empty")

        if current_status not in ["empty", "error", "failure"]:
            # allow changing if manual testing required, otherwise "pending" state indicates that testing is in progress
            manual_testing = current_status == "pending" and status.get("description", None) == testinfra.NO_TESTING
            # also allow override with --force
            if not (manual_testing or opts.force):
                if not all:
                    sys.stderr.write("{0}: isn't in triggerable state (is: {1})\n".format(context, status["state"]))
                    ret = 1
                continue
        sys.stderr.write("{0}: triggering on pull request {1}\n".format(context, opts.pull))
        changes = { "state": "pending", "description": testinfra.NOT_TESTED, "context": context }

        # Keep the old link for reference, until testing starts again
        link = status.get("target_url", None)
        if link:
            changes["target_url"] = link

        github.post("statuses/" + revision, changes)

    return ret

def main():
    parser = argparse.ArgumentParser(description='Manually trigger CI Robots')
    parser.add_argument('-f', '--force', action="store_true",
                        help='Force setting the status even if the program logic thinks it shouldn''t be done')
    parser.add_argument('-a', '--allow', action='store_true', dest='allow',
                        help="Allow triggering for users that aren't whitelisted")
    parser.add_argument('--image', help='Trigger a image refresh instead of a testsuite run')
    parser.add_argument('pull', nargs='?', help='The pull request to trigger')
    parser.add_argument('context', nargs='?', help='The github task context to trigger')
    opts = parser.parse_args()

    github = testinfra.GitHub()

    if opts.image:
        return trigger_image(github, opts)
    elif opts.pull and not opts.image:
        return trigger_pull(github, opts)
    else:
        sys.stderr.write("Please specify either --image or a pull request.\n")
        return 1

if __name__ == '__main__':
    sys.exit(main())
