import unittest
import datetime

from sno import Sno, epoch

TestSnos = [
    # inspired by https://github.com/rs/xid/blob/master/id_test.go
    {
        'sno': Sno([0x2b, 0x44, 0x5f, 0x68, 0x34, 0x86, 0xe4, 0x28, 0x2d, 0xc9]),
        'ts': datetime.datetime(2021, 10, 11, 15, 16, 34, 24000),
        'tick': False,
        'metabyte': 0x86,
        'partition': 0xe428,
        'counter': 0x2dc9
    },
    {
        'sno': Sno([0x2b, 0x44, 0x5f, 0x68, 0x35, 0x86, 0xe4, 0x28, 0x2d, 0xc9]),
        'ts': datetime.datetime(2021, 10, 11, 15, 16, 34, 24000),
        'tick': True,
        'metabyte': 0x86,
        'partition': 0xe428,
        'counter': 0x2dc9
    },
    {
        'sno': Sno([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
        'ts': epoch,
        'tick': False,
        'metabyte': 0x00,
        'partition': 0x0000,
        'counter': 0
    },
    {
        'sno': Sno([0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xdd, 0xee, 0x00, 0x01]),
        'ts': epoch,
        'tick': False,
        'metabyte': 0xcc,
        'partition': 0xddee,
        'counter': 1
    },
    {
        'sno': Sno([0x00, 0x00, 0x00, 0x00, 0x01, 0xcc, 0xdd, 0xee, 0x00, 0x01]),
        'ts': epoch,
        'tick': True,
        'metabyte': 0xcc,
        'partition': 0xddee,
        'counter': 1
    }
]

class TestSno(unittest.TestCase):
    def test_no_duplicates(self):
        collect = []
        for i in range(0, 1000):
            collect.append(Sno.new())

        ids = [i.to_string() for i in collect]
        self.assertEqual(len(set(ids)), 1000)

    def test_from_string(self):
        x = Sno.new()
        y = Sno.from_string(x.to_string())

        self.assertEqual(x.value, y.value)
        self.assertEqual(x.to_bytes(), y.to_bytes())
        self.assertEqual(x.to_string(), y.to_string())

    def test_sno_always_reversible(self):
        for i in range(1000):
            s = Sno.new().to_string()
            self.assertEqual(Sno.from_string(s).to_string(), s)


    def test_datetime(self):
        for x in TestSnos:
            self.assertEqual(x['sno'].datetime, x['ts'])

    def test_tick(self):
        for x in TestSnos:
            self.assertEqual(x['sno'].tick, x['tick'])

    def test_metabyte(self):
        for x in TestSnos:
            self.assertEqual(x['sno'].metabyte, x['metabyte'])
            
    def test_partition(self):
        for x in TestSnos:
            self.assertEqual(x['sno'].partition, x['partition'])

    def test_counter(self):
        for x in TestSnos:
            self.assertEqual(x['sno'].counter, x['counter'])

    def test_copy_array_from_golang(self):
        x = Sno([78, 111, 33, 96, 160, 255, 154, 10, 16, 51])
        self.assertEqual('brpk4q72xwf2m63l', x.to_string())

    def test_copy_string_from_golang(self):
        x = Sno.from_string('brpk4q72xwf2m63l')
        self.assertEqual(x.value, [78, 111, 33, 96, 160, 255, 154, 10, 16, 51])

    def test_thread_safety(self):
        # TODO: this does not test that it's thread safe?
        import threading
        threads = []

        def worker():
            for i in range(10):
                threading.current_thread().ident, Sno.new().to_string()

        for i in range(10):
            t = threading.Thread(target=worker)
            threads.append(t)
            t.start()

        for t in threads:
            t.join()


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