# Copyright 2016 FUJITSU LIMITED
#
# 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.utils.translation import ugettext_lazy as _  # noqa
from django.views.decorators.debug import sensitive_variables

from horizon import exceptions
from horizon import forms
from horizon.utils import memoized
from horizon import workflows

from monitoring.alarmdefs import constants
from monitoring.alarmdefs import forms as ad_forms
from monitoring import api


class SetAlarmNotificationsAction(workflows.Action):
    notifications = ad_forms.NotificationField(
        label=_('Notifications'),
        required=False,
        widget=ad_forms.NotificationCreateWidget(),
        help_text=_('Notification methods. '
                    'Notifications can be sent when an alarm '
                    'state transition occurs.'))

    alarm_actions = ad_forms.NotificationField(
        label=_("Alarm Actions"),
        required=False,
        widget=forms.MultipleHiddenInput()
    )
    ok_actions = ad_forms.NotificationField(
        label=_("OK Actions"),
        required=False,
        widget=forms.MultipleHiddenInput()
    )
    undetermined_actions = ad_forms.NotificationField(
        label=_("Undetermined Actions"),
        required=False,
        widget=forms.MultipleHiddenInput()
    )

    class Meta(object):
        name = _('Notifications')
        help_text_template = ("monitoring/alarmdefs/"
                              "_create_ad_notification_help.html")

    def __init__(self, request, context, *args, **kwargs):
        super(SetAlarmNotificationsAction, self).__init__(
            request, context, *args, **kwargs
        )
        try:
            notifications = ad_forms._get_notifications(request)
            self.fields['notifications'].choices = notifications
        except Exception:
            exceptions.handle(request,
                              _('Unable to retrieve notifications.'))


_SEVERITY_CHOICES = [("LOW", _("Low")),
                     ("MEDIUM", _("Medium")),
                     ("HIGH", _("High")),
                     ("CRITICAL", _("Critical"))]


class SetAlarmDefinitionAction(workflows.Action):
    name = forms.CharField(label=_('Name'),
                           required=True,
                           max_length=250,
                           help_text=_('An unique name of the alarm.'))

    description = forms.CharField(label=_('Description'),
                                  required=False,
                                  help_text=_('A description of an alarm.'))

    severity = forms.ChoiceField(label=_('Severity'),
                                 choices=_SEVERITY_CHOICES,
                                 initial=_SEVERITY_CHOICES[0],
                                 widget=forms.SelectWidget,
                                 required=False,
                                 help_text=_('Severity of an alarm. Must be '
                                             'either LOW, MEDIUM, HIGH '
                                             'or CRITICAL. Default is LOW.'))

    class Meta(object):
        name = _('Details')
        help_text_template = ("monitoring/alarmdefs/"
                              "_create_ad_details_help.html")

    def clean(self):
        cleaned_data = super(SetAlarmDefinitionAction, self).clean()

        alarm_def_name = cleaned_data.get('name', '').strip()
        if not alarm_def_name:
            return
        is_name_valid = self._is_alarm_def_name_unique_validator(
            alarm_def_name)
        if not is_name_valid:
            self.add_error('name',
                           _('Alarm definition with %s name already exists')
                           % alarm_def_name)

    def _is_alarm_def_name_unique_validator(self, value):
        try:
            ret = self._get_alarm_def_by_name(value)
            return not (ret and len(ret))
        except Exception:
            exceptions.handle(request=self.request,
                              message=_('Failed to validate name'),
                              ignore=True)
        return True

    @memoized.memoized_method
    def _get_alarm_def_by_name(self, value):
        return api.monitor.alarmdef_get_by_name(self.request, value)


class SetAlarmDefinitionExpressionAction(workflows.Action):
    expression = ad_forms.ExpressionField(label=_("Expression"),
                                          required=True,
                                          widget=ad_forms.ExpressionWidget(''),
                                          help_text=_(
                                              'An alarm expression.'))

    match_by = forms.CharField(label=_('Match by'),
                               required=False,
                               widget=ad_forms.MatchByWidget(''),
                               help_text=_('The metric dimensions used '
                                           'to create unique alarms.'))

    class Meta(object):
        name = _('Expression')
        help_text_template = ("monitoring/alarmdefs/"
                              "_create_ad_expression_help.html")

    def __init__(self, request, context, *args, **kwargs):
        super(SetAlarmDefinitionExpressionAction, self).__init__(request,
                                                                 context,
                                                                 *args,
                                                                 **kwargs)

        try:
            self.fields['expression'].metrics = ad_forms._get_metrics(request)
        except Exception:
            exceptions.handle(request, _('Unable to retrieve metrics'))


class SetDetailsStep(workflows.Step):
    action_class = SetAlarmDefinitionAction
    contributes = ('name', 'description', 'severity')
    template_name = 'monitoring/alarmdefs/workflow_step.html'


class SetExpressionStep(workflows.Step):
    action_class = SetAlarmDefinitionExpressionAction
    contributes = ('expression', 'match_by')
    template_name = 'monitoring/alarmdefs/workflow_step.html'

    def contribute(self, data, context):
        context = (super(SetExpressionStep, self)
                   .contribute(data, context))

        if 'expression' in data and data['expression']:
            context['expression'] = data['expression'].strip()

        if 'match_by' in data and data['match_by']:
            context['match_by'] = context['match_by'].split(',')
        else:
            context['match_by'] = []

        return context


class SetNotificationsStep(workflows.Step):
    action_class = SetAlarmNotificationsAction
    contributes = ('alarm_actions', 'ok_actions', 'undetermined_actions')
    template_name = 'monitoring/alarmdefs/workflow_step.html'


class AlarmDefinitionWorkflow(workflows.Workflow):
    slug = 'create_alarm_definition'
    name = _('Create Alarm Definition')
    finalize_button_name = _('Create Alarm Definition')
    success_message = _('Alarm definition %s has been created')
    failure_message = _('Unable to create alarm definition %s')
    success_url = constants.URL_PREFIX + 'index'
    wizard = True
    default_steps = (
        SetDetailsStep,
        SetExpressionStep,
        SetNotificationsStep
    )

    def format_status_message(self, message):
        name = self.context.get('name', _('Unknown name'))
        return message % name

    @sensitive_variables('alarm_actions',
                         'ok_actions',
                         'undetermined_actions')
    def handle(self, request, context):
        try:
            api.monitor.alarmdef_create(
                request,
                name=context['name'],
                expression=context['expression'],
                description=context['description'],
                severity=context['severity'],
                match_by=context['match_by'],
                alarm_actions=context['alarm_actions'],
                ok_actions=context['ok_actions'],
                undetermined_actions=context['undetermined_actions'],
            )
        except Exception:
            exceptions.handle(request,
                              _('Unable to create alarm definition.'),
                              escalate=True)
            return False

        return True
