# Copyright 2014 - StackStorm, Inc.
#
# 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 django.views import generic

from django.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _

from horizon import exceptions
from horizon import forms
from horizon import tables

from mistraldashboard import api
from mistraldashboard.default import utils
from mistraldashboard.executions import forms as m_forms
from mistraldashboard.executions import tables as mistral_tables
from mistraldashboard import forms as mistral_forms


def get_single_data(request, id, type="execution"):
    """Get Execution or Task data by ID.

    :param request: Request data
    :param id: Entity ID
    :param type: Request dispatch flag, Default: Execution
    """

    if type == "execution":
        try:
            execution = api.execution_get(request, id)
        except Exception:
            msg = _('Unable to get execution by its ID"%s".') % id
            redirect = reverse('horizon:mistral:executions:index')
            exceptions.handle(request, msg, redirect=redirect)

        return execution

    elif type == "task":
        try:
            task = api.task_get(request, id)
        except Exception:
            msg = _('Unable to get task by its ID "%s".') % id
            redirect = reverse('horizon:mistral:tasks:index')
            exceptions.handle(request, msg, redirect=redirect)

        return task

    elif type == "task_by_execution":
        try:
            task = api.task_list(request, id)[0]
        except Exception:
            msg = _('Unable to get task by Execution ID "%s".') % id
            redirect = reverse('horizon:mistral:executions:index')
            exceptions.handle(request, msg, redirect=redirect)

        return task


class IndexView(tables.DataTableView):
    table_class = mistral_tables.ExecutionsTable
    template_name = 'mistral/executions/index.html'

    def has_prev_data(self, table):
        return self._prev

    def has_more_data(self, table):
        return self._more

    def get_data(self):
        executions = []
        prev_marker = self.request.GET.get(
            mistral_tables.ExecutionsTable._meta.prev_pagination_param,
            None
        )

        if prev_marker is not None:
            sort_dir = 'asc'
            marker = prev_marker
        else:
            sort_dir = 'desc'
            marker = self.request.GET.get(
                mistral_tables.ExecutionsTable._meta.pagination_param,
                None
            )

        try:
            executions, self._more, self._prev = api.pagination_list(
                entity="executions",
                request=self.request,
                marker=marker,
                sort_dirs=sort_dir,
                paginate=True
            )

            if prev_marker is not None:
                executions = sorted(
                    executions,
                    key=lambda execution: getattr(
                        execution, 'created_at'
                    ),
                    reverse=True
                )

        except Exception as e:
            self._prev = False
            self._more = False
            msg = _('Unable to retrieve executions list: %s') % e
            exceptions.handle(self.request, msg)

        return executions


class TasksView(tables.DataTableView):
    table_class = mistral_tables.ExecutionsTable
    template_name = 'mistral/executions/index_filtered_task.html'

    def has_prev_data(self, table):
        return self._prev

    def has_more_data(self, table):
        return self._more

    def get_data(self):
        executions = []
        prev_marker = self.request.GET.get(
            mistral_tables.ExecutionsTable._meta.prev_pagination_param,
            None
        )

        if prev_marker is not None:
            sort_dir = 'asc'
            marker = prev_marker
        else:
            sort_dir = 'desc'
            marker = self.request.GET.get(
                mistral_tables.ExecutionsTable._meta.pagination_param,
                None
            )

        try:
            executions, self._more, self._prev = api.pagination_list(
                entity="executions",
                request=self.request,
                marker=marker,
                sort_dirs=sort_dir,
                paginate=True,
                selector=self.kwargs['task_execution_id']
            )

            if prev_marker is not None:
                executions = sorted(
                    executions,
                    key=lambda execution: getattr(
                        execution, 'created_at'
                    ),
                    reverse=True
                )

        except Exception as e:
            self._prev = False
            self._more = False
            msg = (
                _('Unable to retrieve executions list of '
                  'the requested task: %s') % e
            )
            exceptions.handle(self.request, msg)

        return executions


class DetailView(generic.TemplateView):
    template_name = 'mistral/executions/detail.html'
    page_title = _("Execution Overview")
    workflow_url = 'horizon:mistral:workflows:detail'
    task_url = 'horizon:mistral:tasks:execution'

    def get_context_data(self, **kwargs):
        context = super(DetailView, self).get_context_data(**kwargs)
        task = {}
        execution = {}
        if 'caller' in kwargs:
            if kwargs['caller'] == 'task':
                kwargs['task_id'] = kwargs['execution_id']
                del kwargs['execution_id']
                task = get_single_data(
                    self.request,
                    kwargs['task_id'],
                    "task"
                )
                execution = get_single_data(
                    self.request,
                    task.workflow_execution_id,
                )
        else:
            execution = get_single_data(
                self.request,
                kwargs['execution_id'],
            )
            task = get_single_data(
                self.request,
                self.kwargs['execution_id'],
                "task_by_execution"
            )

        execution.workflow_url = reverse(self.workflow_url,
                                         args=[execution.workflow_name])
        execution.input = utils.prettyprint(execution.input)
        execution.output = utils.prettyprint(execution.output)
        execution.params = utils.prettyprint(execution.params)
        execution.state = utils.label(execution.state)
        task.url = reverse(self.task_url, args=[execution.id])

        breadcrumb = [(execution.id, reverse(
            'horizon:mistral:executions:detail',
            args=[execution.id]
        ))]

        context["custom_breadcrumb"] = breadcrumb
        context['execution'] = execution
        context['task'] = task

        return context


class CodeView(forms.ModalFormView):
    template_name = 'mistral/default/code.html'
    modal_header = _("Code view")
    form_id = "code_view"
    form_class = mistral_forms.EmptyForm
    cancel_label = "OK"
    cancel_url = reverse_lazy("horizon:mistral:executions:index")
    page_title = _("Code view")

    def get_context_data(self, **kwargs):
        context = super(CodeView, self).get_context_data(**kwargs)
        execution = get_single_data(
            self.request,
            self.kwargs['execution_id'],
        )
        column = self.kwargs['column']
        io = {}
        if column == 'input':
            io['name'] = _('Input')
            io['value'] = execution.input = utils.prettyprint(execution.input)
        elif column == 'output':
            io['name'] = _('Output')
            io['value'] = execution.output = utils.prettyprint(
                execution.output
            )

        context['io'] = io

        return context


class UpdateDescriptionView(forms.ModalFormView):
    template_name = 'mistral/executions/update_description.html'
    modal_header = _("Update Execution Description")
    form_id = "update_execution_description"
    form_class = m_forms.UpdateDescriptionForm
    submit_label = _("Update")
    success_url = reverse_lazy("horizon:mistral:executions:index")
    submit_url = "horizon:mistral:executions:update_description"
    cancel_url = "horizon:mistral:executions:index"
    page_title = _("Update Execution Description")

    def get_initial(self):
        return {"execution_id": self.kwargs["execution_id"]}

    def get_context_data(self, **kwargs):
        context = super(UpdateDescriptionView, self).get_context_data(**kwargs)
        context['submit_url'] = reverse(
            self.submit_url,
            args=[self.kwargs["execution_id"]]
        )

        return context
