#!/usr/bin/env python3
"""Run the Docker container demo of nprintML"""
import argparse
import os
import pathlib
import platform
import re
import subprocess


DEBUG = os.getenv('NPRINTML_DOCKER_DEBUG', '0') == '1'

CONTAINER_REMOVE = os.getenv('NPRINTML_DOCKER_REMOVE', '1') == '1'

CONTAINER_REPO = os.getenv('NPRINTML_DOCKER_REPOSITORY', 'ghcr.io/nprint/nprintml')

CONTAINER_VERSION = os.getenv('NPRINTML_DOCKER_VERSION', 'latest')

CHOWN_OUTPUT = os.getenv('NPRINTML_DOCKER_CHOWN', '1') == '1'

MOUNT_PATH = '/mnt/nprintml-docker/'

RUN_PATH = '/run/nprintml-docker/'

COMMAND_BASE = (
    'docker',
    'run',
    '--workdir', RUN_PATH,
)

if CONTAINER_REMOVE:
    COMMAND_BASE += (
        '--rm',
    )

CURRENT_UID = os.getuid()
CURRENT_GID = os.getgid()

SUDO_UID = os.getenv('SUDO_UID')
SUDO_GID = os.getenv('SUDO_GID')


def map_path(command, args, flag, path):
    abspath = os.path.abspath(path)
    mount_path = os.path.join(MOUNT_PATH, abspath.lstrip(os.path.sep))

    command1 = command + [
        '--volume',
        f'{abspath}:{mount_path}',
    ]

    if flag is None:
        args1 = args + [
            mount_path,
        ]
    else:
        args1 = args + [
            flag,
            mount_path,
        ]

    return (command1, args1)


def flag_match(arg):
    return arg in flag_match.words or flag_match.pattern.fullmatch(arg)

flag_match.words = {'learn', '-C', '-L', '-I', '-N', '-P'}
flag_match.pattern = re.compile(r'-.*\b((file)|(dir))\b.*')


def map_paths(command, args):
    command1 = command
    args1 = []

    for (index, arg1) in enumerate(args):
        if index > 0 and flag_match(args[index - 1]) and os.path.exists(arg1):
            (command1, args1) = map_path(command1, args1, None, arg1)
        else:
            args1.append(arg1)

    return (command1, args1)


def log(*args, start='\n'):
    print(f'{start}[nprintml-docker]', *args)


def main(args, outdir=None):
    command = list(COMMAND_BASE)

    (command, args) = map_paths(command, args)

    if outdir:
        (command, args) = map_path(command, args, '--output', outdir)
    else:
        outdir = pathlib.Path.cwd() / 'nprintml'
        outdir_mount = pathlib.Path(RUN_PATH) / 'nprintml'
        command.extend([
            '--volume',
            f'{outdir}:{outdir_mount}',
        ])

    command.append(f'{CONTAINER_REPO}:{CONTAINER_VERSION}')
    command.extend(args)

    if DEBUG:
        log('command:', *command, start='')

    command_proc = subprocess.run(command)

    if CHOWN_OUTPUT and (CURRENT_UID or SUDO_UID) and not command_proc.returncode:
        # 1. use subprocess to make use of "sudo"
        # 2. use pipe rather than "chown --from" to support darwin
        chown_command = ['chown']

        if CURRENT_UID:
            chown_command.insert(0, 'sudo')
            chown_command.append(f'{CURRENT_UID}:{CURRENT_GID}')
        else:
            chown_command.append(f'{SUDO_UID}:{SUDO_GID}')

        if platform.system() == 'Linux':
            chown_command.insert(-1, '--recursive')
            chown_command.insert(-1, '--from')
            chown_command.insert(-1, '0')
            chown_command.append(outdir)

            log('clean-up:', *chown_command)
            chown_proc = subprocess.run(chown_command)
        else:
            chown_index = chown_command.index('chown')
            chown_command.insert(chown_index, 'xargs')
            chown_command.insert(chown_index + 1, '-0')

            find_command = ['find', outdir, '-user', '0', '-print0']

            log('clean-up:', *find_command, '|', *chown_command)
            find_proc = subprocess.Popen(find_command, stdout=subprocess.PIPE)
            chown_proc = subprocess.run(chown_command, stdin=find_proc.stdout)

        return (command_proc, chown_proc)

    return (command_proc,)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        add_help=False,
        usage="%(prog)s [options]",
    )

    parser.add_argument(
        '-o', '--output',
        dest='outdir',
        type=pathlib.Path,
    )

    (args, remainder) = parser.parse_known_args()

    processes = main(args=remainder, **vars(args))

    raise SystemExit(max(proc.returncode for proc in processes))