import datetime
import decimal
import unittest

from lesana import types


class testTypes(unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def _get_field_def(self, type_name):
        return {
            'type': type_name,
            'name': 'test_field',
        }

    def test_base(self):
        checker = types.LesanaType(self._get_field_def('base'), {})

        # The base class does not implement empty nor load
        with self.assertRaises(NotImplementedError):
            checker.empty()

        with self.assertRaises(NotImplementedError):
            checker.load("")

    def test_string(self):
        checker = types.LesanaString(self._get_field_def('string'), {})

        s = checker.empty()
        self.assertEqual(s, "")

        s = checker.load("Hello World!")
        self.assertEqual(s, "Hello World!")

        s = checker.load(None)
        self.assertEqual(s, None)

    def test_text(self):
        checker = types.LesanaText(self._get_field_def('text'), {})

        s = checker.empty()
        self.assertEqual(s, "")

        s = checker.load("Hello World!")
        self.assertEqual(s, "Hello World!")

        s = checker.load(None)
        self.assertEqual(s, None)

    def test_int(self):
        checker = types.LesanaInt(self._get_field_def('integer'), {})

        v = checker.empty()
        self.assertEqual(v, 0)

        v = checker.load("10")
        self.assertEqual(v, 10)

        v = checker.load(10.5)
        self.assertEqual(v, 10)

        for d in ("ten", "10.5"):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)

        v = checker.load(None)
        self.assertEqual(v, None)

    def test_float(self):
        checker = types.LesanaFloat(self._get_field_def('float'), {})

        v = checker.empty()
        self.assertEqual(v, 0.0)

        v = checker.load("10")
        self.assertEqual(v, 10)

        v = checker.load(10.5)
        self.assertEqual(v, 10.5)

        v = checker.load("10.5")
        self.assertEqual(v, 10.5)

        for d in ("ten"):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)

        v = checker.load(None)
        self.assertEqual(v, None)

    def test_decimal(self):
        checker = types.LesanaDecimal(self._get_field_def('decimal'), {})

        v = checker.empty()
        self.assertEqual(v, decimal.Decimal(0))

        v = checker.load("10")
        self.assertEqual(v, decimal.Decimal(10))

        v = checker.load(10.5)
        self.assertEqual(v, decimal.Decimal(10.5))

        v = checker.load("10.5")
        self.assertEqual(v, decimal.Decimal(10.5))

        for d in ("ten"):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)

        v = checker.load(None)
        self.assertEqual(v, None)

    def test_timestamp(self):
        checker = types.LesanaTimestamp(self._get_field_def('timestamp'), {})

        v = checker.empty()
        self.assertEqual(v, None)

        now = datetime.datetime.now()
        v = checker.load(now)
        self.assertEqual(v, now)

        v = checker.load("1600000000")
        self.assertEqual(v, datetime.datetime(2020, 9, 13, 14, 26, 40))

        today = datetime.date.today()
        for d in (
            today,
            "today",
            "2020-13-01", "2020-01-01",
            "2020-01-01 10:00"
        ):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)

        v = checker.load(None)
        self.assertEqual(v, None)

    def test_datetime(self):
        checker = types.LesanaDatetime(self._get_field_def('datetime'), {})

        v = checker.empty()
        self.assertEqual(v, None)

        now = datetime.datetime.now()
        v = checker.load(now)
        self.assertEqual(v, now)

        today = datetime.date.today()
        v = checker.load(today)
        self.assertIsInstance(v, datetime.datetime)
        for part in ('year', 'month', 'day'):
            self.assertEqual(getattr(v, part), getattr(today, part))

        v = checker.load("2020-01-01")
        self.assertEqual(v, datetime.datetime(2020, 1, 1))

        v = checker.load("2020-01-01 10:00")
        self.assertEqual(v, datetime.datetime(2020, 1, 1, 10, 0))

        for d in ("today", "2020-13-01"):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)

        v = checker.load(None)
        self.assertEqual(v, None)

    def test_date(self):
        checker = types.LesanaDate(self._get_field_def('date'), {})

        v = checker.empty()
        self.assertEqual(v, None)

        now = datetime.datetime.now()
        v = checker.load(now)
        self.assertIsInstance(v, datetime.date)
        for part in ('year', 'month', 'day'):
            self.assertEqual(getattr(v, part), getattr(now, part))

        today = datetime.date.today()
        v = checker.load(today)
        self.assertEqual(v, today)

        v = checker.load("2020-01-01")
        self.assertEqual(v, datetime.datetime(2020, 1, 1))

        v = checker.load("2020-01-01 10:00")
        self.assertEqual(v, datetime.datetime(2020, 1, 1, 10, 0))

        for d in ("today", "2020-13-01"):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)

        v = checker.load(None)
        self.assertEqual(v, None)

    def test_boolean(self):
        checker = types.LesanaBoolean(self._get_field_def('boolean'), {})

        v = checker.empty()
        self.assertEqual(v, None)

        v = checker.load(True)
        self.assertEqual(v, True)

        for d in ("maybe", "yes", "no"):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)

        v = checker.load(None)
        self.assertEqual(v, None)

    def test_file(self):
        checker = types.LesanaFile(self._get_field_def('file'), {})

        v = checker.empty()
        self.assertEqual(v, "")

        v = checker.load("relative/path/to/file")
        self.assertEqual(v, "relative/path/to/file")

        v = checker.load(None)
        self.assertEqual(v, None)

        # TODO: check for invalid file paths

    def test_url(self):
        checker = types.LesanaURL(self._get_field_def('url'), {})

        v = checker.empty()
        self.assertEqual(v, "")

        v = checker.load("http://example.org")
        self.assertEqual(v, "http://example.org")

        v = checker.load(None)
        self.assertEqual(v, None)

        # TODO: check for invalid URLs

    def test_yaml(self):
        checker = types.LesanaYAML(self._get_field_def('yaml'), {})

        v = checker.empty()
        self.assertEqual(v, None)

        some_data = {
            'anything': 'goes',
            'everything': 42
        }
        v = checker.load(some_data)
        self.assertEqual(v, some_data)

        v = checker.load(None)
        self.assertEqual(v, None)

    def test_list(self):
        field_def = self._get_field_def('yaml')
        # we use one type that is easy to check for correct validation
        field_def['list'] = 'int'
        checker = types.LesanaList(field_def, {'int': types.LesanaInt})

        v = checker.empty()
        self.assertEqual(v, [])

        some_data = [1, 2, 3]
        v = checker.load(some_data)
        self.assertEqual(v, some_data)

        v = checker.load(None)
        self.assertEqual(v, [])

        for d in (['hello'], 1):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)

    def test_list_unknown_subtype(self):
        field_def = self._get_field_def('yaml')
        # we use one type that is easy to check for correct validation
        field_def['list'] = 'int'
        checker = types.LesanaList(field_def, {'yaml': types.LesanaYAML})

        v = checker.empty()
        self.assertEqual(v, [])

        some_data = [1, 2, 3]
        v = checker.load(some_data)
        self.assertEqual(v, some_data)

        some_data = ["hello"]
        v = checker.load(some_data)
        self.assertEqual(v, some_data)

        v = checker.load(None)
        self.assertEqual(v, [])

        for d in (1, 1.0):
            with self.assertRaises(types.LesanaValueError):
                checker.load(d)


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