#!/usr/bin/python3
# This file is part of Cockpit.
#
# Copyright (C) 2021 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.

import argparse
import os
import subprocess
import sys
import time

REPO = os.getenv("GITHUB_BASE", "cockpit-project/cockpit-machines") + "-dist"
PROJECT = REPO.split('/')[1]


def message(*args):
    print(*args, file=sys.stderr)


def download_dist(wait=False):
    '''Download pre-built dist/ for current git SHA from GitHub

    These are produced by .github/workflows/build-dist.yml for every PR and push.
    This is a lot faster than having to npm install and run webpack.

    Returns True when successful, or False if the download isn't available.
    This can happen because dist/ already exists, or the current directory is not a git checkout,
    or it is a SHA which is not pushed/PRed.
    '''
    if os.path.exists("dist"):
        message("download-dist: dist/ already exists")
        return False

    if os.getenv("NODE_ENV") == "development":
        message("download-dist: pre-built dist/ are for NODE_ENV=production")
        return False

    try:
        sha = subprocess.check_output(["git", "rev-parse", "HEAD"], stderr=subprocess.DEVNULL).decode().strip()
    except subprocess.CalledProcessError:
        message("download-dist: not a git repository")
        return False

    if subprocess.call(["git", "diff", "--quiet", "--", ":^test", ":^packit.yaml", ":^packaging", ":^.github"]) > 0:
        message("download-dist: uncommitted local changes, skipping download")
        return False

    dist_git_checkout = os.path.join(os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache")), "cockpit-dev", REPO)

    if not os.path.exists(dist_git_checkout):
        message(f"download-dist: Creating dist cache {dist_git_checkout}")
        # we can't directly clone a tag with clone -b, so always fetch main initially
        # this is usually very close to what we are interested in anyway
        subprocess.check_call(["git", "clone", "--quiet", "--depth=1", "--bare", "https://github.com/" + REPO, dist_git_checkout])

    retries = 50 if wait else 1  # 25 minutes, once every 30s
    while retries > 0:
        try:
            subprocess.check_call(["git", "-C", dist_git_checkout, "fetch", "origin", "sha-" + sha, "--depth=1"])
            break
        except subprocess.CalledProcessError:
            retries -= 1

            if retries == 0:
                message(f"download-dist: Downloading pre-built dist for SHA {sha} failed")
                return False

            message(f"download-dist: pre-built dist for {sha} not yet available, waiting...")
            time.sleep(30)

    message("download-dist: Extracting dist cache...")
    p_git = subprocess.Popen(["git", "-C", dist_git_checkout, "archive", "FETCH_HEAD", "dist", "package-lock.json"],
                             stdout=subprocess.PIPE)
    # need this to have current time, not commit time, to satisfy make dependencies
    subprocess.check_call(["tar", "--touch", "-x"], stdin=p_git.stdout)
    assert p_git.wait() == 0

    return True


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Download pre-built dist/ for current commit")
    parser.add_argument('-w', '--wait', action='store_true', help="Wait for up to 20 minutes for download tarball")
    args = parser.parse_args()
    if not download_dist(args.wait):
        sys.exit(1)
