#!/usr/bin/env python
# -*- encoding: utf-8 -*-

# Copyright 2011-2019, Nigel Small
#
# 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 unittest import TestCase

from neotime import Date, Time, DateTime, Duration

from py2neo.data import Node
from py2neo.cypher import cypher_escape, cypher_repr
from py2neo.cypher.encoding import LabelSetView, PropertyDictView, PropertySelector


class LabelSetViewTestCase(TestCase):

    def test_can_create_empty_view(self):
        view = LabelSetView([])
        self.assertEqual(repr(view), "")

    def test_can_create_single_label_view(self):
        view = LabelSetView(["A"])
        self.assertEqual(repr(view), ":A")

    def test_can_create_double_label_view(self):
        view = LabelSetView(["A", "B"])
        self.assertEqual(repr(view), ":A:B")

    def test_can_select_existing_in_view(self):
        view = LabelSetView(["A", "B"]).B
        self.assertEqual(repr(view), ":B")

    def test_can_select_non_existing_in_view(self):
        view = LabelSetView(["A", "B"]).C
        self.assertEqual(repr(view), "")

    def test_can_chain_select(self):
        view = LabelSetView(["A", "B", "C"]).B.C
        self.assertEqual(repr(view), ":B:C")

    def test_can_reselect_same(self):
        view = LabelSetView(["A", "B", "C"]).B.B.C
        self.assertEqual(repr(view), ":B:C")

    def test_length(self):
        view = LabelSetView(["A", "B", "C"])
        self.assertEqual(len(view), 3)

    def test_iterable(self):
        view = LabelSetView(["A", "B", "C"])
        self.assertSetEqual(set(view), {"A", "B", "C"})

    def test_containment(self):
        view = LabelSetView(["A", "B", "C"])
        self.assertIn("A", view)

    def test_non_containment(self):
        view = LabelSetView(["A", "B", "C"])
        self.assertNotIn("D", view)


class PropertyDictViewTestCase(TestCase):

    def test_can_create_empty_view(self):
        view = PropertyDictView({})
        self.assertEqual(repr(view), "{}")

    def test_can_create_single_property_view(self):
        view = PropertyDictView({"A": 1})
        self.assertEqual(repr(view), "{A: 1}")

    def test_can_create_double_property_view(self):
        view = PropertyDictView({"A": 1, "B": 2})
        self.assertEqual(repr(view), "{A: 1, B: 2}")

    def test_can_select_existing_in_view(self):
        view = PropertyDictView({"A": 1, "B": 2}).B
        self.assertEqual(repr(view), "{B: 2}")

    def test_can_select_non_existing_in_view(self):
        view = PropertyDictView({"A": 1, "B": 2}).C
        self.assertEqual(repr(view), "{}")

    def test_can_chain_select(self):
        view = PropertyDictView({"A": 1, "B": 2, "C": 3}).B.C
        self.assertEqual(repr(view), "{B: 2, C: 3}")

    def test_can_reselect_same(self):
        view = PropertyDictView({"A": 1, "B": 2, "C": 3}).B.B.C
        self.assertEqual(repr(view), "{B: 2, C: 3}")

    def test_length(self):
        view = PropertyDictView({"A": 1, "B": 2, "C": 3})
        self.assertEqual(len(view), 3)

    def test_iterable(self):
        view = PropertyDictView({"A": 1, "B": 2, "C": 3})
        self.assertEqual(set(view), {"A", "B", "C"})

    def test_containment(self):
        view = PropertyDictView({"A": 1, "B": 2, "C": 3})
        self.assertIn("A", view)

    def test_non_containment(self):
        view = PropertyDictView({"A": 1, "B": 2, "C": 3})
        self.assertNotIn("D", view)


class PropertySelectorTestCase(TestCase):

    def test_simple(self):
        selector = PropertySelector({"A": 1, "B": 2, "C": 3})
        self.assertEqual(selector.A, "1")

    def test_non_existent(self):
        selector = PropertySelector({"A": 1, "B": 2, "C": 3})
        self.assertEqual(selector.D, "null")


class NodeReprTestCase(TestCase):

    def test_empty(self):
        a = Node()
        r = cypher_repr(a)
        self.assertEqual("({})", r)

    def test_single_property(self):
        a = Node(name="Alice")
        r = cypher_repr(a)
        self.assertEqual("({name: 'Alice'})", r)

    def test_property_and_label(self):
        a = Node("Person", name="Alice")
        r = cypher_repr(a)
        self.assertEqual("(:Person {name: 'Alice'})", r)

    def test_date_property(self):
        a = Node(d=Date(1970, 1, 1))
        r = cypher_repr(a)
        self.assertEqual("({d: date('1970-01-01')})", r)

    def test_time_property(self):
        a = Node(t=Time(12, 34, 56))
        r = cypher_repr(a)
        self.assertEqual("({t: time('12:34:56.000000000')})", r)

    def test_datetime_property(self):
        a = Node(dt=DateTime(1970, 1, 1, 12, 34, 56))
        r = cypher_repr(a)
        self.assertEqual("({dt: datetime('1970-01-01T12:34:56.000000000')})", r)

    def test_duration_property(self):
        a = Node(dur=Duration(days=3))
        r = cypher_repr(a)
        self.assertEqual("({dur: duration('P3D')})", r)


class CypherEscapeTestCase(TestCase):

    def test_empty_string(self):
        value = ""
        with self.assertRaises(ValueError):
            _ = cypher_escape(value)

    def test_simple_string(self):
        value = "foo"
        escaped = "foo"
        self.assertEqual(escaped, cypher_escape(value))

    def test_string_with_space(self):
        value = "foo bar"
        escaped = "`foo bar`"
        self.assertEqual(escaped, cypher_escape(value))

    def test_string_with_backtick(self):
        value = "foo `bar`"
        escaped = "`foo ``bar```"
        self.assertEqual(escaped, cypher_escape(value))
