from tornado.testing import AsyncTestCase, gen_test
from tornado.util import ObjectDict
from unittest.mock import patch
import json
from datetime import datetime
from schema.thing import Thing, Data
from objects.table import Record, Table
from tests.base import AsyncMock
from motorengine.queryset import QuerySet
from motorengine import connect


class TestRecord(AsyncTestCase):

    def setUp(self):
        connect("dt", host="mongodb://localhost:27017/dt")
        super(TestRecord, self).setUp()

    def tearDown(self):
        super(TestRecord, self).tearDown()

    def test_record_creation(self):
        r = Record("News", 1, {"name": "CNNIBN"})
        self.assertIsNotNone(r)
        self.assertEqual(r.name, "CNNIBN")
        self.assertEqual(r._id, 1)

    @patch.object(Thing, 'add', new_callable=AsyncMock)
    @patch.object(Data, 'add', new_callable=AsyncMock)
    @gen_test
    def test_record_put_add(self, add_mock, tng_mock):
        r = Record("News", None, {"name": "CNNIBN"})
        yield r.put()
        self.assertTrue(add_mock.called)
        self.assertTrue(tng_mock.called)

    @patch.object(Data, 'update', new_callable=AsyncMock)
    @gen_test
    def test_record_put_update(self, update_mock):
        r = Record("News", 1, {"name": "CNNIBN"})
        yield r.put()
        self.assertTrue(update_mock.called)


class TestTable(AsyncTestCase):

    def setUp(self):
        connect("dt", host="mongodb://localhost:27017/dt")
        super(TestTable, self).setUp()

    def tearDown(self):
        super(TestTable, self).tearDown()

    def test_table_instation(self):
        t = Table("Cache", ['key', 'value'])
        self.assertIsNotNone(t)

    @patch.object(Table, '_fetch_data', new_callable=AsyncMock)
    @gen_test
    def test_get(self, call_mock):
        t = Table("Cache", ['key', 'value'])
        yield t.get(1)
        self.assertTrue(call_mock.called)

    @patch.object(QuerySet, 'find_all', new_callable=AsyncMock)
    @gen_test
    def test_fetch_thing(self, find_mock):
        t = Table("Cache", ["key", "value"])
        yield t._fetch_thing(1)
        self.assertTrue(find_mock.called)

    @patch.object(Table, '_fetch_thing', new_callable=AsyncMock)
    @patch.object(QuerySet, 'find_all', new_callable=AsyncMock)
    @gen_test
    def test_fetch_data(self, find_mock, fetch_mock):

        fetch_mock.return_value = [ObjectDict(name="Cache",
                                              thing_id=1,
                                              reads=0)]
        find_mock.return_value = [ObjectDict(thing_id=1,
                                             key="Key",
                                             value="Value"),
                                  ObjectDict(thing_id=1,
                                             key="Key1",
                                             value="Value2")]
        t = Table("Cache", ["key", "value"])
        result = yield t._fetch_data(1)
        self.assertTrue(find_mock.called)
        self.assertEqual(result, dict(id=1, Key="Value", Key1="Value2"))

    @patch.object(Record, 'put', new_callable=AsyncMock)
    @gen_test
    def test_new(self, rec_mock):
        t = Table("Cache", ["key", "value"])
        result = yield t.new(dict(key="k", value="v"))
        self.assertIsNotNone(result)

    @patch.object(Record, 'put', new_callable=AsyncMock)
    @gen_test
    def test_update(self, rec_mock):
        t = Table("Cache", ["key", "value"])
        result = yield t.update(1, dict(key="k", value="v"))
        self.assertIsNotNone(result)

    @patch.object(Table, '_fetch_table', new_callable=AsyncMock)
    @patch.object(Table, '_fetch_data', new_callable=AsyncMock)
    @gen_test
    def test_list(self, data_mock, fetch_mock):
        t = Table("Cache", ["key", "value"])
        fetch_mock.return_value = ([ObjectDict(thing_id=1),
                                    ObjectDict(thing_id=2)], 0, 0)
        result = yield t.list()
        self.assertIsNotNone(result)

    @patch.object(Table, '_search_table', new_callable=AsyncMock)
    @gen_test
    def test_search(self, search_mock):
        t = Table("Cache", ["key", "value"])
        search_mock.return_value = ([ObjectDict(thing_id=1),
                                    ObjectDict(thing_id=2)], 0, 0)
        result = yield t.search("hello")
        self.assertIsNotNone(result)

    @patch.object(QuerySet, 'find_all', new_callable=AsyncMock)
    @gen_test
    def test_search_table(self, find_mock):

        find_mock.return_value = [ObjectDict(thing_id=1,
                                             key="Key",
                                             value="Value"),
                                  ObjectDict(thing_id=1,
                                             key="Key1",
                                             value="Value2")]
        t = Table("Cache", ["key", "value"])
        result = yield t._search_table("hello")
        self.assertTrue(find_mock.called)
        self.assertIsNotNone(result)
