#!python

# Python script to build/clean containers associated for a project
#
import argparse
import configparser
import os
import shlex
import subprocess
import sys

from dockerutils import *


def is_multistage(mode):
    return 'as builder' in open(f'docker/{mode}/Dockerfile').read()


def run_pre_script(script: str, config: dict) -> int:
    print(f'Running pre-build-script: "{script}"')
    return subprocess.call(shlex.split(script), cwd=os.getcwd())


def run_post_script(script: str, config: dict) -> int:
    print(f'Running post-build-script: "{script}"')
    return subprocess.call(shlex.split(script), cwd=os.getcwd())


def build(image, image_name, image_tag, config={}, pull=False):
    pre_script = config.get('pre_build_script', None)
    post_script = config.get('post_build_script', None)

    if pre_script:
        rc = run_pre_script(pre_script, config=config)
        if rc != 0:
            print(f'pre-build-script failed: {rc}')
            return rc

    rc = 0
    pull_base = ''
    if pull:
        pull_base = '--pull'
    if is_multistage(image):
        # if this is a multistage build and it follows the conventions, tag the builder image
        # otherwise, a prune will remove the layers used during the builder phase and subsequent
        # builds will take longer than required
        rc = image_operation(f'docker build {pull_base} --compress -t {image_name}-builder:{image_tag} '
                             f'-f docker/{image}/Dockerfile --target builder .')
    if not rc:
        rc = image_operation(f'docker build {pull_base} --compress -t {image_name}:{image_tag} '
                             f'-f docker/{image}/Dockerfile .')

    if rc != 0:
        print(f'docker build failed: {rc}')
        return rc

    if post_script:
        rc = run_post_script(post_script, config=config)

    return rc


def clean(image, image_name, image_tag, pull=False):
    rc = image_operation(f'docker rmi {image_name}:{image_tag}')
    if is_multistage(image):
        image_operation(f'docker rmi {image_name}-builder:{image_tag}')
    return rc


def image_operation(operation):
    print('\n\n============================================================================')
    print(f'{operation}\n\n')
    return subprocess.call(shlex.split(operation), cwd=os.getcwd())


if __name__ == '__main__':
    try:
        root_dir = get_root_dir()
    except ValueError as e:
        print(str(e))
        sys.exit(1)
    with cd(root_dir):
        config = configparser.ConfigParser()
        config.optionxform = str
        config.read(os.path.join('docker', 'dockerutils.cfg'))

        if os.path.isfile('setup.cfg'):
            config_versioneer = configparser.ConfigParser()
            config_versioneer.optionxform = str
            config_versioneer.read('setup.cfg')
            if 'versioneer' in config_versioneer:
                gen_version_file()

        with pip_conf(root_dir):
            image_types = get_image_types()

            parser = argparse.ArgumentParser()
            parser.add_argument("image", choices=['all', 'clean'] + image_types, help="image to build")
            parser.add_argument("-f", "--force_build_base", help="Build the base along with the requested image",
                                action='store_true')
            parser.add_argument("-i", "--image_name", help="use this image name rather than the default")
            parser.add_argument("-t", "--image_tag", help="use this image tag rather than the default")
            parser.add_argument("-p", "--pull_base", help="pull the base image as part fo the build", action='store_true')
            args = parser.parse_args()

            images_to_build = []
            fn = build
            if args.image == 'all':
                images_to_build = image_types
            elif args.image == 'clean':
                images_to_build = image_types
                fn = clean
            else:
                images_to_build = []
                if not args.image == 'base' and args.force_build_base and 'base' in image_types:
                    images_to_build.append('base')
                images_to_build.append(args.image)

            for image in images_to_build:
                image_name, image_tag = get_image_designation(image, config)
                if args.image_name:
                    image_name = args.image_name
                if args.image_tag:
                    image_tag = args.image_tag

                pull_FROM_on_force = False
                image_config = {}
                if image in config.sections():
                    image_config = config[image]
                    if 'pull_FROM_on_force' in image_config:
                        pull_FROM_on_force = config[image]['pull_FROM_on_force']

                if os.path.isfile(f'docker/{image}/Dockerfile'):
                    rc = fn(image, image_name, image_tag, config=image_config, pull=args.pull_base or (args.force_build_base and pull_FROM_on_force))
                    # because an image may not be present on the clean, ignore a non-zero return code
                    if rc and not args.image == 'clean':
                        sys.exit(rc)
    sys.exit(0)
