#!/usr/bin/python3
# coding: utf-8

import click
from os.path import join, dirname, realpath
import sys

HERE = realpath(dirname(__file__))
# this is to allow to use it directory from source repository
sys.path.append(join(HERE, 'src'))

from debocker.utils import tmppath, \
    calculate_md5_and_size, log_check_call, log_check_output, \
    assert_command
from debocker.log import log, fail, LOW, set_verbosity, is_verbose
from debocker.debian import Package, docker_build_bundle, STAGES

# CLI INTERFACE

@click.group()
@click.option('-v', '--verbose', count=True,
              help = 'be verbose, repeat for more effect')
def cli(verbose):
    set_verbosity(verbose)

@cli.command(help = 'Write tar bundle')
@click.argument('path', default = '.')
@click.option('-o', '--output', default = None, metavar = 'FILE',
              help = 'output file')
@click.option('-f', '--flags', default = '', metavar = 'FLAGS',
              help = 'build flags')
@click.option('step', '--from', default = 'end',
              help = 'start from the given step',
              type = click.Choice(STAGES))
@click.option('--image', default = 'debian:unstable', metavar = 'IMAGE',
              help = 'base docker image')
def bundle(path, output, flags, step, image):
    pkg = Package(path)
    pkg.assert_is_valid()
    if output is None:
        name = '{}_{}_bundle.tar'.format(pkg.name, pkg.long_version)
        output = join('..', name)
    log('Preparing bundle for {} ({})...'.format(pkg.name, pkg.version))
    if not pkg.native:
        pkg.assert_orig_tarball()
    pkg.build_docker_tarball(output, {
        'flags': flags, 'step': step, 'image': image })
    log("Bundle created in '{}'.".format(output))
    if is_verbose():
        md5, size = calculate_md5_and_size(output)
        log('Bundle hash and size: {}, {}.'.format(md5, size), LOW)

@cli.command('build-bundle', help = 'Build bundle')
@click.argument('bundle')
@click.option('-o', '--output', default = '.', metavar = 'DIRECTORY',
              help = 'output directory')
@click.option('--sign', '-s', default = False,
              is_flag = True, help = 'sign built package with debsign')
@click.option('no_cache', '--no-cache', default = False,
              is_flag = True, help = 'do not use docker image cache')
@click.option('--pull', default = False,
              is_flag = True, help = 'pull the newest base image')
def build_bundle(bundle, output, sign, no_cache, pull):
    assert_command('docker')
    if sign:
        assert_command('debsign')
    image = docker_build_bundle(bundle,
                                no_cache = no_cache, pull = pull)
    log('Build successful (in {})'.format(image))
    # extract the build
    build_tar = tmppath('build.tar')
    with open(build_tar, 'wb') as f:
        log_check_call([ 'docker', 'run', '--rm=true',
                         image, '/root/steps/build-tar' ], stdout = f)
    log("Build tar stored in '{}'".format(build_tar))
    tar_list = log_check_output([ 'tar', 'tf', build_tar ])
    tar_files = tar_list.decode('utf-8').split()
    log("Build files: {}".format(' '.join(tar_files)), LOW)
    log_check_call([ 'tar', 'xf', build_tar, '-C', output ])
    log("Build files stored in '{}'.".format(output))
    if sign:
        # TODO: needs devscripts, and hence will not work outside Debian
        # we probably have to copy/fork debsign, cause signing within
        # the container is not a good idea security-wise
        changes = [ fn for fn in tar_files if fn.endswith('.changes') ]
        if len(changes) != 1:
            fail('could not find the changes files')
        changes_path = join(output, changes[0])
        log("Trying to sign '{}'.".format(changes_path), LOW)
        log_check_call([ 'debsign', changes_path ])

@cli.command(help = 'Build package')
@click.argument('path', default = '.')
@click.option('-o', '--output', default = '..', metavar = 'DIRECTORY',
              help = 'output directory')
@click.option('--sign', '-s', default = False,
              is_flag = True, help = 'sign built package with debsign')
@click.option('-f', '--flags', default = '', metavar = 'FLAGS',
              help = 'build flags')
@click.option('no_cache', '--no-cache', default = False,
              is_flag = True, help = 'do not use docker image cache')
@click.option('step', '--from', default = 'end',
              help = 'start from the given step',
              type = click.Choice(STAGES))
@click.option('--image', default = 'debian:unstable', metavar = 'IMAGE',
              help = 'base docker image')
@click.option('--pull', default = False,
              is_flag = True, help = 'pull the newest base image')
@click.pass_context
def build(ctx, path, output, sign, flags, no_cache, step, image, pull):
    assert_command('docker')
    if sign:
        assert_command('debsign')
    tarball_path = tmppath('bundle.tar')
    ctx.invoke(bundle, path = path, output = tarball_path,
               flags = flags, step = step, image = image)
    ctx.invoke(build_bundle,
               bundle = tarball_path, sign = sign, output = output,
               no_cache = no_cache, pull = pull)

if __name__ == '__main__':
    cli.main()
