#!/usr/libexec/platform-python

import os
import click
import platform
from subprocess import call, Popen, PIPE
import yaml
from glob import glob
import shutil
import grp
import hashlib
import shlex

INSTALL_SH='''#!/bin/sh
set -eu

ARCH=`uname -m`
if [ X"$ARCH" = X"i686" ]; then
    ARCH="i386"
fi
SCRIPT_FILE=`readlink -f "$0"`
BASE_DIR=`dirname "$SCRIPT_FILE"`
REPO_DIR=$BASE_DIR/$ARCH
YUM_CONF=$(mktemp)
trap "rm -f $YUM_CONF" EXIT
if [ -f /etc/yum.conf ]; then
    cat /etc/yum.conf > $YUM_CONF
    echo >> $YUM_CONF
fi
cat <<EOF >> $YUM_CONF
[{name}]
name={name}
baseurl=file://$REPO_DIR
enable=1
gpgcheck=0
EOF

yum -c $YUM_CONF {yum_opt} install {yes} {install_packages} "$@"

'''

YUM_CONF='''
[local]
name=Local RPMS
baseurl=file://{cwd}/RPMS
enabled=0
gpgcheck=0
'''

@click.group()
@click.option('--debug/--no-debug', default=False)
@click.pass_context
def cli(ctx, debug):
    ctx.obj['DEBUG'] = debug
    ctx.obj['ARCH'] = platform.machine()
    if ctx.obj['ARCH'] == 'i686':
        ctx.obj['ARCH'] = 'i386'
    ctx.obj['RPMS'] = 'RPMS/%s' % (ctx.obj['ARCH'])
    ctx.obj['OS'] = platform.dist()[0]
    ctx.obj['VERSION'] = int(platform.dist()[1].split('.')[0])

    if ctx.obj['OS'] == "centos" or ctx.obj['OS'] == "redhat":
        ctx.obj['OS'] = 'el'

    if ctx.obj['OS'] == "el":
        if 5 <= ctx.obj['VERSION'] <= 8:
            ctx.obj['OSVER'] = "el%d" % (ctx.obj['VERSION'])
            if ctx.obj['DEBUG']:
                print("OSVER: %s" % (ctx.obj['OSVER']))
        else:
            exit('unsupported version: %s' % (ctx.obj['VERSION']))
    else:
        exit('unsupported platform: %s' % (ctx.obj['OS']))


@cli.command()
@click.pass_context
def release(ctx):
    packages = glob('RPMS/%s/*.%s.%s.rpm' %
                    (ctx.obj['ARCH'], ctx.obj['OSVER'], ctx.obj['ARCH']))
    packages.extend(glob('RPMS/noarch/*.%s.noarch.rpm' % (ctx.obj['OSVER'])))
    targetdir = '/srv/share/product/RHEL%d/%s/RPMS' % \
                (ctx.obj['VERSION'], ctx.obj['ARCH'])
    os.umask(0o002)
    for p in packages:
        call(['cp', '-iv', p, targetdir])
    call(['make', '-C', targetdir])

def find_package_file(ctx):
    find_list = [
        "package-%s.yml" % (ctx.obj['OSVER']),
        "package-%s.yml" % (ctx.obj['OS']),
        "package.yml"
    ]
    for package_file in find_list:
        if ctx.obj['DEBUG']:
            print("finding package_file: %s" % (package_file))
        if os.path.exists(package_file):
            return package_file
    return None

@cli.command()
@click.pass_context
@click.option('--package-file')
@click.option('--format', '--formats', 'formats', default='gztar,iso')
@click.option('--local/--no-local', default=False)
@click.option('--output-dir', '-o', default='DIST')
def archive(ctx, package_file, formats, local, output_dir):
    if not package_file:
        package_file = find_package_file(ctx)
    if package_file:
        print("found package_file: %s" % (package_file))
    else:
        exit('package file does not exists.')
    package_info = yaml.load(open(package_file), Loader=yaml.SafeLoader)

    # ローカル用レポジトリ作成
    cmd = ['createrepo', 'RPMS']
    rc = call(cmd)
    if rc != 0:
        exit('createrepo failed: {}'.format(cmd))

    # ローカルの yum.conf を生成
    conf = YUM_CONF.format(cwd=os.getcwd())
    open('RPMS/yum.conf', 'w').write(conf)
    cmd = ['repoquery',
           '--config=RPMS/yum.conf',
           '--disablerepo=*',
           '--nvr']
    if local:
        cmd.append('--enablerepo=local')
    else:
        cmd.append('--enablerepo=osstech')
    cmd.append(package_info['main'])
    if ctx.obj['DEBUG']:
        print_cmd(cmd)
    outputs = Popen(cmd, stdout=PIPE).stdout.readlines()
    if len(outputs) == 0:
        exit('package %s does not found. try: repoquery %s' %
             (package_info['main'], package_info['main']))
    if len(outputs) > 1:
        print('repoquery returned multiple package. somethig wrong Xo')
    basename = outputs[-1].decode().rstrip()
    if not basename:
        exit('main package %s does not found.' % (package_info['main']))

    packages = [basename]
    del(cmd[-1])
    cmd.extend(package_info['packages'])
    if ctx.obj['DEBUG']:
        print_cmd(cmd)
    outputs = Popen(cmd, stdout=PIPE).stdout.readlines()
    packages += [x.decode().rstrip() for x in outputs if ' ' not in x.decode()]

    try:
        os.mkdir(output_dir)
    except:
        pass

    basedir = '{}/{}'.format(output_dir, basename)
    repodir = '{}/{}'.format(basedir, ctx.obj['ARCH'])
    if ctx.obj['DEBUG']:
        print("BASENAME: %s" % (basename))
        print("BASEDIR: %s" % (basedir))
        print("REPODIR: %s" % (repodir))
    try:
        os.makedirs(repodir)
    except:
        ans = input("directory %s is already exists. continue? [y/n]: "
                    % (repodir))
        if not ans.lower().startswith('y'):
            exit()

    # oceanからRPMを取ってくる
    cmd = ['yumdownloader', '-v',
            '--config=RPMS/yum.conf',
            '--disablerepo=*',
            '--enablerepo=osstech',
            '--destdir={}'.format(repodir)]
    if local:
        cmd.append('--enablerepo=local')
    cmd.extend(packages)
    if ctx.obj['DEBUG']:
        print_cmd(cmd)
    rc = call(cmd)
    if rc != 0:
        exit('yumdownloader failed. %s' % (' '.join(cmd)))


    # 念の為カレントディレクトリのRPMファイルで上書き
    # oceanのレポジトリとローカルに同じバージョンがあるかもしれないので
    if local:
        for f in glob('RPMS/%s/*.%s.%s.rpm' %
                      (ctx.obj['ARCH'], ctx.obj['OSVER'], ctx.obj['ARCH'])):
            if ctx.obj['DEBUG']:
                print("ADD LOCAL RPM: {}".format(f))
            shutil.copy(f, repodir)
        # 古いRPMを削除
        cmd = ['repomanage', '--keep=1', '--old', repodir]
        if ctx.obj['DEBUG']:
            print_cmd(cmd)
        outputs = Popen(cmd, stdout=PIPE).stdout.readlines()
        for line in outputs:
            if ' ' in line.decode():
                continue
            f = line.decode().rstrip()
            if ctx.obj['DEBUG']:
                print("DELETE OLD RPM: {}".format(f))
            os.remove(f)

    # createrepo
    if ctx.obj['VERSION'] >= 6:
        call(['createrepo', '--no-database', repodir])
    else:
        call(['createrepo', repodir])

    # create install file
    install_file = '{}/install.sh'.format(basedir)
    if 'install_packages' in package_info:
        install_packages = ' '.join(package_info['install_packages'])
    else:
        install_packages = package_info['main']
    if 'assumeyes' in package_info and package_info['assumeyes']:
        yes = '-y'
    else:
        yes = ''
    # enablerepoオプションなど
    yum_opt = ''
    if 'enablerepo' in package_info:
        for repo in package_info['enablerepo']:
            yum_opt += ' --enablerepo=\'%s\'' % (repo)
    script = INSTALL_SH.format(name=basename,
                               install_packages=install_packages,
                               yes=yes,
                               yum_opt=yum_opt)
    open(install_file, 'w').write(script)
    os.chmod(install_file, 0o755)

    # copy doc files
    if 'docs' in package_info:
        docdir = '{}/doc'.format(basedir)
        try:
            os.mkdir(docdir)
        except:
            pass
        for f in package_info['docs']:
            shutil.copy(f, docdir)

    for ftype in formats.split(','):
        if ftype == 'gztar':
            archive_file = '{}.tar.gz'.format(basedir)
            g = grp.getgrgid(0)
            shutil.make_archive(basedir, 'gztar', output_dir, basename, False, False, 'root', g.gr_name)
            make_sha1sum(archive_file)
        elif ftype == 'zip':
            shutil.make_archive(basedir, 'zip', output_dir, basename)
        elif ftype == 'iso':
            archive_file = '{}.iso'.format(basedir)
            call(['mkisofs', '-r', '-J', '-joliet-long', '-o', archive_file, basedir])
            make_sha1sum(archive_file)
            make_content_file(basedir)
        else:
            exit('unsupported archive format: %s' % [format])
    # cleanup
    shutil.rmtree(basedir)

def make_sha1sum(fn):
    hash = hashlib.sha1(open(fn, 'rb').read()).hexdigest()
    open('{}.sha1sum'.format(fn), 'w').write(hash)

def make_content_file(basedir):
    content_file = '{}.md'.format(basedir)
    cf = open(content_file, 'w')
    cf.write("# Table of contents\n\n")
    for root, dirs, files in os.walk(basedir):
        for f in files:
            path = os.path.join(root[len(basedir)+1:], f)
            if path.find('repodata') > 0:
                continue
            cf.write("- %s\n\n" % (path))

def print_cmd(cmd):
    print("CALL: {}".format(' '.join(list(map(shlex.quote, cmd)))))

if __name__ == "__main__":
    cli(obj={})
