#!/usr/bin/python3 -s

import subprocess
import sys
import argparse
import os
import re
import time
import shutil

parser = argparse.ArgumentParser(description='Remove old packages from rpm-md repository')

parser.add_argument('path', action='store',
                   help='local path to a yum repository')
parser.add_argument('--days', type=int, action='store', default=0,
                   help='only remove packages older than DAYS by their build date, does remove srpm only if associated rpm is removed')
parser.add_argument('--cleancopr', action='store_true',
                   help='additionaly remove whole copr build dirs and logs if the associated package gets deleted')
parser.add_argument('--nocreaterepo', action='store_true',
                   help='repository is not automatically recreated after deletion')
parser.add_argument('--verbose', action='store_true',
                   help='print all deleted items to stdout')
parser.add_argument('--quiet', action='store_true',
                   help='do not print any info messages, just do your job')
parser.add_argument('-v', '--version', action='version', version='1.0',
                   help='print program version and exit')

args = parser.parse_args()


get_all_packages_cmd = [
    'dnf',
    'repoquery',
    '--disablerepo=*',
    '--repofrompath=prunerepo_query,'+os.path.abspath(args.path),
    '--repoid=prunerepo_query',
    '--enablerepo=prunerepo_query',
    '--refresh',
    '--queryformat="%{location}"',
    '--quiet',
]

get_latest_packages_cmd = get_all_packages_cmd + [ '--latest-limit=1' ]


def is_srpm(package):
    return re.match(r'.*\.src\.rpm$', package)


def rm_file(path):
    if args.verbose:
        log_info("Removing: "+path)
    if os.path.exists(path) and os.path.isfile(path):
        os.remove(path)


def log_info(msg):
    if not args.quiet:
        print(msg)


def run_cmd(cmd, silent=False):
    """
    Run given command in a subprocess
    """
    if not silent:
        log_info("Executing: "+' '.join(cmd))
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (stdout, stderr) = process.communicate()
    if process.returncode != 0:
        print(stderr.decode(encoding='utf-8'), file=sys.stderr)
        sys.exit(1)
    return [line.strip('"') for line in stdout.decode(encoding='utf-8').split()]


def get_package_build_time(package_path):
    """
    Get build time by reading package metadata
    """
    createrepo_cmd = ['/usr/bin/rpm', '-qp', '--queryformat', '%{BUILDTIME}'] + [ package_path ]
    stdout = run_cmd(createrepo_cmd, silent=True)
    return int(stdout[0])


def get_packages(repoquery_cmd):
    """
    Get paths to packages in the repository according to given repoquery_cmd
    """
    stdout = run_cmd(repoquery_cmd)
    rel_package_paths = [relpath.strip('"') for relpath in stdout]
    abs_package_paths = [os.path.abspath(os.path.join(args.path, relpath)) for relpath in rel_package_paths]
    return abs_package_paths


def rm_srpm(package):
    """
    Attempt to remove srpm for the associated rpm package described by absolute fs path
    """
    get_srpm_cmd = get_all_packages_cmd + [ '--srpm', os.path.splitext(os.path.basename(package))[0] ]
    srpm = run_cmd(get_srpm_cmd, silent=True)[0]
    rm_file(os.path.abspath(os.path.join(args.path, srpm)))


def prune_packages():
    """
    Remove obsoleted packages
    """
    log_info('Removing obsoleted packages...')
    latest_packages = get_packages(get_latest_packages_cmd)
    all_packages = get_packages(get_all_packages_cmd)
    to_remove_packages = set(all_packages) - set(latest_packages)
    for package in to_remove_packages:
        if args.days:
            if not is_srpm(package) and time.time() - get_package_build_time(package) > args.days * 24 * 3600:
                rm_file(package)
                rm_srpm(package)
        else:
            rm_file(package)


def recreate_repo():
    """
    Recreate the repository by using createrepo_c
    """
    log_info("Recreating repository...")
    createrepo_cmd = ['/usr/bin/createrepo_c', '--database', '--update'] + [ args.path ]
    return run_cmd(createrepo_cmd)


def clean_copr():
    """
    Remove whole copr build dirs if they no longer contain a srpm/rpm file
    """
    log_info("Cleaning COPR repository...")
    for dir_name in os.listdir(args.path):
        dir_path = os.path.abspath(os.path.join(args.path, dir_name))

        if not os.path.isdir(dir_path):
            continue
        if not os.path.isfile(os.path.join(dir_path, 'build.info')):
            continue
        if [item for item in os.listdir(dir_path) if re.match(r'.*\.rpm$', item)]:
            continue

        if args.verbose:
            log_info('Removing: '+dir_path)
        shutil.rmtree(dir_path)

        # also remove the associated log in the main dir
        build_id = os.path.basename(dir_path).split('-')[0]
        buildlog_name = 'build-' + build_id + '.log'
        buildlog_path = os.path.abspath(os.path.join(args.path, buildlog_name))
        if args.verbose:
            log_info('Removing: '+buildlog_path)
        os.remove(os.path.join(args.path, buildlog_path))


if __name__ == '__main__':
    prune_packages()
    if not args.nocreaterepo:
        recreate_repo()
    if args.cleancopr:
        clean_copr()
