# 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 jsonschema.exceptions as json_exc
import mock
import testtools

from sahara import conductor as cond
from sahara import context
from sahara import exceptions as ex
from sahara.plugins import base
from sahara.tests.unit import base as unit_base
from sahara.utils import api_validator

conductor = cond.API

EXPECTED_SCHEMA = {
    "type": "object",
    "additionalProperties": False,
    "properties": {
        "plugin_labels": {
            "type": "object",
            "additionalProperties": False,
            "properties": {
                "hidden": {
                    "type": "object",
                    "additionalProperties": False,
                    "properties": {
                        "status": {
                            "type": "boolean"
                        }
                    }
                },
                "stable": {
                    "type": "object",
                    "additionalProperties": False,
                    "properties": {
                        "status": {
                            "type": "boolean"
                        }
                    }
                },
                "enabled": {
                    "type": "object",
                    "additionalProperties": False,
                    "properties": {
                        "status": {
                            "type": "boolean"
                        }
                    }
                },
                "deprecated": {
                    "type": "object",
                    "additionalProperties": False,
                    "properties": {
                        "status": {
                            "type": "boolean"
                        }
                    }
                }
            }
        },
        "version_labels": {
            "type": "object",
            "additionalProperties": False,
            "properties": {
                "0.1": {
                    "type": "object",
                    "additionalProperties": False,
                    "properties": {
                        "hidden": {
                            "type": "object",
                            "additionalProperties": False,
                            "properties": {
                                "status": {
                                    "type": "boolean"
                                }
                            }
                        },
                        "stable": {
                            "type": "object",
                            "additionalProperties": False,
                            "properties": {
                                "status": {
                                    "type": "boolean"
                                }
                            }
                        },
                        "enabled": {
                            "type": "object",
                            "additionalProperties": False,
                            "properties": {
                                "status": {
                                    "type": "boolean"
                                }
                            }
                        },
                        "deprecated": {
                            "type": "object",
                            "additionalProperties": False,
                            "properties": {
                                "status": {
                                    "type": "boolean"
                                }
                            }
                        }
                    }
                }
            }
        },
    }
}


class TestPluginLabels(unit_base.SaharaWithDbTestCase):
    def test_validate_default_labels_load(self):
        all_plugins = ['cdh', 'ambari', 'fake', 'storm', 'mapr', 'spark',
                       'vanilla']
        self.override_config('plugins', all_plugins)
        manager = base.PluginManager()
        for plugin in all_plugins:
            data = manager.label_handler.get_label_details(plugin)
            self.assertIsNotNone(data)
            # order doesn't play a role
            self.assertIsNotNone(data['plugin_labels'])
            self.assertEqual(
                sorted(list(manager.get_plugin(plugin).get_versions())),
                sorted(list(data.get('version_labels').keys())))

    def test_get_label_full_details(self):
        self.override_config('plugins', ['fake'])
        lh = base.PluginManager().label_handler

        result = lh.get_label_full_details('fake')
        self.assertIsNotNone(result.get('plugin_labels'))
        self.assertIsNotNone(result.get('version_labels'))
        pl = result.get('plugin_labels')

        self.assertEqual(
            ['enabled', 'hidden'],
            sorted(list(pl.keys()))
        )
        for lb in ['hidden', 'enabled']:
            self.assertEqual(
                ['description', 'mutable', 'status'],
                sorted(list(pl[lb]))
            )
        vl = result.get('version_labels')
        self.assertEqual(['0.1'], list(vl.keys()))
        vl = vl.get('0.1')

        self.assertEqual(
            ['enabled'], list(vl.keys()))

        self.assertEqual(
            ['description', 'mutable', 'status'],
            sorted(list(vl['enabled']))
        )

    def test_validate_plugin_update(self):
        def validate(plugin_name, values, validator, lh):
            validator.validate(values)
            lh.validate_plugin_update(plugin_name, values)

        values = {'plugin_labels': {'enabled': {'status': False}}}
        self.override_config('plugins', ['fake', 'spark'])
        lh = base.PluginManager()
        validator = api_validator.ApiValidator(
            lh.get_plugin_update_validation_jsonschema())
        validate('fake', values, validator, lh)
        values = {'plugin_labels': {'not_exists': {'status': False}}}

        with testtools.ExpectedException(json_exc.ValidationError):
            validate('fake', values, validator, lh)

        values = {'plugin_labels': {'enabled': {'status': 'False'}}}
        with testtools.ExpectedException(json_exc.ValidationError):
            validate('fake', values, validator, lh)

        values = {'field': {'blala': 'blalalalalala'}}

        with testtools.ExpectedException(json_exc.ValidationError):
            validate('fake', values, validator, lh)

        values = {'plugin_labels': {'hidden': {'status': True}}}

        with testtools.ExpectedException(ex.InvalidDataException):
            # valid under schema, but not valid under validator
            # hidden is not available to spark
            validate('spark', values, validator, lh)

        values = {'plugin_labels': {'enabled': {'mutable': False}}}
        with testtools.ExpectedException(json_exc.ValidationError):
            validate('spark', values, validator, lh)

        values = {'version_labels': {'enabled': {'status': False}}}
        with testtools.ExpectedException(json_exc.ValidationError):
            validate('spark', values, validator, lh)

        values = {'version_labels': {'0.1': {'enabled': {'status': False}}}}
        validate('fake', values, validator, lh)

        values = {'version_labels': {'0.1': {'enabled': {'status': False}}}}
        with testtools.ExpectedException(ex.InvalidDataException):
            validate('spark', values, validator, lh)

        values = {'version_labels': {'0.1': {'hidden': {'status': True}}}}
        with testtools.ExpectedException(ex.InvalidDataException):
            validate('fake', values, validator, lh)

    def test_jsonschema(self):
        self.override_config('plugins', ['fake'])
        lh = base.PluginManager()
        schema = lh.get_plugin_update_validation_jsonschema()
        self.assertEqual(EXPECTED_SCHEMA, schema)

    def test_update(self):
        self.override_config('plugins', ['fake'])
        lh = base.PluginManager()

        data = lh.update_plugin('fake', values={
            'plugin_labels': {'enabled': {'status': False}}}).dict

        # enabled is updated, but hidden still same
        self.assertFalse(data['plugin_labels']['enabled']['status'])
        self.assertTrue(data['plugin_labels']['hidden']['status'])

        data = lh.update_plugin('fake', values={
            'version_labels': {'0.1': {'enabled': {'status': False}}}}).dict

        self.assertFalse(data['plugin_labels']['enabled']['status'])
        self.assertTrue(data['plugin_labels']['hidden']['status'])
        self.assertFalse(data['version_labels']['0.1']['enabled']['status'])

    @mock.patch('sahara.plugins.labels.LOG.warning')
    def test_validate_plugin_labels(self, logger):
        self.override_config('plugins', ['fake'])
        lh = base.PluginManager()

        lh.validate_plugin_labels('fake', '0.1')
        self.assertEqual(0, logger.call_count)

        dct = {
            'name': 'fake',
            'version_labels': {
                '0.1': {
                    'deprecated': {'status': True},
                    'enabled': {'status': True}
                }
            },
            'plugin_labels': {
                'deprecated': {'status': True},
                'enabled': {'status': True}
            }
        }

        conductor.plugin_create(context.ctx(), dct)
        lh.validate_plugin_labels('fake', '0.1')
        self.assertEqual(2, logger.call_count)

        conductor.plugin_remove(context.ctx(), 'fake')
        dct['plugin_labels']['enabled']['status'] = False
        conductor.plugin_create(context.ctx(), dct)
        with testtools.ExpectedException(ex.InvalidReferenceException):
            lh.validate_plugin_labels('fake', '0.1')

        conductor.plugin_remove(context.ctx(), 'fake')
        dct['plugin_labels']['enabled']['status'] = True
        dct['version_labels']['0.1']['enabled']['status'] = False
        conductor.plugin_create(context.ctx(), dct)
        with testtools.ExpectedException(ex.InvalidReferenceException):
            lh.validate_plugin_labels('fake', '0.1')
