#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-or-later
# "git review download", like git review -d, but without need for ssh key. Run
# this script inside a git repository with the gerrit review ID as argument to
# fetch and checkout the patch.
import argparse
import configparser
import json
import os
import subprocess
import urllib.request
import urllib.parse


def get_topdir():
    try:
        return subprocess.run(["git", "rev-parse", "--show-toplevel"],
                              check=True, capture_output=True,
                              encoding="UTF-8").stdout.rstrip()
    except subprocess.CalledProcessError:
        print("ERROR: not running inside a git repository")
        exit(1)


def get_config_path():
    ret = f"{get_topdir()}/.gitreview"
    if not os.path.exists(ret):
        print(f"ERROR: config not found: {ret}")
        exit(1)
    return ret


def get_config():
    config_path = get_config_path()
    config = configparser.ConfigParser()
    config.read(config_path)
    return config["gerrit"]["host"], config["gerrit"]["project"]


def get_gerrit_details(host, project, patch_id, verbose):
    project_q = urllib.parse.quote(project, "")
    url = f"https://{host}/changes/{project_q}~{args.patch_id}/detail"
    print(f"Download {url}")
    with urllib.request.urlopen(url) as response:
        content = response.read().decode()
        content = "{" + content.split("{", 1)[1]
        ret = json.loads(content)
        if args.verbose:
            print(json.dumps(ret, indent=4))
    return ret


def get_highest_revision(details):
    ret = 1
    for message in details["messages"]:
        rev = message["_revision_number"]
        if rev > ret:
            ret = rev
    return ret


def git_fetch(host, project, patch_id, rev):
    last_digits = str(patch_id)[-2:]
    url = f"https://{host}/{project}"
    ref = f"refs/changes/{last_digits}/{patch_id}/{rev}"
    cmd = ["git", "fetch", url, ref]
    print(f"+ {' '.join(cmd)}")

    try:
        return subprocess.run(cmd, check=True)
    except subprocess.CalledProcessError:
        exit(1)

def git_cherry_pick_fetch_head():
    cmd = ["git", "cherry-pick", "FETCH_HEAD"];
    print(f"+ {' '.join(cmd)}")

    try:
        return subprocess.run(cmd, check=True)
    except subprocess.CalledProcessError:
        exit(1)

def git_checkout_fetch_head(patch_id, rev):
    cmd = ["git", "checkout", "-B", f"gerrit/{patch_id}_{rev}", "FETCH_HEAD"]
    print(f"+ {' '.join(cmd)}")

    try:
        return subprocess.run(cmd, check=True)
    except subprocess.CalledProcessError:
        exit(1)


desc = "git review download: fetch and checkout a patch from gerrit"
parser = argparse.ArgumentParser(description=desc)
parser.add_argument("patch_id", type=int,
                    help="gerrit review ID")
parser.add_argument("-c", "--cherry-pick", action="store_true",
                    help="cherry-pick into current branch instead of "
                         "fetching into a branch")
parser.add_argument("-r", "--revision", type=int,
                    help="patchset revision, default is latest")
parser.add_argument("-v", "--verbose", action="store_true")
args = parser.parse_args()

host, project = get_config()
rev = args.revision

if not rev:
    details = get_gerrit_details(host, project, args.patch_id, args.verbose)
    rev = get_highest_revision(details)

git_fetch(host, project, args.patch_id, rev)
if args.cherry_pick:
    git_cherry_pick_fetch_head()
else:
    git_checkout_fetch_head(args.patch_id, rev)
