#!/usr/bin/python3
import sys

updates_repo = "QubesOS/updates-status"

q_template = """query {{
    resource(url:"{repo}") {{
        ... on Repository {{
            issues({pagination} states:OPEN) {{
                edges {{
                    node {{
                        body
                        number
                        createdAt
                        title
                        id
                        labels(first:100) {{
                            nodes {{
                                name
                            }}
                        }}
                        reactions(first:100) {{
                            nodes {{
                                content
                            }}
                        }}
                    }}
                    cursor
                }}
                pageInfo {{
                    hasNextPage
                }}
            }}
        }}
    }}
}}"""

comment_mutation = """mutation {{
    addComment(input:{{subjectId:"{subjectid!s}",body:"{body!s}"}}) {{
        subject
    }}
}}"""

import requests
import json
import re
import argparse
import subprocess
import datetime

parser = argparse.ArgumentParser("updates-upload-repo")

parser.add_argument("--verbose", action="store_true")
parser.add_argument(
    "--days",
    action="store",
    type=int,
    default=5,
    help="ensure package at least this time in testing (default: %(default)d)",
)
parser.add_argument("release_name")
parser.add_argument(
    "repo_name",
    help="target repo name, like 'current' or 'templates-itl'; list will include components applicable to the repo",
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--all", action="store_true", help="upload all matching components")
group.add_argument(
    "--list", action="store_true", help="only list components, do not upload"
)
group.add_argument("--components", nargs="*", default=[])
group.add_argument(
    "--dists",
    nargs="*",
    default=[],
    help="upload those dists only, applicable for uploading templates",
)


def graphql(query):
    r = requests.post("https://api.github.com/graphql", json={"query": query}, auth=())
    if not r.ok:
        raise Exception("API call failed: %s" % r.text)
    return r.text


def sign_command(cmd):
    p = subprocess.Popen(
        ["qubes-gpg-client", "--clearsign", "-u", "commands"],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
    )
    (out, _) = p.communicate(cmd.encode())
    return out.decode()


def comment_issue(issue_id, issue_no, body, repo=updates_repo):
    r = requests.post(
        "https://api.github.com/repos/{}/issues/{}/comments".format(repo, issue_no),
        json={"body": body},
        auth=(),
    )
    if not r.ok:
        raise Exception("API call failed: %d %s" % (r.status_code, r.text))


def close_issue(issue_id, issue_no, repo=updates_repo):
    # Github API v4 lack closeIssue mutation
    # https://platform.github.community/t/add-closeissue-mutation/3250/3

    r = requests.patch(
        "https://api.github.com/repos/{}/issues/{}".format(repo, issue_no),
        json={"state": "closed"},
        auth=(),
    )
    if not r.ok:
        raise Exception("API call failed: %d %s" % (r.status_code, r.text))


def main():
    args = parser.parse_args()

    q = q_template.format(repo=updates_repo, pagination="first:100")

    data = graphql(q)
    data = json.loads(data)
    issues_and_commands = [
        e["node"] for e in data["data"]["resource"]["issues"]["edges"]
    ]
    while data["data"]["resource"]["issues"]["pageInfo"]["hasNextPage"]:
        cursor = data["data"]["resource"]["issues"]["edges"][-1]["cursor"]
        q = q_template.format(
            repo=updates_repo, pagination='first:100 after:"{}"'.format(cursor)
        )
        data = graphql(q)
        data = json.loads(data)
        issues_and_commands.extend(
            e["node"] for e in data["data"]["resource"]["issues"]["edges"]
        )

    for issue in issues_and_commands:
        issue_no = issue["number"]
        labels = [l["name"] for l in issue["labels"]["nodes"]]
        reactions = [r["content"] for r in issue["reactions"]["nodes"]]
        match = re.search(
            r"`Upload [a-z0-9. -]* {}(?: vm-[a-z0-9+-]*)? repo`".format(args.repo_name),
            issue["body"],
        )
        if not match:
            if args.verbose:
                print(
                    "Warning: issue {} has no cmd for {}".format(
                        issue_no, args.repo_name
                    )
                )
            continue
        cmd = match.group(0).strip("`")
        _, component, commit_id, release_name, repo_name, repo = cmd.split(" ", 5)
        dist = "all"
        if repo.startswith("vm-"):
            dist = repo.split(" ")[0]
        if release_name != args.release_name:
            continue
        days = (
            datetime.datetime.now()
            - datetime.datetime.strptime(issue["createdAt"], "%Y-%m-%dT%H:%M:%SZ")
        ).days
        if args.list:
            extra_info = ""
            thumb_up = len(list(r for r in reactions if r == "THUMBS_UP"))
            thumb_down = len(list(r for r in reactions if r == "THUMBS_DOWN"))
            if thumb_up:
                extra_info += "\033[32m {}\U0001F44D \033[m".format(thumb_up)
            if thumb_down:
                extra_info += "\033[31m {}\U0001F44E \033[m".format(thumb_down)
            if "buggy" in labels:
                extra_info += "\033[31m buggy\033[m"
            if days >= args.days:
                color = "\033[32m"
            else:
                color = "\033[31m"
            print(
                "issue no: {} {}{} ({} days ago)\033[m{} cmd: {}".format(
                    issue_no, color, issue["title"], days, extra_info, cmd
                )
            )
        else:
            if (
                args.all
                or component in args.components
                or (component == "linux-template-builder" and dist in args.dists)
            ):
                if args.dists and component != "linux-template-builder":
                    print(
                        "ERROR: selective dists upload for non-templates not supported by this script"
                    )
                    sys.exit(1)
                if "buggy" in labels:
                    print("WARNING: skipping buggy {} for {}".format(component, dist))
                    continue
                if days < args.days and args.repo_name in (
                    "current",
                    "templates-itl",
                    "templates-community",
                ):
                    print(
                        "WARNING: {} for {} not long enough in testing".format(
                            component, dist
                        )
                    )
                    continue
                print(cmd)
                signed_command = sign_command(cmd)
                comment_issue(issue["id"], issue["number"], signed_command)
                # if args.repo_name == 'current':
                #    close_issue(issue['id'], issue['number'])


if __name__ == "__main__":
    main()
