#
#    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 heat.common.i18n import _
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine import support


COMMON_PROPERTIES = (
    ALARM_ACTIONS, OK_ACTIONS, REPEAT_ACTIONS,
    INSUFFICIENT_DATA_ACTIONS, DESCRIPTION, ENABLED, TIME_CONSTRAINTS,
    SEVERITY,
) = (
    'alarm_actions', 'ok_actions', 'repeat_actions',
    'insufficient_data_actions', 'description', 'enabled', 'time_constraints',
    'severity',
)

_TIME_CONSTRAINT_KEYS = (
    NAME, START, DURATION, TIMEZONE, TIME_CONSTRAINT_DESCRIPTION,
) = (
    'name', 'start', 'duration', 'timezone', 'description',
)

common_properties_schema = {
    DESCRIPTION: properties.Schema(
        properties.Schema.STRING,
        _('Description for the alarm.'),
        update_allowed=True
    ),
    ENABLED: properties.Schema(
        properties.Schema.BOOLEAN,
        _('True if alarm evaluation/actioning is enabled.'),
        default='true',
        update_allowed=True
    ),
    ALARM_ACTIONS: properties.Schema(
        properties.Schema.LIST,
        _('A list of URLs (webhooks) to invoke when state transitions to '
          'alarm.'),
        update_allowed=True
    ),
    OK_ACTIONS: properties.Schema(
        properties.Schema.LIST,
        _('A list of URLs (webhooks) to invoke when state transitions to '
          'ok.'),
        update_allowed=True
    ),
    INSUFFICIENT_DATA_ACTIONS: properties.Schema(
        properties.Schema.LIST,
        _('A list of URLs (webhooks) to invoke when state transitions to '
          'insufficient-data.'),
        update_allowed=True
    ),
    REPEAT_ACTIONS: properties.Schema(
        properties.Schema.BOOLEAN,
        _("False to trigger actions when the threshold is reached AND "
          "the alarm's state has changed. By default, actions are called "
          "each time the threshold is reached."),
        default='true',
        update_allowed=True
    ),
    SEVERITY: properties.Schema(
        properties.Schema.STRING,
        _('Severity of the alarm.'),
        default='low',
        constraints=[
            constraints.AllowedValues(['low', 'moderate', 'critical'])
        ],
        update_allowed=True,
        support_status=support.SupportStatus(version='5.0.0'),
    ),
    TIME_CONSTRAINTS: properties.Schema(
        properties.Schema.LIST,
        _('Describe time constraints for the alarm. '
          'Only evaluate the alarm if the time at evaluation '
          'is within this time constraint. Start point(s) of '
          'the constraint are specified with a cron expression, '
          'whereas its duration is given in seconds.'
          ),
        schema=properties.Schema(
            properties.Schema.MAP,
            schema={
                NAME: properties.Schema(
                    properties.Schema.STRING,
                    _("Name for the time constraint."),
                    required=True
                ),
                START: properties.Schema(
                    properties.Schema.STRING,
                    _("Start time for the time constraint. "
                      "A CRON expression property."),
                    constraints=[
                        constraints.CustomConstraint(
                            'cron_expression')
                    ],
                    required=True
                ),
                TIME_CONSTRAINT_DESCRIPTION: properties.Schema(
                    properties.Schema.STRING,
                    _("Description for the time constraint."),
                ),
                DURATION: properties.Schema(
                    properties.Schema.INTEGER,
                    _("Duration for the time constraint."),
                    constraints=[
                        constraints.Range(min=0)
                    ],
                    required=True
                ),
                TIMEZONE: properties.Schema(
                    properties.Schema.STRING,
                    _("Timezone for the time constraint "
                      "(eg. 'Taiwan/Taipei', 'Europe/Amsterdam')."),
                    constraints=[
                        constraints.CustomConstraint('timezone')
                    ],
                )
            }

        ),
        support_status=support.SupportStatus(version='5.0.0'),
        default=[],
    )
}


NOVA_METERS = ['instance', 'memory', 'memory.usage',
               'cpu', 'cpu_util', 'vcpus',
               'disk.read.requests', 'disk.read.requests.rate',
               'disk.write.requests', 'disk.write.requests.rate',
               'disk.read.bytes', 'disk.read.bytes.rate',
               'disk.write.bytes', 'disk.write.bytes.rate',
               'disk.device.read.requests', 'disk.device.read.requests.rate',
               'disk.device.write.requests', 'disk.device.write.requests.rate',
               'disk.device.read.bytes', 'disk.device.read.bytes.rate',
               'disk.device.write.bytes', 'disk.device.write.bytes.rate',
               'disk.root.size', 'disk.ephemeral.size',
               'network.incoming.bytes', 'network.incoming.bytes.rate',
               'network.outgoing.bytes', 'network.outgoing.bytes.rate',
               'network.incoming.packets', 'network.incoming.packets.rate',
               'network.outgoing.packets', 'network.outgoing.packets.rate']


class BaseAlarm(resource.Resource):
    """Base Alarm Manager."""

    default_client_name = 'aodh'

    entity = 'alarm'

    alarm_type = 'threshold'

    def actions_to_urls(self, props):
        kwargs = {}
        for k, v in iter(props.items()):
            if k in [ALARM_ACTIONS, OK_ACTIONS,
                     INSUFFICIENT_DATA_ACTIONS] and v is not None:
                kwargs[k] = []
                for act in v:
                    # if the action is a resource name
                    # we ask the destination resource for an alarm url.
                    # the template writer should really do this in the
                    # template if possible with:
                    # {Fn::GetAtt: ['MyAction', 'AlarmUrl']}
                    if act in self.stack:
                        url = self.stack[act].FnGetAtt('AlarmUrl')
                        kwargs[k].append(url)
                    else:
                        if act:
                            kwargs[k].append(act)
            else:
                kwargs[k] = v
        return kwargs

    def _reformat_properties(self, props):
        rule = {}
        for name in self.PROPERTIES:
            value = props.pop(name, None)
            if value:
                rule[name] = value
        if rule:
            props['%s_rule' % self.alarm_type] = rule
        return props

    def handle_suspend(self):
        if self.resource_id is not None:
            alarm_update = {'enabled': False}
            self.client().alarm.update(self.resource_id,
                                       alarm_update)

    def handle_resume(self):
        if self.resource_id is not None:
            alarm_update = {'enabled': True}
            self.client().alarm.update(self.resource_id,
                                       alarm_update)

    def handle_check(self):
        self.client().alarm.get(self.resource_id)
