# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

import os
import tempfile
import unittest

from maro.data_lib import BinaryConverter, BinaryReader
from maro.data_lib.item_meta import BinaryMeta


class TestBinaryConverter(unittest.TestCase):
    def test_convert_with_events(self):
        out_dir = tempfile.mkdtemp()

        out_bin = os.path.join(out_dir, "trips.bin")

        meta_file = os.path.join("tests", "data", "data_lib", "case_1", "meta.yml")
        csv_file = os.path.join("tests", "data", "data_lib", "trips.csv")

        bct = BinaryConverter(out_bin, meta_file)

        # add and convert 1st csv file
        bct.add_csv(csv_file)

        # add again will append to the end ignore the order
        bct.add_csv(csv_file)

        # flush will close the file, cannot add again
        bct.flush()

        # check if output exist
        self.assertTrue(os.path.exists(out_bin))

        # check content
        reader = BinaryReader(out_bin)

        # start tick should be smallest one
        start_date = reader.start_datetime

        self.assertEqual(start_date.year, 2019)
        self.assertEqual(start_date.month, 1)
        self.assertEqual(start_date.day, 1)
        self.assertEqual(start_date.hour, 0)
        self.assertEqual(start_date.minute, 0)
        self.assertEqual(start_date.second, 0)

        end_date = reader.end_datetime

        self.assertEqual(end_date.year, 2019)
        self.assertEqual(end_date.month, 1)
        self.assertEqual(end_date.day, 1)
        self.assertEqual(end_date.hour, 0)
        self.assertEqual(end_date.minute, 5)
        self.assertEqual(end_date.second, 0)

        # there should be double items as trips.csv
        self.assertEqual(4 * 2, reader.header.item_count)

        # 20 byte
        self.assertEqual(20, reader.header.item_size)

        start_station_index = [0, 0, 1, 0]

        idx = 0

        # check iterating interface
        for item in reader.items():
            # check if fields same as meta
            self.assertTupleEqual(("timestamp", "durations", "src_station", "dest_station"), item._fields)

            # check item start station index
            self.assertEqual(start_station_index[idx % len(start_station_index)], item.src_station)

            idx += 1

        # check if filter works as expected
        l = len([item for item in reader.items(end_time_offset=0, time_unit="m")])

        # although there are 2 items that match the condition, but they not sorted, reader will not try to read to the end, but
        # to the first item which not match the condition
        self.assertEqual(1, l)

        l = len([item for item in reader.items(start_time_offset=1, time_unit="m")])

        # reader will try to read 1st one that > end tick, so there should be 6 items
        self.assertEqual(6, l)

    def test_convert_without_events(self):
        out_dir = tempfile.mkdtemp()

        out_bin = os.path.join(out_dir, "trips.bin")

        meta_file = os.path.join("tests", "data", "data_lib", "case_2", "meta.yml")
        csv_file = os.path.join("tests", "data", "data_lib", "trips.csv")

        bct = BinaryConverter(out_bin, meta_file)

        bct.add_csv(csv_file)

        # flush will close the file, cannot add again
        bct.flush()

        reader = BinaryReader(out_bin)

        meta: BinaryMeta = reader.meta

        self.assertIsNotNone(meta)

        # check events
        self.assertListEqual(
            ["require_bike", "return_bike", "rebalance_bike", "deliver_bike"],
            [event.display_name for event in meta.events],
        )

        self.assertListEqual(
            ["RequireBike", "ReturnBike", "RebalanceBike", "DeliverBike"],
            [event.type_name for event in meta.events],
        )

        self.assertEqual("RequireBike", meta.default_event_name)
        self.assertIsNone(meta.event_attr_name)

    def test_convert_with_starttimestamp(self):
        out_dir = tempfile.mkdtemp()

        out_bin = os.path.join(out_dir, "trips.bin")

        meta_file = os.path.join("tests", "data", "data_lib", "case_2", "meta.yml")
        csv_file = os.path.join("tests", "data", "data_lib", "trips.csv")

        # 12/31/2018 @ 11:59pm (UTC)
        bct = BinaryConverter(out_bin, meta_file, utc_start_timestamp=1546300740)

        bct.add_csv(csv_file)

        # flush will close the file, cannot add again
        bct.flush()

        reader = BinaryReader(out_bin)

        # check header
        self.assertEqual(1546300740, reader.header.starttime)

        # then tick 0 will not be 2019/01/01 00:00:00
        l = len([item for item in reader.items(end_time_offset=0, time_unit="m")])

        self.assertEqual(0, l)

        # it should be tick 1 for now
        l = len([item for item in reader.items(end_time_offset=1, time_unit="m")])

        self.assertEqual(1, l)

    def test_convert_without_meta_timestamp(self):
        out_dir = tempfile.mkdtemp()

        out_bin = os.path.join(out_dir, "trips.bin")

        meta_file = os.path.join("tests", "data", "data_lib", "case_3", "meta.yml")
        os.path.join("tests", "data", "data_lib", "trips.csv")

        # 12/31/2018 @ 11:59pm (UTC)
        with self.assertRaises(Exception) as ctx:
            BinaryConverter(out_bin, meta_file)


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