# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

'''Stable library interface to managing containers with paunch.'''

import json
import pbr.version
import yaml

from paunch.builder import compose1
from paunch.builder import podman
from paunch import runner
from paunch.utils import common

__version__ = pbr.version.VersionInfo('paunch').version_string()


def apply(config_id, config, managed_by, labels=None, cont_cmd='podman',
          default_runtime=None, log_level=None, log_file=None,
          cont_log_path=None, healthcheck_disabled=False, cleanup=True):
    """Execute supplied container configuration.

    :param str config_id: Unique config ID, should not be re-used until any
                          running containers with that config ID have been
                          deleted.
    :param dict config: Configuration data describing container actions to
                        apply.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param dict labels: Optional keys/values of labels to apply to containers
                        created with this invocation.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    :param int log_level: optional log level for loggers
    :param str log_file: optional log file for messages
    :param str cont_log_path: optional log path for containers. Works only for
                              podman engine. Must be an absolute path.
    :param bool healthcheck_disabled: optional boolean to disable container
                                      healthcheck.
    :param bool cleanup: optional boolean to delete containers missing in the
                         config.

    :returns (list, list, int) lists of stdout and stderr for each execution,
                               and a single return code representing the
                               overall success of the apply.
    :rtype: tuple
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
        builder = podman.PodmanBuilder(
            config_id=config_id,
            config=config,
            runner=r,
            labels=labels,
            log=log,
            cont_log_path=cont_log_path,
            healthcheck_disabled=healthcheck_disabled,
            cleanup=cleanup
        )
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)
        builder = compose1.ComposeV1Builder(
            config_id=config_id,
            config=config,
            runner=r,
            labels=labels,
            log=log,
            cleanup=cleanup
        )
    return builder.apply()


def cleanup(config_ids, managed_by, cont_cmd='podman', default_runtime=None,
            log_level=None, log_file=None):
    """Delete containers no longer applied, rename others to preferred name.

    :param list config_ids: List of config IDs still applied. All containers
                            managed by this tool will be deleted if their
                            config ID is not specified in this list.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    :param int log_level: optional log level for loggers
    :param int log_file: optional log file for messages
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)

    r.delete_missing_configs(config_ids)
    r.rename_containers()


def list(managed_by, cont_cmd='podman', default_runtime=None,
         log_level=None, log_file=None):
    """List all containers associated with all config IDs.

    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    :param int log_level: optional log level for loggers
    :param int log_file: optional log file for messages

    :returns a dict where the key is the config ID and the value is a list of
             'podman inspect' dicts for each container.
    :rtype: defaultdict(list)
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)

    return r.list_configs()


def debug(config_id, container_name, action, config, managed_by, labels=None,
          cont_cmd='podman', default_runtime=None, log_level=None,
          log_file=None):
    """Execute supplied container configuration.

    :param str config_id: Unique config ID, should not be re-used until any
                          running containers with that config ID have been
                          deleted.
    :param str container_name: Name of the container in the config you
                               wish to manipulate.
    :param str action: Action to take.
    :param dict config: Configuration data describing container actions to
                        apply.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labeled with this will be modified.
    :param dict labels: Optional keys/values of labels to apply to containers
                        created with this invocation.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    :param int log_level: optional log level for loggers
    :param int log_file: optional log file for messages

    :returns integer return value from running command or failure for any
             other reason.
    :rtype: int
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
        builder = podman.PodmanBuilder(
            config_id=config_id,
            config=config,
            runner=r,
            labels=labels,
            log=log
        )
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)
        builder = compose1.ComposeV1Builder(
            config_id=config_id,
            config=config,
            runner=r,
            labels=labels,
            log=log
        )
    if action == 'print-cmd':
        uname = r.unique_container_name(container_name)
        cmd = [
            r.cont_cmd,
            'run',
            '--name',
            uname
        ]
        builder.label_arguments(cmd, container_name)
        builder.container_run_args(cmd, container_name, uname)

        if '--health-cmd' in cmd:
            health_check_arg_index = cmd.index('--health-cmd') + 1

            # The argument given needs to be quoted to work properly with a
            # copy and paste of the full command.
            try:
                cmd[health_check_arg_index] = (
                    '"%s"' % cmd[health_check_arg_index])
            except IndexError:
                log.warning("No argument provided to --health-cmd.")

        print(' '.join(cmd))
    elif action == 'run':
        uname = r.unique_container_name(container_name)
        cmd = [
            r.cont_cmd,
            'run',
            '--name',
            uname
        ]
        builder.label_arguments(cmd, container_name)
        if builder.container_run_args(cmd, container_name, uname):
            return r.execute_interactive(cmd, log)
    elif action == 'dump-yaml':
        print(yaml.safe_dump(config, default_flow_style=False))
    elif action == 'dump-json':
        print(json.dumps(config, indent=4))
    else:
        raise ValueError('action should be one of: "dump-json", "dump-yaml"',
                         '"print-cmd", or "run"')


def delete(config_ids, managed_by, cont_cmd='podman', default_runtime=None,
           log_level=None, log_file=None):
    """Delete containers with the specified config IDs.

    :param list config_ids: List of config IDs to delete the containers for.
    :param str managed_by: Name of the tool managing the containers. Only
                           containers labelled with this will be modified.
    :param str cont_cmd: Optional override to the container command to run.
    :param str default_runtime: (deprecated) does nothing.
    """
    log = common.configure_logging(__name__, log_level, log_file)
    if default_runtime:
        log.warning("DEPRECATION: 'default_runtime' does nothing, "
                    "use 'cont_cmd' instead")

    if not config_ids:
        log.warn('No config IDs specified')

    if cont_cmd == 'podman':
        r = runner.PodmanRunner(managed_by, cont_cmd=cont_cmd, log=log)
    else:
        r = runner.DockerRunner(managed_by, cont_cmd=cont_cmd, log=log)

    for conf_id in config_ids:
        r.remove_containers(conf_id)
