# -*- coding: utf-8; -*-

from unittest.mock import patch

import colander
from pyramid import testing

from sqlalchemy import orm

from wuttjamaican.conf import WuttaConfig
from wuttaweb.forms import schema as mod
from tests.util import DataTestCase


class TestObjectNode(DataTestCase):

    def setUp(self):
        self.setup_db()
        self.request = testing.DummyRequest(wutta_config=self.config)

    def test_dictify(self):
        model = self.app.model
        person = model.Person(full_name="Betty Boop")

        # unsupported type is converted to string
        node = mod.ObjectNode(colander.String())
        value = node.dictify(person)
        self.assertEqual(value, "Betty Boop")

        # but supported type can dictify
        node = mod.ObjectNode(mod.PersonRef(self.request))
        value = node.dictify(person)
        self.assertIs(value, person)

    def test_objectify(self):
        model = self.app.model
        person = model.Person(full_name="Betty Boop")

        # unsupported type raises error
        node = mod.ObjectNode(colander.String())
        self.assertRaises(NotImplementedError, node.objectify, person)

        # but supported type can objectify
        node = mod.ObjectNode(mod.PersonRef(self.request))
        value = node.objectify(person)
        self.assertIs(value, person)


class TestObjectRef(DataTestCase):

    def setUp(self):
        self.setup_db()
        self.request = testing.DummyRequest(wutta_config=self.config)

    def test_empty_option(self):

        # null by default
        typ = mod.ObjectRef(self.request)
        self.assertIsNone(typ.empty_option)

        # passing true yields default empty option
        typ = mod.ObjectRef(self.request, empty_option=True)
        self.assertEqual(typ.empty_option, ('', "(none)"))

        # can set explicitly
        typ = mod.ObjectRef(self.request, empty_option=('foo', 'bar'))
        self.assertEqual(typ.empty_option, ('foo', 'bar'))

        # can set just a label
        typ = mod.ObjectRef(self.request, empty_option="(empty)")
        self.assertEqual(typ.empty_option, ('', "(empty)"))

    def test_model_class(self):
        typ = mod.ObjectRef(self.request)
        self.assertRaises(NotImplementedError, getattr, typ, 'model_class')

    def test_serialize(self):
        model = self.app.model
        node = colander.SchemaNode(colander.String())

        # null
        typ = mod.ObjectRef(self.request)
        value = typ.serialize(node, colander.null)
        self.assertIs(value, colander.null)

        # model instance
        person = model.Person(full_name="Betty Boop")
        self.session.add(person)
        self.session.commit()
        self.assertIsNotNone(person.uuid)
        typ = mod.ObjectRef(self.request)
        value = typ.serialize(node, person)
        self.assertEqual(value, person.uuid)

    def test_deserialize(self):
        model = self.app.model
        node = colander.SchemaNode(colander.String())

        # null
        typ = mod.ObjectRef(self.request)
        value = typ.deserialize(node, colander.null)
        self.assertIs(value, colander.null)

        # model instance
        person = model.Person(full_name="Betty Boop")
        self.session.add(person)
        self.session.commit()
        self.assertIsNotNone(person.uuid)
        with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
            typ = mod.ObjectRef(self.request, session=self.session)
            value = typ.deserialize(node, person.uuid)
            self.assertIs(value, person)

    def test_dictify(self):
        model = self.app.model
        node = colander.SchemaNode(colander.String())

        # model instance
        person = model.Person(full_name="Betty Boop")
        self.session.add(person)
        self.session.commit()
        self.assertIsNotNone(person.uuid)
        typ = mod.ObjectRef(self.request)
        value = typ.dictify(person)
        self.assertIs(value, person)

    def test_objectify(self):
        model = self.app.model
        node = colander.SchemaNode(colander.String())

        # null
        typ = mod.ObjectRef(self.request)
        value = typ.objectify(None)
        self.assertIsNone(value)

        # model instance
        person = model.Person(full_name="Betty Boop")
        self.session.add(person)
        self.session.commit()
        self.assertIsNotNone(person.uuid)
        with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
            typ = mod.ObjectRef(self.request, session=self.session)
            value = typ.objectify(person.uuid)
            self.assertIs(value, person)

        # error if not found
        with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
            typ = mod.ObjectRef(self.request, session=self.session)
            self.assertRaises(ValueError, typ.objectify, 'WRONG-UUID')

    def test_get_query(self):
        model = self.app.model
        with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
            typ = mod.ObjectRef(self.request, session=self.session)
            query = typ.get_query()
            self.assertIsInstance(query, orm.Query)

    def test_sort_query(self):
        model = self.app.model
        with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
            typ = mod.ObjectRef(self.request, session=self.session)
            query = typ.get_query()
            sorted_query = typ.sort_query(query)
            self.assertIs(sorted_query, query)

    def test_widget_maker(self):
        model = self.app.model
        person = model.Person(full_name="Betty Boop")
        self.session.add(person)
        self.session.commit()

        # basic
        with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
            typ = mod.ObjectRef(self.request, session=self.session)
            widget = typ.widget_maker()
            self.assertEqual(len(widget.values), 1)
            self.assertEqual(widget.values[0][1], "Betty Boop")

        # empty option
        with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
            typ = mod.ObjectRef(self.request, session=self.session, empty_option=True)
            widget = typ.widget_maker()
            self.assertEqual(len(widget.values), 2)
            self.assertEqual(widget.values[0][1], "(none)")
            self.assertEqual(widget.values[1][1], "Betty Boop")


class TestPersonRef(DataTestCase):

    def setUp(self):
        self.setup_db()
        self.request = testing.DummyRequest(wutta_config=self.config)

    def test_sort_query(self):
        typ = mod.PersonRef(self.request, session=self.session)
        query = typ.get_query()
        self.assertIsInstance(query, orm.Query)
        sorted_query = typ.sort_query(query)
        self.assertIsInstance(sorted_query, orm.Query)
        self.assertIsNot(sorted_query, query)


class TestRoleRefs(DataTestCase):

    def setUp(self):
        self.setup_db()
        self.request = testing.DummyRequest(wutta_config=self.config)

    def test_widget_maker(self):
        model = self.app.model
        auth = self.app.get_auth_handler()
        admin = auth.get_role_administrator(self.session)
        authed = auth.get_role_authenticated(self.session)
        anon = auth.get_role_anonymous(self.session)
        blokes = model.Role(name="Blokes")
        self.session.add(blokes)
        self.session.commit()

        # default values for widget includes all but: authed, anon
        typ = mod.RoleRefs(self.request, session=self.session)
        widget = typ.widget_maker()
        self.assertEqual(len(widget.values), 2)
        self.assertEqual(widget.values[0][1], "Administrator")
        self.assertEqual(widget.values[1][1], "Blokes")


class TestPermissions(DataTestCase):

    def setUp(self):
        self.setup_db()
        self.request = testing.DummyRequest(wutta_config=self.config)

    def test_widget_maker(self):

        # no supported permissions
        permissions = {}
        typ = mod.Permissions(self.request, permissions)
        widget = typ.widget_maker()
        self.assertEqual(len(widget.values), 0)

        # supported permissions are morphed to values
        permissions = {
            'widgets': {
                'label': "Widgets",
                'perms': {
                    'widgets.polish': {
                        'label': "Polish the widgets",
                    },
                },
            },
        }
        typ = mod.Permissions(self.request, permissions)
        widget = typ.widget_maker()
        self.assertEqual(len(widget.values), 1)
        self.assertEqual(widget.values[0], ('widgets.polish', "Polish the widgets"))
