"""Tests for dictionary exercises"""
from operator import itemgetter
import unittest


from dictionaries import (
    count_words,
    group_by,
    get_shared_keys,
    flip_dict,
    translate,
    pluck,
    get_all_factors,
    get_ascii_codes,
    dict_from_truple,
    dict_from_tuple,
    sort_dict,
)


class CountWordsTests(unittest.TestCase):

    """Tests for count_words."""

    def test_simple_sentence(self):
        self.assertEqual(
            count_words("oh what a day what a lovely day"),
            {"oh": 1, "what": 2, "a": 2, "day": 2, "lovely": 1},
        )

    def test_apostrophe(self):
        self.assertEqual(
            count_words("don't stop believing"),
            {"don't": 1, "stop": 1, "believing": 1},
        )

    @unittest.skip("Comment this line for the bonus count_words.")
    def test_capitalization(self):
        self.assertEqual(
            count_words("Oh what a day what a lovely day"),
            {"oh": 1, "what": 2, "a": 2, "day": 2, "lovely": 1},
        )

    @unittest.skip("Comment this line for the bonus count_words.")
    def test_symbols(self):
        self.assertEqual(
            count_words("Oh what a day, what a lovely day!"),
            {"oh": 1, "what": 2, "a": 2, "day": 2, "lovely": 1},
        )


class GroupByTests(unittest.TestCase):

    """Tests for group_by."""

    def test_test_tuples_of_strings(self):
        animals = [
            ("agatha", "dog"),
            ("kurt", "cat"),
            ("margaret", "mouse"),
            ("cory", "cat"),
            ("mary", "mouse"),
        ]
        animals_by_type = {
            "mouse": [("margaret", "mouse"), ("mary", "mouse")],
            "dog": [("agatha", "dog")],
            "cat": [("kurt", "cat"), ("cory", "cat")],
        }
        self.assertEqual(group_by(animals, key_func=itemgetter(1)), animals_by_type)

    def test_strings(self):
        words = ["Apple", "animal", "apple", "ANIMAL", "animal"]
        word_groups = {
            "apple": ["Apple", "apple"],
            "animal": ["animal", "ANIMAL", "animal"],
        }
        self.assertEqual(group_by(words, key_func=str.lower), word_groups)


class GetSharedKeys(unittest.TestCase):

    """Tests for get_shared_keys."""

    def test_no_keys_in_common(self):
        d1 = {1: (2, 3), 4: (5, 6), 7: (8, 9)}
        d2 = {3: 0, 2: 9, 6: 4}
        self.assertEqual(set(get_shared_keys(d1, d2)), set())

    def test_some_shared_keys(self):
        d1 = {1: (2, 3), 4: (5, 6), 7: (8, 9)}
        d2 = {3: 0, 4: 9, 6: 4, 1: 7}
        self.assertEqual(set(get_shared_keys(d1, d2)), {4, 1})

    def test_with_strings(self):
        self.assertEqual(
            set(
                get_shared_keys(
                    {"c95": "20200315", "d45": "20200401", "b38": "20200415"},
                    {"a56": 8, "b38": 1, "e77": 4, "d45": 3},
                )
            ),
            {"d45", "b38"},
        )


class DictFromTrupleTests(unittest.TestCase):

    """Tests for dict_from_truple."""

    def test_three_tuples(self):
        self.assertEqual(
            dict_from_truple([(1, 2, 3), (4, 5, 6), (7, 8, 9)]),
            {1: (2, 3), 4: (5, 6), 7: (8, 9)},
        )


class TranslateTests(unittest.TestCase):

    """Tests for translate."""

    def test_cat(self):
        self.assertEqual(translate("gato"), "cat")

    def test_the_cat(self):
        self.assertEqual(translate("el gato"), "the cat")

    def test_cat_in_house(self):
        self.assertEqual(
            translate("el gato esta en la casa"),
            "the cat is in the house",
        )


class PluckTests(unittest.TestCase):

    """Tests for pluck."""

    def test_pluck_top_level(self):
        d = {'a': {'b': 5, 'z': 20}, 'c': {'d': 3}, 'x': 40}
        self.assertEqual(pluck(d, 'x'), 40)
        self.assertEqual(pluck(d, 'c'), {'d': 3})

    def test_pluck_one_level_deep(self):
        d = {'a': {'b': 5, 'z': 20}, 'c': {'d': 3}, 'x': 40}
        self.assertEqual(pluck(d, 'a.b'), 5)
        self.assertEqual(pluck(d, 'c.d'), 3)

    def test_pluck_many_levels_deep(self):
        d = {'a': {'b': {'c': {'d': {'e': 4}}}}}
        self.assertEqual(pluck(d, 'a.b.c'), {'d': {'e': 4}})
        self.assertEqual(pluck(d, 'a.b.c.d'), {'e': 4})
        self.assertEqual(pluck(d, 'a.b.c.d.e'), 4)

    def test_exception_on_missing_item(self):
        d = {'a': {'b': 5, 'z': 20}, 'c': {'d': 3}, 'x': 40}
        with self.assertRaises(KeyError):
            pluck(d, 'c.e')
        with self.assertRaises(KeyError):
            pluck(d, 'z')

    def test_specifying_separator(self):
        d = {'a': {'b': 5, 'z': 20}, 'c': {'d': 3}, 'x': 40}
        self.assertEqual(pluck(d, 'c/d', sep='/'), 3)
        self.assertEqual(pluck(d, 'a.b', sep='.'), 5)
        self.assertEqual(pluck(d, 'a z', sep=' '), 20)


class GetAllFactorsTests(unittest.TestCase):

    """Tests for get_all_factors."""

    def test_small_numbers(self):
        self.assertEqual(
            get_all_factors({1, 2, 3, 4}), {1: [1], 2: [1, 2], 3: [1, 3], 4: [1, 2, 4]}
        )

    def test_larger_numbers(self):
        self.assertEqual(
            get_all_factors({62, 293, 314}),
            {62: [1, 2, 31, 62], 293: [1, 293], 314: [1, 2, 157, 314]},
        )


class GetASCIICodeTests(unittest.TestCase):

    """Tests for get_ascii_codes."""

    def test_multiple_words(self):
        words = ["hello", "bye", "yes", "no", "python"]
        self.assertEqual(
            get_ascii_codes(words),
            {
                "yes": [121, 101, 115],
                "hello": [104, 101, 108, 108, 111],
                "python": [112, 121, 116, 104, 111, 110],
                "no": [110, 111],
                "bye": [98, 121, 101],
            },
        )


class DictFromTupleTests(unittest.TestCase):

    """Tests for dict_from_tuple."""

    def test_four_items(self):
        self.assertEqual(
            dict_from_tuple([(1, 2, 3, 4), (5, 6, 7, 8)]),
            {1: (2, 3, 4), 5: (6, 7, 8)},
        )

    def test_three_items(self):
        self.assertEqual(
            dict_from_tuple([(1, 2, 3), (4, 5, 6), (7, 8, 9)]),
            {1: (2, 3), 4: (5, 6), 7: (8, 9)},
        )


class FlipDictTests(unittest.TestCase):

    """Tests for flip_dict."""

    def test_no_collisions(self):
        self.assertEqual(
            flip_dict(
                {"Python": "2015-09-15", "Java": "2015-09-14", "C": "2015-09-13"}
            ),
            {"2015-09-13": "C", "2015-09-15": "Python", "2015-09-14": "Java"},
        )

    @unittest.skip("Comment out this line for the exception version")
    def test_with_collisions(self):
        with self.assertRaises(ValueError):
            flip_dict(
                {
                    "Python": "2015-09-15",
                    "Java": "2015-09-14",
                    "C": "2015-09-13",
                    "JavaScript": "2015-09-13",
                }
            )


class SortDictTests(unittest.TestCase):

    """Tests for sort_dict."""

    def test_sort_dict(self):
        expected = [1, 2, 3, 4, 5, 6]
        my_dict = {1: "one", 6: "six", 4: "four", 3: "three", 2: "two", 5: "five"}
        new_dict = sort_dict(my_dict)
        self.assertEqual(list(new_dict.keys()), expected)


if __name__ == "__main__":
    from helpers import error_message

    error_message()
