# Copyright (c) 2018 European Organization for Nuclear Research.
# All Rights Reserved.
#
#    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.

from magnumclient.common import utils as magnum_utils
from magnumclient.i18n import _

from osc_lib.command import command
from osc_lib import utils


NODEGROUP_ATTRIBUTES = [
    'uuid',
    'name',
    'cluster_id',
    'project_id',
    'docker_volume_size',
    'labels',
    'labels_overridden',
    'labels_skipped',
    'labels_added',
    'flavor_id',
    'image_id',
    'node_addresses',
    'node_count',
    'role',
    'max_node_count',
    'min_node_count',
    'is_default',
    'stack_id',
    'status',
    'status_reason',
]


class CreateNodeGroup(command.Command):
    _description = _("Create a nodegroup")

    def get_parser(self, prog_name):
        parser = super(CreateNodeGroup, self).get_parser(prog_name)
        # NOTE: All arguments are positional and, if not provided
        # with a default, required.
        parser.add_argument('--docker-volume-size',
                            dest='docker_volume_size',
                            type=int,
                            metavar='<docker-volume-size>',
                            help=('The size in GB for the docker volume to '
                                  'use.'))
        parser.add_argument('--labels',
                            metavar='<KEY1=VALUE1,KEY2=VALUE2;KEY3=VALUE3...>',
                            action='append',
                            help=_('Arbitrary labels in the form of key=value'
                                   'pairs to associate with a nodegroup. '
                                   'May be used multiple times.'))
        parser.add_argument('cluster',
                            metavar='<cluster>',
                            help='Name of the nodegroup to create.')
        parser.add_argument('name',
                            metavar='<name>',
                            help='Name of the nodegroup to create.')
        parser.add_argument('--node-count',
                            dest='node_count',
                            type=int,
                            default=1,
                            metavar='<node-count>',
                            help='The nodegroup node count.')
        parser.add_argument('--min-nodes',
                            dest='min_node_count',
                            type=int,
                            default=0,
                            metavar='<min-nodes>',
                            help='The nodegroup minimum node count.')
        parser.add_argument('--max-nodes',
                            dest='max_node_count',
                            type=int,
                            default=None,
                            metavar='<max-nodes>',
                            help='The nodegroup maximum node count.')
        parser.add_argument('--role',
                            dest='role',
                            type=str,
                            default='worker',
                            metavar='<role>',
                            help=('The role of the nodegroup'))
        parser.add_argument(
            '--image',
            metavar='<image>',
            help=_('The name or UUID of the base image to customize for the '
                   'NodeGroup.'))
        parser.add_argument(
            '--flavor',
            metavar='<flavor>',
            help=_('The nova flavor name or UUID to use when launching the '
                   'nodes in this NodeGroup.'))
        parser.add_argument(
            '--merge-labels',
            dest='merge_labels',
            action='store_true',
            default=False,
            help=_('The labels provided will be merged with the labels '
                   'configured in the specified cluster.'))

        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)", parsed_args)

        mag_client = self.app.client_manager.container_infra
        args = {
            'name': parsed_args.name,
            'node_count': parsed_args.node_count,
            'max_node_count': parsed_args.max_node_count,
            'min_node_count': parsed_args.min_node_count,
            'role': parsed_args.role,
        }

        if parsed_args.labels is not None:
            args['labels'] = magnum_utils.handle_labels(parsed_args.labels)

        if parsed_args.docker_volume_size is not None:
            args['docker_volume_size'] = parsed_args.docker_volume_size

        if parsed_args.flavor is not None:
            args['flavor_id'] = parsed_args.flavor

        if parsed_args.image is not None:
            args['image_id'] = parsed_args.image

        if parsed_args.merge_labels:
            # We are only sending this if it's True. This
            # way we avoid breaking older APIs.
            args["merge_labels"] = parsed_args.merge_labels

        cluster_id = parsed_args.cluster
        nodegroup = mag_client.nodegroups.create(cluster_id, **args)
        print("Request to create nodegroup %s accepted"
              % nodegroup.uuid)


class DeleteNodeGroup(command.Command):
    _description = _("Delete a nodegroup")

    def get_parser(self, prog_name):
        parser = super(DeleteNodeGroup, self).get_parser(prog_name)
        parser.add_argument(
            'cluster',
            metavar='<cluster>',
            help=_('ID or name of the cluster where the nodegroup(s) '
                   'belong(s).'))
        parser.add_argument(
            'nodegroup',
            nargs='+',
            metavar='<nodegroup>',
            help='ID or name of the nodegroup(s) to delete.')

        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)", parsed_args)

        mag_client = self.app.client_manager.container_infra
        cluster_id = parsed_args.cluster
        for ng in parsed_args.nodegroup:
            mag_client.nodegroups.delete(cluster_id, ng)
            print("Request to delete nodegroup %s has been accepted." % ng)


class ListNodeGroup(command.Lister):
    _description = _("List nodegroups")

    def get_parser(self, prog_name):
        parser = super(ListNodeGroup, self).get_parser(prog_name)

        parser.add_argument(
            'cluster',
            metavar='<cluster>',
            help=_('ID or name of the cluster where the nodegroup belongs.'))
        parser.add_argument(
            '--limit',
            metavar='<limit>',
            type=int,
            help=_('Maximum number of nodegroups to return'))
        parser.add_argument(
            '--sort-key',
            metavar='<sort-key>',
            help=_('Column to sort results by'))
        parser.add_argument(
            '--sort-dir',
            metavar='<sort-dir>',
            choices=['desc', 'asc'],
            help=_('Direction to sort. "asc" or "desc".'))
        parser.add_argument(
            '--role',
            metavar='<role>',
            help=_('List the nodegroups in the cluster with this role'))

        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)", parsed_args)

        mag_client = self.app.client_manager.container_infra
        columns = ['uuid', 'name', 'flavor_id', 'image_id', 'node_count',
                   'status', 'role']
        cluster_id = parsed_args.cluster
        nodegroups = mag_client.nodegroups.list(cluster_id,
                                                limit=parsed_args.limit,
                                                sort_key=parsed_args.sort_key,
                                                sort_dir=parsed_args.sort_dir,
                                                role=parsed_args.role)
        return (
            columns,
            (utils.get_item_properties(n, columns) for n in nodegroups)
        )


class ShowNodeGroup(command.ShowOne):
    _description = _("Show a nodegroup")

    def get_parser(self, prog_name):
        parser = super(ShowNodeGroup, self).get_parser(prog_name)
        parser.add_argument(
            'cluster',
            metavar='<cluster>',
            help=_('ID or name of the cluster where the nodegroup belongs.'))
        parser.add_argument(
            'nodegroup',
            metavar='<nodegroup>',
            help=_('ID or name of the nodegroup to show.')
            )
        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)", parsed_args)

        columns = NODEGROUP_ATTRIBUTES

        mag_client = self.app.client_manager.container_infra
        cluster_id = parsed_args.cluster
        nodegroup = mag_client.nodegroups.get(cluster_id,
                                              parsed_args.nodegroup)

        return (columns, utils.get_item_properties(nodegroup, columns))


class UpdateNodeGroup(command.Command):
    _description = _("Update a Nodegroup")

    def get_parser(self, prog_name):
        parser = super(UpdateNodeGroup, self).get_parser(prog_name)
        parser.add_argument(
            'cluster',
            metavar='<cluster>',
            help=_('ID or name of the cluster where the nodegroup belongs.'))
        parser.add_argument(
            'nodegroup',
            metavar='<nodegroup>',
            help=_('The name or UUID of cluster to update'))

        parser.add_argument(
            'op',
            metavar='<op>',
            choices=['add', 'replace', 'remove'],
            help=_("Operations: one of 'add', 'replace' or 'remove'"))

        parser.add_argument(
            'attributes',
            metavar='<path=value>',
            nargs='+',
            action='append',
            default=[],
            help=_(
                "Attributes to add/replace or remove (only PATH is necessary "
                "on remove)"))

        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)", parsed_args)

        mag_client = self.app.client_manager.container_infra

        patch = magnum_utils.args_array_to_patch(parsed_args.op,
                                                 parsed_args.attributes[0])

        cluster_id = parsed_args.cluster
        mag_client.nodegroups.update(cluster_id, parsed_args.nodegroup,
                                     patch)
        print("Request to update nodegroup %s has been accepted." %
              parsed_args.nodegroup)