import unittest
import unittest.mock

import os
from klon_core.data import HubDatabase
from klon_core.data import NodeDatabase
from klon_core.models import ActionType, Action
from klon_core.models import Revision, NodeInfo

_HUB_DB_FILENAME = ".klon-hub.db"
_NODE_DB_FILENAME = ".klon-node.db"


class MemHubDatabaseTest(unittest.TestCase):
    mem_db = HubDatabase._create_in_memory(access_pw="pw")

    def tearDown(self):
        MemHubDatabaseTest.mem_db.clear()

    def test_hub_database_revision_0(self):
        db = MemHubDatabaseTest.mem_db
        self.assertEqual(0, db.revision_nb)

    def test_hub_database_revision(self):
        db = MemHubDatabaseTest.mem_db
        db.push_history(
            [Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-a.mp3", title="song-a")])
        db.push_history(
            [Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-b.mp3", title="song-b")])
        db.push_history(
            [Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-c.mp3", title="song-c")])
        self.assertEqual(3, db.revision_nb)

    def test_hub_database_get_history_since_0(self):
        db = MemHubDatabaseTest.mem_db
        actions = [
            Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-a.mp3", title="song-a"),
            Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-b.mp3", title="song-b"),
            Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-c.mp3", title="song-c")
        ]
        mock_time = lambda: 42
        with unittest.mock.patch("time.time", mock_time):
            db.push_history(actions)
        self.assertEqual(
            [
                Revision(number=1, timestamp=42,
                         actions=[
                             Action(ActionType.add,
                                    bitrate=320, size=1337, last_mod=1337, path="song-a.mp3", title="song-a"),
                             Action(ActionType.add,
                                    bitrate=320, size=1337, last_mod=1337, path="song-b.mp3", title="song-b"),
                             Action(ActionType.add,
                                    bitrate=320, size=1337, last_mod=1337, path="song-c.mp3", title="song-c")
                         ])
            ],
            db.history(from_revision=0)
        )

    def test_hub_database_get_history_since_n(self):
        db = MemHubDatabaseTest.mem_db
        db.push_history(
            [Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-a.mp3", title="song-a")])
        mock_time = lambda: 42
        with unittest.mock.patch("time.time", mock_time):
            db.push_history([Action(ActionType.add,
                                    bitrate=320, size=1337, last_mod=1337, path="song-b.mp3", title="song-b")])
        mock_time = lambda: 43
        with unittest.mock.patch("time.time", mock_time):
            db.push_history([Action(ActionType.add,
                                    bitrate=320, size=1337, last_mod=1337, path="song-c.mp3", title="song-c")])
        self.assertEqual(
            [
                Revision(number=2, timestamp=42,
                         actions=[Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-b.mp3",
                                         title="song-b")]),
                Revision(number=3, timestamp=43,
                         actions=[Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-c.mp3",
                                         title="song-c")])
            ],
            db.history(from_revision=1)
        )
        self.assertEqual(
            [
                Revision(number=3, timestamp=43,
                         actions=[Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-c.mp3",
                                         title="song-c")])
            ],
            db.history(from_revision=2)
        )

    def test_hub_database_add_node(self):
        db = MemHubDatabaseTest.mem_db
        self.assertFalse(db.nodes)
        mock_time = lambda: 42
        with unittest.mock.patch("time.time", mock_time):
            db.add_node(node_name="node1")
        self.assertEqual(
            [
                NodeInfo(node_name="node1", connection_time=42, last_activity=42, revision_nb=0)
            ],
            db.nodes
        )

    def test_hub_database_add_multiple_nodes(self):
        db = MemHubDatabaseTest.mem_db
        mock_time = lambda: 42
        with unittest.mock.patch("time.time", mock_time):
            db.add_node(node_name="node1")
            db.add_node(node_name="node2")
            db.add_node(node_name="node3")
        self.assertEqual(
            [
                NodeInfo(node_name="node1", connection_time=42, last_activity=42, revision_nb=0),
                NodeInfo(node_name="node2", connection_time=42, last_activity=42, revision_nb=0),
                NodeInfo(node_name="node3", connection_time=42, last_activity=42, revision_nb=0)
            ],
            db.nodes
        )


class HubDatabaseTest(unittest.TestCase):
    def tearDown(self):
        db_file = os.path.join("/tmp/", _HUB_DB_FILENAME)
        if os.path.exists(db_file):
            os.remove(db_file)

    def test_hub_database_creation(self):
        HubDatabase.create(access_pw="pw", db_path="/tmp/")
        self.assertTrue(os.path.exists(os.path.join("/tmp/", _HUB_DB_FILENAME)))

    def test_hub_database_path(self):
        db = HubDatabase.create(access_pw="pw", db_path="/tmp/")
        self.assertEqual(db.path, "/tmp/")

    def test_hub_database_open(self):
        self.assertIsNone(HubDatabase.open(db_path="/tmp/"))
        HubDatabase.create(access_pw="pw", db_path="/tmp/")
        self.assertIsNotNone(HubDatabase.open(db_path="/tmp/"))


class MemNodeDatabaseTest(unittest.TestCase):
    mem_db = NodeDatabase._create_in_memory(node_name="node", hub_url="some url",
                                            node_id="don't care", hub_id="don't care")

    def tearDown(self):
        self.mem_db.clear()

    def test_node_database_revision_0(self):
        db = self.mem_db
        self.assertEqual(0, db.revision_nb)

    def test_node_database_revision(self):
        db = self.mem_db
        db.update_songs(
            [Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-a.mp3", title="song-a")],
            revision_nb=1)
        db.update_songs(
            [Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-b.mp3", title="song-b")],
            revision_nb=3)
        db.update_songs(
            [Action(ActionType.add, bitrate=320, size=1337, last_mod=1337, path="song-c.mp3", title="song-c")],
            revision_nb=5)
        self.assertEqual(5, db.revision_nb)

    def test_node_database_name(self):
        db = self.mem_db
        self.assertEqual("node", db.name)

    def test_node_database_connect(self):
        db = self.mem_db
        self.assertEqual(db.hub_url, "some url")


class NodeDatabaseTest(unittest.TestCase):
    def tearDown(self):
        db_file = os.path.join("/tmp/", _NODE_DB_FILENAME)
        if os.path.exists(db_file):
            os.remove(db_file)

    def test_node_database_creation(self):
        NodeDatabase.create(node_name="node", db_path="/tmp/", hub_url="",
                            node_id="don't care", hub_id="don't care")
        self.assertTrue(os.path.exists(os.path.join("/tmp/", _NODE_DB_FILENAME)))

    def test_node_database_path(self):
        db = NodeDatabase.create(node_name="node", db_path="/tmp/", hub_url="",
                                 node_id="don't care", hub_id="don't care")
        self.assertEqual(db.path, "/tmp/")

    def test_node_database_open(self):
        self.assertIsNone(NodeDatabase.open(db_path="/tmp/"))
        NodeDatabase.create(node_name="node", db_path="/tmp/", hub_url="",
                            node_id="don't care", hub_id="don't care")
        self.assertIsNotNone(NodeDatabase.open(db_path="/tmp/"))


if __name__ == '__main__':
    unittest.main()
