# Copyright 2014 - Mirantis, Inc.
# Copyright 2016 - Brocade Communications Systems, Inc.
# Copyright 2020 Nokia Software.
#
#    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.
#

import logging

from oslo_serialization import jsonutils

from osc_lib.command import command

from mistralclient.commands.v2 import base
from mistralclient import utils

LOG = logging.getLogger(__name__)


class ActionExecutionFormatter(base.MistralFormatter):
    COLUMNS = [
        ('id', 'ID'),
        ('name', 'Name'),
        ('workflow_name', 'Workflow name'),
        ('workflow_namespace', 'Workflow namespace'),
        ('task_name', 'Task name'),
        ('task_execution_id', 'Task ID'),
        ('state', 'State'),
        ('state_info', 'State info'),
        ('accepted', 'Accepted'),
        ('created_at', 'Created at'),
        ('updated_at', 'Updated at'),
    ]

    LIST_COLUMN_FIELD_NAMES = [c[0] for c in COLUMNS if c[0] != 'state_info']
    LIST_COLUMN_HEADING_NAMES = [c[1] for c in COLUMNS if c[0] != 'state_info']

    @staticmethod
    def format(action_ex=None, lister=False):
        if lister:
            columns = ActionExecutionFormatter.LIST_COLUMN_HEADING_NAMES
        else:
            columns = ActionExecutionFormatter.headings()

        if action_ex:
            if hasattr(action_ex, 'task_name'):
                task_name = action_ex.task_name
            else:
                task_name = None

            data = (
                action_ex.id,
                action_ex.name,
                action_ex.workflow_name,
                action_ex.workflow_namespace,
                task_name,
                action_ex.task_execution_id,
                action_ex.state,
            )

            if not lister:
                data += (action_ex.state_info,)

            data += (
                action_ex.accepted,
                action_ex.created_at,
                action_ex.updated_at or '<none>'
            )
        else:
            data = (('',) * len(columns),)

        return columns, data


class Create(command.ShowOne):
    """Create new Action execution or just run specific action."""

    def produce_output(self, parsed_args, column_names, data):
        if not column_names:
            return 0

        return super(Create, self).produce_output(
            parsed_args,
            column_names,
            data
        )

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

        parser.add_argument(
            'name',
            help='Action name to execute.'
        )
        parser.add_argument(
            dest='input',
            nargs='?',
            help='Action input.'
        )
        parser.add_argument(
            '-s',
            '--save-result',
            dest='save_result',
            action='store_true',
            help='Save the result into DB.'
        )
        parser.add_argument(
            '--run-sync',
            dest='run_sync',
            action='store_true',
            help='Run the action synchronously.'
        )
        parser.add_argument(
            '-t',
            '--target',
            dest='target',
            help='Action will be executed on <target> executor.'
        )
        parser.add_argument(
            '--namespace',
            nargs='?',
            default='',
            help="Namespace of the action(s).",
        )

        return parser

    def take_action(self, parsed_args):
        params = {}

        if parsed_args.save_result:
            params['save_result'] = parsed_args.save_result

        if parsed_args.run_sync:
            params['run_sync'] = parsed_args.run_sync

        if parsed_args.target:
            params['target'] = parsed_args.target

        action_input = None

        if parsed_args.input:
            action_input = utils.load_json(parsed_args.input)

        mistral_client = self.app.client_manager.workflow_engine

        action_ex = mistral_client.action_executions.create(
            parsed_args.name,
            action_input,
            namespace=parsed_args.namespace,
            **params
        )

        if not parsed_args.run_sync and parsed_args.save_result:
            return ActionExecutionFormatter.format(action_ex)
        else:
            self.app.stdout.write("%s\n" % action_ex.output)

            return None, None


class List(base.MistralExecutionLister):
    """List all Action executions."""

    def _get_format_function(self):
        return ActionExecutionFormatter.format_list

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

        parser.add_argument(
            'task_execution_id',
            nargs='?',
            help='Task execution ID.'
        )

        return parser

    def _get_resources(self, parsed_args):
        mistral_client = self.app.client_manager.workflow_engine

        return mistral_client.action_executions.list(
            parsed_args.task_execution_id,
            marker=parsed_args.marker,
            limit=parsed_args.limit,
            sort_keys=parsed_args.sort_keys,
            sort_dirs=parsed_args.sort_dirs,
            # TODO(bobh) - Uncomment when the fix for bug 1800322 merges
            # fields=ActionExecutionFormatter.LIST_COLUMN_FIELD_NAMES,
            **base.get_filters(parsed_args)
        )


class Get(command.ShowOne):
    """Show specific Action execution."""

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

        parser.add_argument('action_execution', help='Action execution ID.')

        return parser

    def take_action(self, parsed_args):
        mistral_client = self.app.client_manager.workflow_engine

        execution = mistral_client.action_executions.get(
            parsed_args.action_execution
        )

        return ActionExecutionFormatter.format(execution)


class Update(command.ShowOne):
    """Update specific Action execution."""

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

        parser.add_argument(
            'id',
            help='Action execution ID.'
        )
        parser.add_argument(
            '--state',
            dest='state',
            choices=['PAUSED', 'RUNNING', 'SUCCESS', 'ERROR', 'CANCELLED'],
            help='Action execution state'
        )
        parser.add_argument(
            '--output',
            dest='output',
            help='Action execution output'
        )

        return parser

    def take_action(self, parsed_args):
        output = None

        if parsed_args.output:
            output = utils.load_json(parsed_args.output)

        mistral_client = self.app.client_manager.workflow_engine

        execution = mistral_client.action_executions.update(
            parsed_args.id,
            parsed_args.state,
            output
        )

        return ActionExecutionFormatter.format(execution)


class GetOutput(command.Command):
    """Show Action execution output data."""

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

        parser.add_argument(
            'id',
            help='Action execution ID.'
        )

        return parser

    def take_action(self, parsed_args):
        mistral_client = self.app.client_manager.workflow_engine

        output = mistral_client.action_executions.get(parsed_args.id).output

        try:
            output = jsonutils.loads(output)
            output = jsonutils.dumps(output, indent=4) + "\n"
        except Exception:
            LOG.debug("Task result is not JSON.")

        self.app.stdout.write(output or "\n")


class GetInput(command.Command):
    """Show Action execution input data."""

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

        parser.add_argument(
            'id',
            help='Action execution ID.'
        )

        return parser

    def take_action(self, parsed_args):
        mistral_client = self.app.client_manager.workflow_engine

        result = mistral_client.action_executions.get(parsed_args.id).input

        try:
            result = jsonutils.loads(result)
            result = jsonutils.dumps(result, indent=4) + "\n"
        except Exception:
            LOG.debug("Task result is not JSON.")

        self.app.stdout.write(result or "\n")


class Delete(command.Command):
    """Delete action execution."""

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

        parser.add_argument(
            'action_execution',
            nargs='+',
            help='Id of action execution identifier(s).'
        )

        return parser

    def take_action(self, parsed_args):
        mistral_client = self.app.client_manager.workflow_engine

        utils.do_action_on_many(
            lambda s: mistral_client.action_executions.delete(s),
            parsed_args.action_execution,
            "Request to delete action execution %s has been accepted.",
            "Unable to delete the specified action execution(s)."
        )
