import uuid
from datetime import timedelta

from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.messages.api import get_messages
from django.contrib.messages.storage.base import Message
from django.core import mail
from django.core.mail import EmailMessage
from django.template import loader
from django.test import Client, TestCase, override_settings
from django.urls import reverse

from bda.models import Salle, Tirage
from gestioncof.models import CalendarSubscription, Club, Event, Survey, SurveyAnswer
from gestioncof.tests.mixins import MegaHelperMixin, ViewTestCaseMixin
from shared.autocomplete import Clipper, LDAPSearch
from shared.tests.mixins import CSVResponseMixin, ICalMixin, MockLDAPMixin

from .utils import create_member, create_root, create_user

User = get_user_model()


class RegistrationViewTests(ViewTestCaseMixin, TestCase):
    url_name = "registration"
    url_expected = "/gestion/registration/"

    http_methods = ["GET", "POST"]

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test_get(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)

    @property
    def _minimal_data(self):
        return {
            "first_name": "",
            "last_name": "",
            "email": "",
            # 'is_cof': '1',
            "login_clipper": "",
            "phone": "",
            "occupation": "1A",
            "departement": "",
            "type_cotiz": "normalien",
            "comments": "",
            # 'user_exists': '1',
            "events-TOTAL_FORMS": "0",
            "events-INITIAL_FORMS": "0",
            "events-MIN_NUM_FORMS": "0",
            "events-MAX_NUM_FORMS": "1000",
        }

    def test_post_new(self):
        r = self.client.post(
            self.url,
            dict(
                self._minimal_data,
                **{
                    "username": "username",
                    "first_name": "first",
                    "last_name": "last",
                    "email": "username@mail.net",
                    "is_cof": "1",
                },
            ),
        )

        self.assertEqual(r.status_code, 200)
        u = User.objects.get(username="username")
        expected_message = Message(
            messages.SUCCESS,
            (
                "L'inscription de first last (<tt>username@mail.net</tt>) a été "
                "enregistrée avec succès.\n"
                "Il est désormais membre du COF n°{} !".format(u.pk)
            ),
        )
        self.assertIn(expected_message, get_messages(r.wsgi_request))

        self.assertEqual(u.first_name, "first")
        self.assertEqual(u.last_name, "last")
        self.assertEqual(u.email, "username@mail.net")

    def test_post_edit(self):
        u = self.users["user"]

        r = self.client.post(
            self.url,
            dict(
                self._minimal_data,
                **{
                    "username": "user",
                    "first_name": "first",
                    "last_name": "last",
                    "email": "user@mail.net",
                    "is_cof": "1",
                    "user_exists": "1",
                },
            ),
        )

        self.assertEqual(r.status_code, 200)
        u.refresh_from_db()
        expected_message = Message(
            messages.SUCCESS,
            (
                "L'inscription de first last (<tt>user@mail.net</tt>) a été "
                "enregistrée avec succès.\n"
                "Il est désormais membre du COF n°{} !".format(u.pk)
            ),
        )
        self.assertIn(expected_message, get_messages(r.wsgi_request))

        self.assertEqual(u.first_name, "first")
        self.assertEqual(u.last_name, "last")
        self.assertEqual(u.email, "user@mail.net")

    def _test_mail_welcome(self, was_cof, is_cof, expect_mail):
        u = self.users["member"] if was_cof else self.users["user"]

        data = dict(
            self._minimal_data,
            **{"username": u.username, "email": "user@mail.net", "user_exists": "1"},
        )
        if is_cof:
            data["is_cof"] = "1"
        self.client.post(self.url, data)

        u.refresh_from_db()

        def _is_sent():
            welcome_msg = EmailMessage(
                subject="Bienvenue au COF",
                body=loader.render_to_string(
                    "gestioncof/mails/welcome.txt", context={"member": "u"}
                ),
            )
            for m in mail.outbox:
                if m.subject == welcome_msg.subject:
                    return True
            return False

        self.assertEqual(_is_sent(), expect_mail)

    def test_mail_welcome_0(self):
        self._test_mail_welcome(was_cof=False, is_cof=False, expect_mail=False)

    def test_mail_welcome_1(self):
        self._test_mail_welcome(was_cof=False, is_cof=True, expect_mail=True)

    def test_mail_welcome_2(self):
        self._test_mail_welcome(was_cof=True, is_cof=False, expect_mail=False)

    def test_mail_welcome_3(self):
        self._test_mail_welcome(was_cof=True, is_cof=True, expect_mail=False)

    def test_events(self):
        e = Event.objects.create()

        cf1 = e.commentfields.create(name="Comment Field 1")
        cf2 = e.commentfields.create(name="Comment Field 2", fieldtype="char")

        o1 = e.options.create(name="Option 1")
        o2 = e.options.create(name="Option 2", multi_choices=True)

        oc1 = o1.choices.create(value="O1 - Choice 1")
        o1.choices.create(value="O1 - Choice 2")
        oc3 = o2.choices.create(value="O2 - Choice 1")
        o2.choices.create(value="O2 - Choice 2")

        self.client.post(
            self.url,
            dict(
                self._minimal_data,
                **{
                    "username": "user",
                    "user_exists": "1",
                    "events-TOTAL_FORMS": "1",
                    "events-INITIAL_FORMS": "0",
                    "events-MIN_NUM_FORMS": "0",
                    "events-MAX_NUM_FORMS": "1000",
                    "events-0-status": "paid",
                    "events-0-option_{}".format(o1.pk): [str(oc1.pk)],
                    "events-0-option_{}".format(o2.pk): [str(oc3.pk)],
                    "events-0-comment_{}".format(cf1.pk): "comment 1",
                    "events-0-comment_{}".format(cf2.pk): "",
                },
            ),
        )

        er = e.eventregistration_set.get(user=self.users["user"])
        self.assertQuerysetEqual(
            er.options.all(), map(repr, [oc1, oc3]), transform=repr, ordered=False
        )
        self.assertCountEqual(
            er.comments.values_list("content", flat=True), ["comment 1"]
        )


class RegistrationFormViewTests(ViewTestCaseMixin, TestCase):
    urls_conf = [
        {"name": "empty-registration", "expected": "/gestion/registration/empty"},
        {
            "name": "user-registration",
            "kwargs": {"username": "user"},
            "expected": "/gestion/registration/user/user",
        },
        {
            "name": "clipper-registration",
            "kwargs": {"login_clipper": "uid", "fullname": "First Last1 Last2"},
            "expected": "/gestion/registration/clipper/uid/First%20Last1%20Last2",
        },
    ]

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test_empty(self):
        r = self.client.get(self.reversed_urls[0])

        self.assertIn("user_form", r.context)
        self.assertIn("profile_form", r.context)
        self.assertIn("event_formset", r.context)
        self.assertIn("clubs_form", r.context)

    def test_username(self):
        u = self.users["user"]
        u.first_name = "first"
        u.last_name = "last"
        u.save()

        r = self.client.get(self.reversed_urls[1])

        self.assertIn("user_form", r.context)
        self.assertIn("profile_form", r.context)
        self.assertIn("event_formset", r.context)
        self.assertIn("clubs_form", r.context)
        user_form = r.context["user_form"]
        self.assertEqual(user_form["username"].initial, "user")
        self.assertEqual(user_form["first_name"].initial, "first")
        self.assertEqual(user_form["last_name"].initial, "last")

    def test_clipper(self):
        r = self.client.get(self.reversed_urls[2])

        self.assertIn("user_form", r.context)
        self.assertIn("profile_form", r.context)
        self.assertIn("event_formset", r.context)
        self.assertIn("clubs_form", r.context)
        user_form = r.context["user_form"]
        profile_form = r.context["profile_form"]
        self.assertEqual(user_form["first_name"].initial, "First")
        self.assertEqual(user_form["last_name"].initial, "Last1 Last2")
        self.assertEqual(user_form["email"].initial, "uid@clipper.ens.fr")
        self.assertEqual(profile_form["login_clipper"].initial, "uid")


@override_settings(LDAP_SERVER_URL="ldap_url")
class RegistrationAutocompleteViewTests(MockLDAPMixin, ViewTestCaseMixin, TestCase):
    url_name = "cof.registration.autocomplete"
    url_expected = "/gestion/registration/autocomplete"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

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

        self.u1 = create_user("uu_u1", attrs={"first_name": "abc", "last_name": "xyz"})
        self.u2 = create_user("uu_u2", attrs={"first_name": "wyz", "last_name": "abd"})
        self.m1 = create_member(
            "uu_m1", attrs={"first_name": "ebd", "last_name": "wyv"}
        )

        self.mockLDAP([])

    def _test(self, query, expected_others, expected_members, expected_clippers):
        r = self.client.get(self.url, {"q": query})

        self.assertEqual(r.status_code, 200)

        def extract(section):
            return [r.verbose_name for r in section.entries]

        others = []
        members = []
        clippers = []
        for section in r.context["results"]:
            if section.name == "others":
                others = extract(section)
            elif section.name == "members":
                members = extract(section)
            elif section.name == "clippers":
                clippers = extract(section)
            else:
                raise ValueError("Unexpected section name: {}".format(section.name))

        self.assertQuerysetEqual(
            others, map(str, expected_others), transform=str, ordered=False
        )
        self.assertQuerysetEqual(
            members, map(str, expected_members), transform=str, ordered=False
        )
        self.assertSetEqual(
            set(clippers), set(map(LDAPSearch().result_verbose_name, expected_clippers))
        )

    def test_username(self):
        self._test("uu", [self.u1, self.u2], [self.m1], [])

    def test_firstname(self):
        self._test("ab", [self.u1, self.u2], [], [])

    def test_lastname(self):
        self._test("wy", [self.u2], [self.m1], [])

    def test_multi_query(self):
        self._test("wy bd", [self.u2], [self.m1], [])

    def test_clipper(self):
        mock_ldap = self.mockLDAP([("uid", "first last", "mail")])

        self._test("aa bb", [], [], [Clipper("uid", "first last", "mail")])

        mock_ldap.ldap_obj.search_s.assert_called_once_with(
            "dc=spi,dc=ens,dc=fr",
            mock_ldap.SCOPE_SUBTREE,
            "(&(|(cn=*aa*)(uid=*aa*))(|(cn=*bb*)(uid=*bb*)))",
            ["cn", "uid", "mail"],
        )

    def test_clipper_escaped(self):
        mock_ldap = self.mockLDAP([])

        self._test("; & | (", [], [], [])

        mock_ldap.ldap_obj.search_s.assert_not_called()

    def test_clipper_no_duplicate(self):
        self.mockLDAP([("uid", "abc", "mail")])

        self._test("abc", [self.u1], [], [Clipper("uid", "abc", "mail")])

        self.u1.username = "uid"
        self.u1.save()

        self._test("abc", [self.u1], [], [])


class HomeViewTests(ViewTestCaseMixin, TestCase):
    url_name = "home"
    url_expected = "/gestion/"

    auth_user = "user"
    auth_forbidden = [None]

    def test(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)


class ProfileViewTests(ViewTestCaseMixin, TestCase):
    url_name = "profile"
    url_expected = "/gestion/profile"

    http_methods = ["GET", "POST"]

    auth_user = "member"
    auth_forbidden = [None]

    def test_get(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)

    def test_post(self):
        u = self.users["member"]

        r = self.client.post(
            self.url,
            {
                "u-first_name": "First",
                "u-last_name": "Last",
                "p-phone": "",
                # 'mailing_cof': '1',
                # 'mailing_bda': '1',
                # 'mailing_bda_revente': '1',
            },
        )

        self.assertEqual(r.status_code, 200)
        expected_message = Message(
            messages.SUCCESS, ("Votre profil a été mis à jour avec succès !")
        )
        self.assertIn(expected_message, get_messages(r.wsgi_request))
        u.refresh_from_db()
        self.assertEqual(u.first_name, "First")
        self.assertEqual(u.last_name, "Last")
        self.assertFalse(u.profile.mailing_cof)
        self.assertFalse(u.profile.mailing_bda)
        self.assertFalse(u.profile.mailing_bda_revente)


class UtilsViewTests(ViewTestCaseMixin, TestCase):
    url_name = "utile_cof"
    url_expected = "/gestion/utile_cof"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)


class MailingListDiffCof(ViewTestCaseMixin, TestCase):
    url_name = "ml_diffcof"
    url_expected = "/gestion/utile_cof/diff_cof"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

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

        self.u1 = create_member("u1", attrs={"mailing_cof": True})
        self.u2 = create_member("u2", attrs={"mailing_cof": False})
        self.u3 = create_user("u3", attrs={"mailing_cof": True})

    def test(self):
        r = self.client.get(self.url)

        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.context["personnes"].get(), self.u1.profile)


class ConfigUpdateViewTests(ViewTestCaseMixin, TestCase):
    url_name = "config.edit"
    url_expected = "/gestion/config"

    http_methods = ["GET", "POST"]

    auth_user = "root"
    auth_forbidden = [None, "user", "member", "staff"]

    def get_users_extra(self):
        return {"root": create_root("root")}

    def test_get(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)

    def test_post(self):
        r = self.client.post(self.url, {"gestion_banner": "Announcement !"})

        self.assertRedirects(r, reverse("home"))


class UserAutocompleteViewTests(ViewTestCaseMixin, TestCase):
    url_name = "cof-user-autocomplete"
    url_expected = "/gestion/user/autocomplete"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test(self):
        r = self.client.get(self.url, {"q": "user"})

        self.assertEqual(r.status_code, 200)


class ExportMembersViewTests(CSVResponseMixin, ViewTestCaseMixin, TestCase):
    url_name = "export.members"
    url_expected = "/gestion/export/members"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test(self):
        u1, u2 = self.users["member"], self.users["staff"]
        u1.first_name = "first"
        u1.last_name = "last"
        u1.email = "user@mail.net"
        u1.save()
        u1.profile.phone = "0123456789"
        u1.profile.departement = "Dept"
        u1.profile.save()

        r = self.client.get(self.url)

        self.assertEqual(r.status_code, 200)

        self.assertCSVEqual(
            r,
            [
                [
                    str(u1.pk),
                    "member",
                    "first",
                    "last",
                    "user@mail.net",
                    "0123456789",
                    "1A",
                    "Dept",
                    "normalien",
                ],
                [str(u2.pk), "staff", "", "", "", "", "1A", "", "normalien"],
            ],
        )


class ExportMegaViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
    url_name = "export.mega.all"
    url_expected = "/gestion/export/mega/all"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test(self):
        r = self.client.get(self.url)

        self.assertEqual(r.status_code, 200)
        self.assertCSVEqual(
            r,
            [
                [
                    "u1",
                    "first",
                    "last",
                    "user@mail.net",
                    "0123456789",
                    str(self.u1.pk),
                    "profile.comments",
                    "Comment 1---Comment 2",
                ],
                ["u2", "", "", "", "", str(self.u2.pk), "", ""],
            ],
        )


class ExportMegaOrgasViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
    url_name = "export.mega.orgas"
    url_expected = "/gestion/export/mega/orgas"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test(self):

        r = self.client.get(self.url)

        self.assertEqual(r.status_code, 200)
        self.assertCSVEqual(
            r,
            [
                [
                    "u1",
                    "first",
                    "last",
                    "user@mail.net",
                    "0123456789",
                    str(self.u1.pk),
                    "profile.comments",
                    "Comment 1---Comment 2",
                ]
            ],
        )


class ExportMegaParticipantsViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
    url_name = "export.mega.participants"
    url_expected = "/gestion/export/mega/participants"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test(self):
        r = self.client.get(self.url)

        self.assertEqual(r.status_code, 200)
        self.assertCSVEqual(
            r,
            [["u2", "", "", "", "", str(self.u2.pk), "", ""]],
        )


class ExportMegaRemarksViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
    url_name = "export.mega.remarks"
    url_expected = "/gestion/export/mega/avecremarques"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    def test(self):
        r = self.client.get(self.url)

        self.assertEqual(r.status_code, 200)
        self.assertCSVEqual(
            r,
            [
                [
                    "u1",
                    "first",
                    "last",
                    "user@mail.net",
                    "0123456789",
                    str(self.u1.pk),
                    "profile.comments",
                    "Comment 1",
                ]
            ],
        )


class ClubListViewTests(ViewTestCaseMixin, TestCase):
    url_name = "liste-clubs"
    url_expected = "/gestion/clubs/liste"

    auth_user = "member"
    auth_forbidden = [None, "user"]

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

        self.c1 = Club.objects.create(name="Club1")
        self.c2 = Club.objects.create(name="Club2")

        m = self.users["member"]
        self.c1.membres.add(m)
        self.c1.respos.add(m)

    def test_as_member(self):
        r = self.client.get(self.url)

        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.context["owned_clubs"].get(), self.c1)
        self.assertEqual(r.context["other_clubs"].get(), self.c2)

    def test_as_staff(self):
        u = self.users["staff"]
        c = Client()
        c.force_login(u, backend="django.contrib.auth.backends.ModelBackend")

        r = c.get(self.url)

        self.assertEqual(r.status_code, 200)
        self.assertQuerysetEqual(
            r.context["owned_clubs"],
            map(repr, [self.c1, self.c2]),
            transform=repr,
            ordered=False,
        )


class ClubMembersViewTests(ViewTestCaseMixin, TestCase):
    url_name = "membres-club"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    @property
    def url_kwargs(self):
        return {"name": self.c.name}

    @property
    def url_expected(self):
        return "/gestion/clubs/membres/{}".format(self.c.name)

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

        self.u1 = create_user("u1")
        self.u2 = create_user("u2")

        self.c = Club.objects.create(name="Club")
        self.c.membres.add(self.u1, self.u2)
        self.c.respos.add(self.u1)

    def test_as_staff(self):
        r = self.client.get(self.url)

        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.context["members_no_respo"].get(), self.u2)

    def test_as_respo(self):
        u = self.users["user"]
        self.c.respos.add(u)

        c = Client()
        c.force_login(u, backend="django.contrib.auth.backends.ModelBackend")
        r = c.get(self.url)

        self.assertEqual(r.status_code, 200)


class ClubChangeRespoViewTests(ViewTestCaseMixin, TestCase):
    url_name = "change-respo"

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    @property
    def url_kwargs(self):
        return {"club_name": self.c.name, "user_id": self.users["user"].pk}

    @property
    def url_expected(self):
        return "/gestion/clubs/change_respo/{}/{}".format(
            self.c.name, self.users["user"].pk
        )

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

        self.c = Club.objects.create(name="Club")

    def test(self):
        u = self.users["user"]
        expected_redirect = reverse("membres-club", kwargs={"name": self.c.name})
        self.c.membres.add(u)

        r = self.client.get(self.url)
        self.assertRedirects(r, expected_redirect)
        self.assertIn(u, self.c.respos.all())

        self.client.get(self.url)
        self.assertNotIn(u, self.c.respos.all())


class CalendarViewTests(ViewTestCaseMixin, TestCase):
    url_name = "calendar"
    url_expected = "/gestion/calendar/subscription"

    auth_user = "member"
    auth_forbidden = [None, "user"]

    post_expected_message = Message(
        messages.SUCCESS, "Calendrier mis à jour avec succès."
    )

    def test_get(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)

    def test_post_new(self):
        r = self.client.post(
            self.url,
            {
                "subscribe_to_events": True,
                "subscribe_to_my_shows": True,
                "other_shows": [],
            },
        )

        self.assertEqual(r.status_code, 200)
        self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
        cs = self.users["member"].calendarsubscription
        self.assertTrue(cs.subscribe_to_events)
        self.assertTrue(cs.subscribe_to_my_shows)

    def test_post_edit(self):
        u = self.users["member"]
        token = uuid.uuid4()
        cs = CalendarSubscription.objects.create(token=token, user=u)

        r = self.client.post(self.url, {"other_shows": []})

        self.assertEqual(r.status_code, 200)
        self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
        cs.refresh_from_db()
        self.assertEqual(cs.token, token)
        self.assertFalse(cs.subscribe_to_events)
        self.assertFalse(cs.subscribe_to_my_shows)

    def test_post_other_shows(self):
        t = Tirage.objects.create(ouverture=self.now, fermeture=self.now, active=True)
        location = Salle.objects.create()
        s = t.spectacle_set.create(
            date=self.now, price=3.5, slots=20, location=location, listing=True
        )

        r = self.client.post(self.url, {"other_shows": [str(s.pk)]})

        self.assertEqual(r.status_code, 200)


class CalendarICSViewTests(ICalMixin, ViewTestCaseMixin, TestCase):
    url_name = "calendar.ics"

    auth_user = None
    auth_forbidden = []

    @property
    def url_kwargs(self):
        return {"token": self.token}

    @property
    def url_expected(self):
        return "/gestion/calendar/{}/calendar.ics".format(self.token)

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

        self.token = uuid.uuid4()

        self.t = Tirage.objects.create(
            ouverture=self.now, fermeture=self.now, active=True
        )
        location = Salle.objects.create(name="Location")
        self.s1 = self.t.spectacle_set.create(
            price=1,
            slots=10,
            location=location,
            listing=True,
            title="Spectacle 1",
            date=self.now + timedelta(days=1),
        )
        self.s2 = self.t.spectacle_set.create(
            price=2,
            slots=20,
            location=location,
            listing=True,
            title="Spectacle 2",
            date=self.now + timedelta(days=2),
        )
        self.s3 = self.t.spectacle_set.create(
            price=3,
            slots=30,
            location=location,
            listing=True,
            title="Spectacle 3",
            date=self.now + timedelta(days=3),
        )

    def test(self):
        u = self.users["user"]
        p = u.participant_set.create(tirage=self.t)
        p.attribution_set.create(spectacle=self.s1)

        self.cs = CalendarSubscription.objects.create(
            user=u,
            token=self.token,
            subscribe_to_my_shows=True,
            subscribe_to_events=True,
        )
        self.cs.other_shows.add(self.s2)

        r = self.client.get(self.url)

        def get_dt_from_ical(v):
            return v.dt

        self.assertCalEqual(
            r.content.decode("utf-8"),
            [
                {
                    "summary": "Spectacle 1",
                    "dtstart": (
                        get_dt_from_ical,
                        ((self.now + timedelta(days=1)).replace(microsecond=0)),
                    ),
                    "dtend": (
                        get_dt_from_ical,
                        (
                            (self.now + timedelta(days=1, hours=2)).replace(
                                microsecond=0
                            )
                        ),
                    ),
                    "location": "Location",
                    "uid": "show-{}-{}@example.com".format(self.s1.pk, self.t.pk),
                },
                {
                    "summary": "Spectacle 2",
                    "dtstart": (
                        get_dt_from_ical,
                        ((self.now + timedelta(days=2)).replace(microsecond=0)),
                    ),
                    "dtend": (
                        get_dt_from_ical,
                        (
                            (self.now + timedelta(days=2, hours=2)).replace(
                                microsecond=0
                            )
                        ),
                    ),
                    "location": "Location",
                    "uid": "show-{}-{}@example.com".format(self.s2.pk, self.t.pk),
                },
            ],
        )


class EventViewTests(ViewTestCaseMixin, TestCase):
    url_name = "event.details"
    http_methods = ["GET", "POST"]

    auth_user = "user"
    auth_forbidden = [None]

    post_expected_message = Message(
        messages.SUCCESS,
        (
            "Votre inscription a bien été enregistrée ! Vous pouvez cependant la "
            "modifier jusqu'à la fin des inscriptions."
        ),
    )

    @property
    def url_kwargs(self):
        return {"event_id": self.e.pk}

    @property
    def url_expected(self):
        return "/gestion/event/{}".format(self.e.pk)

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

        self.e = Event.objects.create()

        self.ecf1 = self.e.commentfields.create(name="Comment Field 1")
        self.ecf2 = self.e.commentfields.create(
            name="Comment Field 2", fieldtype="char"
        )

        self.o1 = self.e.options.create(name="Option 1")
        self.o2 = self.e.options.create(name="Option 2", multi_choices=True)

        self.oc1 = self.o1.choices.create(value="O1 - Choice 1")
        self.oc2 = self.o1.choices.create(value="O1 - Choice 2")
        self.oc3 = self.o2.choices.create(value="O2 - Choice 1")
        self.oc4 = self.o2.choices.create(value="O2 - Choice 2")

    def test_get(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)

    def test_post_new(self):
        r = self.client.post(
            self.url,
            {
                "option_{}".format(self.o1.pk): [str(self.oc1.pk)],
                "option_{}".format(self.o2.pk): [str(self.oc3.pk), str(self.oc4.pk)],
            },
        )

        self.assertEqual(r.status_code, 200)
        self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))

        er = self.e.eventregistration_set.get(user=self.users["user"])
        self.assertQuerysetEqual(
            er.options.all(),
            map(repr, [self.oc1, self.oc3, self.oc4]),
            transform=repr,
            ordered=False,
        )
        # TODO: Make the view care about comments.
        # self.assertQuerysetEqual(
        #     er.comments.all(), map(repr, []),
        #     ordered=False,
        # )

    def test_post_edit(self):
        er = self.e.eventregistration_set.create(user=self.users["user"])
        er.options.add(self.oc1, self.oc3, self.oc4)
        er.comments.create(commentfield=self.ecf1, content="Comment 1")

        r = self.client.post(
            self.url,
            {
                "option_{}".format(self.o1.pk): [],
                "option_{}".format(self.o2.pk): [str(self.oc3.pk)],
            },
        )

        self.assertEqual(r.status_code, 200)
        self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))

        er.refresh_from_db()
        self.assertQuerysetEqual(
            er.options.all(), map(repr, [self.oc3]), transform=repr, ordered=False
        )
        # TODO: Make the view care about comments.
        # self.assertQuerysetEqual(
        #     er.comments.all(), map(repr, []),
        #     ordered=False,
        # )


class EventStatusViewTests(ViewTestCaseMixin, TestCase):
    url_name = "event.details.status"

    http_methods = ["GET", "POST"]

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    @property
    def url_kwargs(self):
        return {"event_id": self.e.pk}

    @property
    def url_expected(self):
        return "/gestion/event/{}/status".format(self.e.pk)

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

        self.e = Event.objects.create()

        self.cf1 = self.e.commentfields.create(name="Comment Field 1")
        self.cf2 = self.e.commentfields.create(name="Comment Field 2", fieldtype="char")

        self.o1 = self.e.options.create(name="Option 1")
        self.o2 = self.e.options.create(name="Option 2", multi_choices=True)

        self.oc1 = self.o1.choices.create(value="O1 - Choice 1")
        self.oc2 = self.o1.choices.create(value="O1 - Choice 2")
        self.oc3 = self.o2.choices.create(value="O2 - Choice 1")
        self.oc4 = self.o2.choices.create(value="O2 - Choice 2")

        self.er1 = self.e.eventregistration_set.create(user=self.users["user"])
        self.er1.options.add(self.oc1)
        self.er2 = self.e.eventregistration_set.create(user=self.users["member"])

    def _get_oc_filter_name(self, oc):
        return "option_{}_choice_{}".format(oc.event_option.pk, oc.pk)

    def _test_filters(self, filters, expected):
        r = self.client.post(
            self.url, {self._get_oc_filter_name(oc): v for oc, v in filters}
        )

        self.assertEqual(r.status_code, 200)
        self.assertQuerysetEqual(
            r.context["user_choices"],
            map(repr, expected),
            transform=repr,
            ordered=False,
        )

    def test_filter_none(self):
        self._test_filters([(self.oc1, "none")], [self.er1, self.er2])

    def test_filter_yes(self):
        self._test_filters([(self.oc1, "yes")], [self.er1])

    def test_filter_no(self):
        self._test_filters([(self.oc1, "no")], [self.er2])


class SurveyViewTests(ViewTestCaseMixin, TestCase):
    url_name = "survey.details"
    http_methods = ["GET", "POST"]

    auth_user = "user"
    auth_forbidden = [None]

    post_expected_message = Message(
        messages.SUCCESS,
        (
            "Votre réponse a bien été enregistrée ! Vous pouvez cependant la "
            "modifier jusqu'à la fin du sondage."
        ),
    )

    @property
    def url_kwargs(self):
        return {"survey_id": self.s.pk}

    @property
    def url_expected(self):
        return "/gestion/survey/{}".format(self.s.pk)

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

        self.s = Survey.objects.create(title="Title")

        self.q1 = self.s.questions.create(question="Question 1 ?")
        self.q2 = self.s.questions.create(question="Question 2 ?", multi_answers=True)

        self.qa1 = self.q1.answers.create(answer="Q1 - Answer 1")
        self.qa2 = self.q1.answers.create(answer="Q1 - Answer 2")
        self.qa3 = self.q2.answers.create(answer="Q2 - Answer 1")
        self.qa4 = self.q2.answers.create(answer="Q2 - Answer 2")

    def test_get(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)

    def test_post_new(self):
        r = self.client.post(
            self.url,
            {
                "question_{}".format(self.q1.pk): [str(self.qa1.pk)],
                "question_{}".format(self.q2.pk): [str(self.qa3.pk), str(self.qa4.pk)],
            },
        )

        self.assertEqual(r.status_code, 200)
        self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))

        a = self.s.surveyanswer_set.get(user=self.users["user"])
        self.assertQuerysetEqual(
            a.answers.all(),
            map(repr, [self.qa1, self.qa3, self.qa4]),
            transform=repr,
            ordered=False,
        )

    def test_post_edit(self):
        a = self.s.surveyanswer_set.create(user=self.users["user"])
        a.answers.add(self.qa1, self.qa1, self.qa4)

        r = self.client.post(
            self.url,
            {
                "question_{}".format(self.q1.pk): [],
                "question_{}".format(self.q2.pk): [str(self.qa3.pk)],
            },
        )

        self.assertEqual(r.status_code, 200)
        self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))

        a.refresh_from_db()
        self.assertQuerysetEqual(
            a.answers.all(), map(repr, [self.qa3]), transform=repr, ordered=False
        )

    def test_post_delete(self):
        a = self.s.surveyanswer_set.create(user=self.users["user"])
        a.answers.add(self.qa1, self.qa4)

        r = self.client.post(self.url, {"delete": "1"})

        self.assertEqual(r.status_code, 200)
        expected_message = Message(
            messages.SUCCESS, "Votre réponse a bien été supprimée"
        )
        self.assertIn(expected_message, get_messages(r.wsgi_request))

        with self.assertRaises(SurveyAnswer.DoesNotExist):
            a.refresh_from_db()

    def test_forbidden_closed(self):
        self.s.survey_open = False
        self.s.save()

        r = self.client.get(self.url)

        self.assertNotEqual(r.status_code, 200)

    def test_forbidden_old(self):
        self.s.old = True
        self.s.save()

        r = self.client.get(self.url)

        self.assertNotEqual(r.status_code, 200)


class SurveyStatusViewTests(ViewTestCaseMixin, TestCase):
    url_name = "survey.details.status"

    http_methods = ["GET", "POST"]

    auth_user = "staff"
    auth_forbidden = [None, "user", "member"]

    @property
    def url_kwargs(self):
        return {"survey_id": self.s.pk}

    @property
    def url_expected(self):
        return "/gestion/survey/{}/status".format(self.s.pk)

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

        self.s = Survey.objects.create(title="Title")

        self.q1 = self.s.questions.create(question="Question 1 ?")
        self.q2 = self.s.questions.create(question="Question 2 ?", multi_answers=True)

        self.qa1 = self.q1.answers.create(answer="Q1 - Answer 1")
        self.qa2 = self.q1.answers.create(answer="Q1 - Answer 2")
        self.qa3 = self.q2.answers.create(answer="Q2 - Answer 1")
        self.qa4 = self.q2.answers.create(answer="Q2 - Answer 2")

        self.a1 = self.s.surveyanswer_set.create(user=self.users["user"])
        self.a1.answers.add(self.qa1)
        self.a2 = self.s.surveyanswer_set.create(user=self.users["member"])

    def test_get(self):
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 200)

    def _get_qa_filter_name(self, qa):
        return "question_{}_answer_{}".format(qa.survey_question.pk, qa.pk)

    def _test_filters(self, filters, expected):
        r = self.client.post(
            self.url, {self._get_qa_filter_name(qa): v for qa, v in filters}
        )

        self.assertEqual(r.status_code, 200)
        self.assertQuerysetEqual(
            r.context["user_answers"],
            map(repr, expected),
            transform=repr,
            ordered=False,
        )

    def test_filter_none(self):
        self._test_filters([(self.qa1, "none")], [self.a1, self.a2])

    def test_filter_yes(self):
        self._test_filters([(self.qa1, "yes")], [self.a1])

    def test_filter_no(self):
        self._test_filters([(self.qa1, "no")], [self.a2])
