import datetime
import unittest

import pronotepy
from pronotepy import DiscussionClosed

import warnings

client = pronotepy.Client(
    "https://demo.index-education.net/pronote/eleve.html", "demonstration", "pronotevs"
)

parent_client = pronotepy.ParentClient(
    "https://demo.index-education.net/pronote/parent.html",
    username="demonstration",
    password="pronotevs",
)


def warn_empty(list_: list) -> None:
    if len(list_) == 0:
        warnings.warn("collection empty - cannot test properly")


class TestClient(unittest.TestCase):
    global client
    global parent_client

    def test__get_week(self) -> None:
        self.assertEqual(
            client.get_week(client.start_day + datetime.timedelta(days=8)), 2
        )

    def test_lessons(self) -> None:
        start = client.start_day
        end = client.start_day + datetime.timedelta(days=8)
        lessons = client.lessons(start, end)

        warn_empty(lessons)

        for lesson in lessons:
            self.assertLessEqual(start, lesson.start.date())
            self.assertLessEqual(lesson.start.date(), end)

    def test_periods(self) -> None:
        self.assertIsNotNone(client.periods)

    def test_current_period(self) -> None:
        p = client.current_period
        self.assertIsNotNone(p)

    def test_homework(self) -> None:
        start = client.start_day
        end = client.start_day + datetime.timedelta(days=31)
        homework = client.homework(start, end)

        warn_empty(homework)

        for hw in homework:
            self.assertLessEqual(start, hw.date)
            self.assertLessEqual(hw.date, end)

    def test_recipients(self) -> None:
        recipients = client.get_recipients()

        warn_empty(recipients)

    def test_menus(self) -> None:
        start = client.start_day
        end = client.start_day + datetime.timedelta(days=8)
        menus = client.menus(start, end)
        for menu in menus:
            self.assertLessEqual(start, menu.date)
            self.assertLessEqual(menu.date, end)

    def test_export_ical(self) -> None:
        import requests

        ical = client.export_ical()
        resp = requests.get(ical)
        self.assertEqual(resp.status_code, 200)

    def test_refresh(self) -> None:
        client.refresh()
        self.assertEqual(client.session_check(), True)

    def test_get_teaching_staff(self) -> None:
        warn_empty(client.get_teaching_staff())

    def test_get_calendar(self) -> None:
        import requests

        resp = requests.get(client.generate_timetable_pdf())
        self.assertEqual(resp.status_code, 200)


class TestPeriod(unittest.TestCase):
    period: pronotepy.Period

    @classmethod
    def setUpClass(cls) -> None:
        global client
        cls.period = client.current_period

    def test_grades(self) -> None:
        # We assume demo website will have grades
        warn_empty(self.period.grades)

    def test_averages(self) -> None:
        warn_empty(self.period.averages)

    def test_overall_average(self) -> None:
        self.assertIsNotNone(self.period.overall_average)

    def test_evaluations(self) -> None:
        evaluations = self.period.evaluations
        warn_empty(evaluations)
        for evaluation in evaluations:
            for acquisition in evaluation.acquisitions:
                self.assertIsNotNone(acquisition)

    def test_absences(self) -> None:
        all_absences = []
        for period in client.periods:
            all_absences.extend(period.absences)
        warn_empty(all_absences)

    def test_delays(self) -> None:
        all_delays = []
        for period in client.periods:
            all_delays.extend(period.delays)
        warn_empty(all_delays)

    def test_punishments(self) -> None:
        all_punishments = []
        for period in client.periods:
            all_punishments.extend(period.punishments)

    def test_class_overall_average(self) -> None:
        a = self.period.class_overall_average
        self.assertTrue(type(a) is str or a is None)

    def test_report(self) -> None:
        report = self.period.report
        self.assertTrue(report is None or isinstance(report, pronotepy.Report))


class TestInformation(unittest.TestCase):
    def test_unread(self) -> None:
        information = client.information_and_surveys(only_unread=True)
        for info in information:
            self.assertFalse(info.read)

    def test_time_delta(self) -> None:
        start = datetime.datetime(
            year=client.start_day.year,
            month=client.start_day.month,
            day=client.start_day.day,
        )
        end = start + datetime.timedelta(days=100)
        information = client.information_and_surveys(date_from=start, date_to=end)
        for info in information:
            self.assertTrue(
                info.start_date is not None and start <= info.start_date <= end,
                msg="date outside the research limits",
            )


class TestLesson(unittest.TestCase):
    lesson: pronotepy.Lesson

    @classmethod
    def setUpClass(cls) -> None:
        global client
        cls.lesson = client.lessons(client.start_day + datetime.timedelta(days=4))[0]

    def test_normal(self) -> None:
        self.assertIsNotNone(self.lesson.normal)

    def test_content(self) -> None:
        self.assertIsInstance(self.lesson.content, pronotepy.LessonContent)


class TestLessonContent(unittest.TestCase):
    lessonContent: pronotepy.LessonContent

    @classmethod
    def setUpClass(cls) -> None:
        global client
        content = client.lessons(client.start_day + datetime.timedelta(days=4))[
            0
        ].content
        if content is None:
            raise Exception("Content is None!")
        cls.lessonContent = content

    def test_files(self) -> None:
        self.assertIsNotNone(self.lessonContent.files)


class TestDiscussion(unittest.TestCase):
    discussion: pronotepy.Discussion

    @classmethod
    def setUpClass(cls) -> None:
        global parent_client
        cls.discussion = parent_client.discussions()[0]

    def test_messages(self) -> None:
        warn_empty(self.discussion.messages)

    def test_mark_read(self) -> None:
        self.discussion.mark_as(True)

    def test_reply(self) -> None:
        for discussion in parent_client.discussions():
            if discussion.closed:
                with self.assertRaises(DiscussionClosed):
                    discussion.reply("test")
            else:
                discussion.reply("test")

    def test_delete(self) -> None:
        self.discussion.delete()

    def test_participants(self) -> None:
        warn_empty(self.discussion.participants())


class TestMenu(unittest.TestCase):
    menu: pronotepy.Menu

    @classmethod
    def setUpClass(cls) -> None:
        global client
        i = 0
        menus = client.menus(client.start_day + datetime.timedelta(days=i))
        while len(menus) == 0:
            i += 1
            menus = client.menus(client.start_day + datetime.timedelta(days=i))
        cls.menu = menus[0]

    def test_lunch_dinner(self) -> None:
        self.assertNotEqual(
            self.menu.is_lunch,
            self.menu.is_dinner,
            "The menu is neither a lunch nor a dinner or is both",
        )


class TestParentClient(unittest.TestCase):
    global parent_client
    client: pronotepy.ParentClient = parent_client

    def test_set_child(self) -> None:
        self.client.set_child(self.client.children[1])
        self.client.set_child("PARENT Fanny")

    def test_homework(self) -> None:
        self.assertIsNotNone(
            self.client.homework(
                self.client.start_day,
                self.client.start_day + datetime.timedelta(days=31),
            )
        )

    def test_discussion(self) -> None:
        discussions = self.client.discussions()

        # We assume demo website will always have discussions
        warn_empty(discussions)


class TestVieScolaireClient(unittest.TestCase):
    client: pronotepy.VieScolaireClient

    @classmethod
    def setUpClass(cls) -> None:
        cls.client = pronotepy.VieScolaireClient(
            "https://demo.index-education.net/pronote/viescolaire.html",
            "demonstration2",
            "pronotevs",
        )

    def test_classes(self) -> None:
        warn_empty(self.client.classes)

        for cls in self.client.classes:
            self.assertIsNotNone(cls.name)

        for student in self.client.classes[0].students():
            self.assertIsInstance(student.identity, pronotepy.Identity)
            self.assertGreater(
                len(student.guardians), 0
            )  # pretty safe to assume not empty
            for guardian in student.guardians:
                self.assertIsInstance(guardian.identity, pronotepy.Identity)


class TestClientInfo(unittest.TestCase):
    info: pronotepy.ClientInfo

    @classmethod
    def setUpClass(cls) -> None:
        global client
        cls.info = client.info

    def test_address(self) -> None:
        address = self.info.address
        self.assertIsNotNone(address, "Address was None")
        self.assertEqual(len(address), 8, "Address information was not 8 elements long")
        for i in address:
            self.assertTrue(
                type(i) == str, f"Address information was not a string: {i}"
            )

    def test_ine_number(self) -> None:
        self.assertEqual(self.info.ine_number, "001")

    def test_phone(self) -> None:
        self.assertRegex(self.info.phone, r"\+[0-9]+")

    def test_email(self) -> None:
        self.assertRegex(
            self.info.email,
            r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
            "email did not match regex",
        )


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