"""This module contains the tests for utils of the ``common`` app."""

from datetime import timedelta

from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.test import TestCase

from ..utils import (
    filter_list_of_dicts,
    get_dict_in_list,
    sort_list_of_dicts,
    string_to_timedelta,
    timedelta_to_string
)


class CommonUtilsTestCase(TestCase):
    """TestCase to test the functions found in mtp.apps.common.utils"""

    def setUp(self):
        super().setUp()

        # Create a little list of dictionaries to test the related helpers.
        self.lst = [
            {'first_name': 'Kara', 'last_name': 'Thrace', 'call_sign': 'Starbuck'},
            {'first_name': 'Lee', 'last_name': 'Adama', 'call_sign': 'Apollo'},
            {'first_name': 'William', 'last_name': 'Adama', 'call_sign': 'Husker'},
        ]

    def test_sort_list_of_dicts(self):
        """Test the ``sort_list_of_dicts`` function."""

        # Check that sorting works on all the keys.
        expected = [
            {'first_name': 'Kara', 'last_name': 'Thrace', 'call_sign': 'Starbuck'},
            {'first_name': 'Lee', 'last_name': 'Adama', 'call_sign': 'Apollo'},
            {'first_name': 'William', 'last_name': 'Adama', 'call_sign': 'Husker'},
        ]
        computed = sort_list_of_dicts(self.lst, 'first_name')
        self.assertEqual(expected, computed)

        expected = [
            {'first_name': 'Lee', 'last_name': 'Adama', 'call_sign': 'Apollo'},
            {'first_name': 'William', 'last_name': 'Adama', 'call_sign': 'Husker'},
            {'first_name': 'Kara', 'last_name': 'Thrace', 'call_sign': 'Starbuck'},
        ]
        computed = sort_list_of_dicts(self.lst, 'last_name')
        self.assertEqual(expected, computed)

        expected = [
            {'first_name': 'Lee', 'last_name': 'Adama', 'call_sign': 'Apollo'},
            {'first_name': 'William', 'last_name': 'Adama', 'call_sign': 'Husker'},
            {'first_name': 'Kara', 'last_name': 'Thrace', 'call_sign': 'Starbuck'},
        ]
        computed = sort_list_of_dicts(self.lst, 'call_sign')
        self.assertEqual(expected, computed)

        # Check that sorting on a non-existing key raises a ``KeyError``.
        with self.assertRaises(KeyError):
            sort_list_of_dicts(self.lst, 'is_a_cylon')

    def test_filter_list_in_dicts(self):
        """Test the ``filter_list_of_dicts`` function."""

        # Check that filtering with an existing value works.
        expected = [
            {'first_name': 'Lee', 'last_name': 'Adama', 'call_sign': 'Apollo'},
            {'first_name': 'William', 'last_name': 'Adama', 'call_sign': 'Husker'},
        ]
        computed = filter_list_of_dicts(self.lst, 'last_name', 'Adama')
        self.assertEqual(expected, computed)

        # Check that filtering with a non-existing value returns an empty list.
        expected = []
        computed = filter_list_of_dicts(self.lst, 'last_name', 'Valerii')
        self.assertEqual(expected, computed)

        # Check that filtering with a non-existing key returns an empty list.
        expected = []
        computed = filter_list_of_dicts(self.lst, 'home_world', 'Caprica')
        self.assertEqual(expected, computed)

    def test_get_dict_in_lists(self):
        """Test the ``get_dict_in_list`` function."""

        # Check that the nominal case works.
        expected = {
            'first_name': 'Kara', 'last_name': 'Thrace', 'call_sign': 'Starbuck'
        }
        computed = get_dict_in_list(self.lst, 'call_sign', 'Starbuck')
        self.assertEqual(expected, computed)

        # Check that specifying a non-existing key raises ``ObjectDoesNotExist``.
        with self.assertRaises(ObjectDoesNotExist):
            get_dict_in_list(self.lst, 'home_world', 'Caprica')

        # Check that specifying a non-existing value raises ``ObjectDoesNotExist``.
        with self.assertRaises(ObjectDoesNotExist):
            get_dict_in_list(self.lst, 'call_sign', 'Boomer')

        # Check that getting multiple results raises ``MultipleObjectsReturned``.
        with self.assertRaises(MultipleObjectsReturned):
            get_dict_in_list(self.lst, 'last_name', 'Adama')

    def test_timedelta_to_string(self):
        """Test the ``timedelta_to_string`` function."""

        time = timedelta(days=3, seconds=3569)
        self.assertEqual(
            timedelta_to_string(time),
            '3 days, 59 minutes, 29 seconds'
        )

        time = timedelta(days=1, seconds=1)
        self.assertEqual(
            timedelta_to_string(time),
            '1 day, 1 second'
        )

        time = timedelta(hours=3, seconds=90)
        self.assertEqual(
            timedelta_to_string(time),
            '3 hours, 1 minute, 30 seconds'
        )

        time = timedelta(hours=7)
        self.assertEqual(
            timedelta_to_string(time),
            '7 hours'
        )

        time = timedelta(seconds=3569)
        self.assertEqual(
            timedelta_to_string(time),
            '59 minutes, 29 seconds'
        )

        self.assertEqual(
            timedelta_to_string(None),
            ''
        )

    def test_string_to_timedelta(self):
        """Test the ``string_to_timedelta`` function."""

        time_text = '3 days, 59 minutes, 29 seconds'
        self.assertEqual(
            string_to_timedelta(time_text),
            timedelta(days=3, seconds=3569)
        )

        time_text = '1 day, 1 second'
        self.assertEqual(
            string_to_timedelta(time_text),
            timedelta(days=1, seconds=1)
        )

        time_text = '3 hours, 1 minute, 30 seconds'
        self.assertEqual(
            string_to_timedelta(time_text),
            timedelta(hours=3, seconds=90)
        )

        time_text = '7 hours'
        self.assertEqual(
            string_to_timedelta(time_text),
            timedelta(hours=7)
        )

        time_text = '59 minutes, 29 seconds'
        self.assertEqual(
            string_to_timedelta(time_text),
            timedelta(seconds=3569)
        )

        time_text = '59 foo, 29 bar'
        self.assertEqual(
            string_to_timedelta(time_text),
            None
        )
