#!/usr/bin/env python3

import argparse
import getpass
import logging
import os
import sys

import requests

log = logging.getLogger(__name__)


DEFAULT_HEADERS = {
    "Accept": "application/vnd.github+json",
    "X-GitHub-Api-Version": "2022-11-28",
}


def get_auth_header(token):
    return {"Authorization": f"Bearer {token}"}


def get_latest_draft_release(token):
    url = "https://api.github.com/repos/freedomofpress/dangerzone/releases"
    headers = DEFAULT_HEADERS.copy()
    headers.update(get_auth_header(token))

    r = requests.get(url, headers=headers)
    r.raise_for_status()

    draft_releases = [release["id"] for release in r.json() if release["draft"]]
    if len(draft_releases) > 1:
        raise RuntimeError("Found more than one draft releases")
    elif len(draft_releases) == 0:
        raise RuntimeError("No draft releases have been found")

    return draft_releases[0]


def get_release_from_tag(token, tag):
    url = f"https://api.github.com/repos/freedomofpress/dangerzone/releases/tags/v{tag}"
    headers = DEFAULT_HEADERS.copy()
    headers.update(get_auth_header(token))

    r = requests.get(url, headers=headers)
    r.raise_for_status()

    return r.json()["id"]


def upload_asset(token, release_id, path):
    filename = os.path.basename(path)
    url = f"https://uploads.github.com/repos/freedomofpress/dangerzone/releases/{release_id}/assets?name={filename}"
    headers = DEFAULT_HEADERS.copy()
    headers.update(get_auth_header(token))
    headers["Content-Type"] = "application/octet-stream"

    with open(path, "rb") as f:
        data = f.read()
        # XXX: We have to load the data in-memory. Another solution is to use multipart
        # encoding, but this doesn't work for GitHub.
        r = requests.post(url, headers=headers, data=data)
        r.raise_for_status()


def setup_logging():
    logging.basicConfig(
        level=logging.DEBUG,
        format="%(asctime)s - %(levelname)s - %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
    )


def main():
    parser = argparse.ArgumentParser(
        prog=sys.argv[0],
        description="Dev script for uploading assets to a GitHub release",
    )
    parser.add_argument(
        "--token",
        help="the file path to the GitHub token we will use for uploading assets",
    )
    parser.add_argument(
        "--tag",
        help="use the release with this tag",
    )
    parser.add_argument(
        "--release-id",
        help="use the release with this ID",
    )
    parser.add_argument(
        "--draft",
        action="store_true",
        help="use the latest draft release",
    )
    parser.add_argument(
        "file",
        help="the file path to the asset we want to upload",
    )
    args = parser.parse_args()

    setup_logging()

    if args.token:
        log.debug(f"Reading token from {args.token}")
        # Ensure we are not uploading the token as an asset
        assert args.file != args.token
        with open(args.token) as f:
            token = f.read().strip()
    else:
        token = getpass.getpass("Token: ")

    if args.tag:
        log.debug(f"Getting the ID of the {args.tag} release")
        release_id = get_release_from_tag(token, args.tag)
        log.debug(f"The {args.tag} release has ID '{release_id}'")
    elif args.release_id:
        release_id = args.release_id
    else:
        log.debug("Getting the ID of the latest draft release")
        release_id = get_latest_draft_release(token)
        log.debug(f"The latest draft release has ID '{release_id}'")

    log.info(f"Uploading file '{args.file}' to GitHub release '{release_id}'")
    upload_asset(token, release_id, args.file)
    log.info(
        f"Successfully uploaded file '{args.file}' to GitHub release '{release_id}'"
    )


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