# Copyright 2019 Extreme Networks, 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 orquesta.expressions import yql as yaql_expr
from orquesta.tests.unit import base as test_base
from orquesta.utils import plugin as plugin_util


class YAQLEvaluationTest(test_base.ExpressionEvaluatorTest):
    @classmethod
    def setUpClass(cls):
        cls.language = "yaql"
        super(YAQLEvaluationTest, cls).setUpClass()

    def test_get_evaluator(self):
        e = plugin_util.get_module("orquesta.expressions.evaluators", self.language)

        self.assertEqual(e, yaql_expr.YAQLEvaluator)
        self.assertIn("ctx", e._custom_functions.keys())

    def test_basic_eval(self):
        expr = "<% ctx().foo %>"

        data = {"foo": "bar"}

        self.assertEqual("bar", self.evaluator.evaluate(expr, data))

    def test_basic_eval_undefined(self):
        expr = "<% ctx().foo %>"

        data = {}

        with self.assertRaises(yaql_expr.YaqlEvaluationException) as raised:
            self.evaluator.evaluate(expr, data)

        expected = "Unable to resolve key 'foo' in expression '<% ctx().foo %>' from context."
        self.assertEqual(str(raised.exception), expected)

    def test_nested_eval(self):
        expr = "<% ctx(ctx().foo) %>"

        data = {"foo": "bar", "bar": "foobar"}

        self.assertEqual("foobar", self.evaluator.evaluate(expr, data))

    def test_dict_eval(self):
        expr = "<% ctx().nested.foo %>"

        data = {"nested": {"foo": "bar"}}

        self.assertEqual("bar", self.evaluator.evaluate(expr, data))

    def test_multi_eval(self):
        expr = "<% ctx().foo %> and <% ctx().marco %>"

        data = {"foo": "bar", "marco": "polo"}

        self.assertEqual("bar and polo", self.evaluator.evaluate(expr, data))

    def test_eval_recursive(self):
        expr = "<% ctx().fee %>"

        data = {
            "fee": "<% ctx().fi %>",
            "fi": "<% ctx().fo %>",
            "fo": "<% ctx().fum %>",
            "fum": "fee-fi-fo-fum",
        }

        self.assertEqual("fee-fi-fo-fum", self.evaluator.evaluate(expr, data))

    def test_multi_eval_recursive(self):
        expr = "<% ctx().fee %> <% ctx().im %>"

        data = {
            "fee": "<% ctx().fi %>",
            "fi": "<% ctx().fo %>",
            "fo": "<% ctx().fum %>",
            "fum": "fee-fi-fo-fum!",
            "im": "<% ctx().hungry %>",
            "hungry": "i'm hungry!",
        }

        self.assertEqual("fee-fi-fo-fum! i'm hungry!", self.evaluator.evaluate(expr, data))

    def test_type_preservation(self):
        data = {"k1": 101, "k2": 1.999, "k3": True, "k4": [1, 2], "k5": {"k": "v"}, "k6": None}

        self.assertEqual(data["k1"], self.evaluator.evaluate("<% ctx().k1 %>", data))

        self.assertEqual(data["k2"], self.evaluator.evaluate("<% ctx().k2 %>", data))

        self.assertTrue(self.evaluator.evaluate("<% ctx().k3 %>", data))

        self.assertListEqual(data["k4"], self.evaluator.evaluate("<% ctx().k4 %>", data))

        self.assertDictEqual(data["k5"], self.evaluator.evaluate("<% ctx().k5 %>", data))

        self.assertIsNone(self.evaluator.evaluate("<% ctx().k6 %>", data))

    def test_type_string_detection(self):
        expr = "<% ctx().foo %> -> <% ctx().bar %>"

        data = {"foo": 101, "bar": 201}

        self.assertEqual("101 -> 201", self.evaluator.evaluate(expr, data))
