#
#    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 mock
from oslo_utils import importutils
import testtools

from heat.common import exception
from heat.common import template_format
from heat.engine import resources
from heat.engine.resources.openstack.mistral import cron_trigger
from heat.engine import scheduler
from heat.engine import stack as stack_parser
from heat.engine import template
from heat.tests import common
from heat.tests import utils

mistral_client = importutils.try_import('mistralclient.api.base')

stack_template = '''
heat_template_version: 2013-05-23

resources:
  cron_trigger:
    type: OS::Mistral::CronTrigger
    properties:
      name: my_cron_trigger
      pattern: "* * 0 * *"
      workflow: {'name': 'get_first_glance_image', 'input': {} }
      count: 3
      first_time: "2015-04-08 06:20"
'''


class FakeCronTrigger(object):

    def __init__(self, name):
        self.name = name
        self.next_execution_time = '2015-03-01 00:00:00'
        self.remaining_executions = 3


class MistralCronTriggerTest(common.HeatTestCase):

    def setUp(self):
        super(MistralCronTriggerTest, self).setUp()
        resources.initialise()
        utils.setup_dummy_db()
        self.ctx = utils.dummy_context()

        t = template_format.parse(stack_template)
        self.stack = utils.parse_stack(t)
        resource_defns = self.stack.t.resource_definitions(self.stack)
        self.rsrc_defn = resource_defns['cron_trigger']

        self.client = mock.Mock()
        self.patchobject(cron_trigger.CronTrigger, 'client',
                         return_value=self.client)

    def _create_resource(self, name, snippet, stack):
        ct = cron_trigger.CronTrigger(name, snippet, stack)
        self.client.cron_triggers.create.return_value = FakeCronTrigger(
            'my_cron_trigger')
        self.client.cron_triggers.get.return_value = FakeCronTrigger(
            'my_cron_trigger')
        scheduler.TaskRunner(ct.create)()
        args = self.client.cron_triggers.create.call_args[1]
        self.assertEqual('* * 0 * *', args['pattern'])
        self.assertEqual('get_first_glance_image', args['workflow_name'])
        self.assertEqual({}, args['workflow_input'])
        self.assertEqual('2015-04-08 06:20', args['first_time'])
        self.assertEqual(3, args['count'])
        self.assertEqual('my_cron_trigger', ct.resource_id)
        return ct

    def test_create(self):
        ct = self._create_resource('trigger', self.rsrc_defn, self.stack)
        expected_state = (ct.CREATE, ct.COMPLETE)
        self.assertEqual(expected_state, ct.state)

    def test_resource_mapping(self):
        mapping = cron_trigger.resource_mapping()
        self.assertEqual(1, len(mapping))
        self.assertEqual(cron_trigger.CronTrigger,
                         mapping['OS::Mistral::CronTrigger'])

    def test_attributes(self):
        ct = self._create_resource('trigger', self.rsrc_defn, self.stack)
        self.assertEqual('2015-03-01 00:00:00',
                         ct.FnGetAtt('next_execution_time'))
        self.assertEqual(3, ct.FnGetAtt('remaining_executions'))

    def test_delete(self):
        ct = self._create_resource('trigger', self.rsrc_defn, self.stack)
        scheduler.TaskRunner(ct.delete)()
        self.assertEqual((ct.DELETE, ct.COMPLETE), ct.state)
        self.client.cron_triggers.delete.assert_called_once_with(
            ct.resource_id)

    def test_delete_not_found(self):
        ct = self._create_resource('trigger', self.rsrc_defn, self.stack)
        self.client.cron_triggers.delete.side_effect = (
            self.client.mistral_base.APIException(error_code=404))
        scheduler.TaskRunner(ct.delete)()
        self.assertEqual((ct.DELETE, ct.COMPLETE), ct.state)
        self.client.cron_triggers.delete.assert_called_once_with(
            ct.resource_id)

    @testtools.skipIf(mistral_client is not None,
                      'Tests mistral client not installed')
    def test_no_client(self):
        tmpl = template.Template((template_format.parse(stack_template)))
        stack = stack_parser.Stack(utils.dummy_context(), 'foo', tmpl)
        self.assertRaises(exception.ResourceTypeNotFound, stack.validate)
