from unittest import mock

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.test import Client, TestCase
from django.urls import reverse

from events.models import (
    Event,
    ExtraField,
    ExtraFieldContent,
    Option,
    OptionChoice,
    Registration,
)
from shared.tests.mixins import CSVResponseMixin

User = get_user_model()


def make_user(name):
    return User.objects.create_user(username=name, password=name)


def make_staff_user(name):
    view_event_perm = Permission.objects.get(
        codename="view_event",
        content_type__app_label="events",
    )
    user = make_user(name)
    user.user_permissions.add(view_event_perm)
    return user


class MessagePatch:
    def setUp(self):
        # Signals handlers on login/logout send messages.
        # Due to the way the Django' test Client performs login, this raise an
        # error. As workaround, we mock the Django' messages module.
        patcher_messages = mock.patch("gestioncof.signals.messages")
        patcher_messages.start()
        self.addCleanup(patcher_messages.stop)


class CSVExportAccessTest(MessagePatch, TestCase):
    def setUp(self):
        super().setUp()

        self.staff = make_staff_user("staff")
        self.u1 = make_user("toto")
        self.event = Event.objects.create(title="test_event", location="somewhere")
        self.url = reverse("events:csv-participants", args=[self.event.id])

    def test_get(self):
        client = Client()
        client.force_login(
            self.staff, backend="django.contrib.auth.backends.ModelBackend"
        )
        r = client.get(self.url)
        self.assertEqual(r.status_code, 200)

    def test_anonymous(self):
        client = Client()
        r = client.get(self.url)
        login_url = "{}?next={}".format(reverse(settings.LOGIN_URL), self.url)
        self.assertRedirects(r, login_url, fetch_redirect_response=False)

    def test_unauthorised(self):
        client = Client()
        client.force_login(self.u1, backend="django.contrib.auth.backends.ModelBackend")
        r = client.get(self.url)
        self.assertEqual(r.status_code, 403)


class CSVExportContentTest(MessagePatch, CSVResponseMixin, TestCase):
    def setUp(self):
        super().setUp()

        self.event = Event.objects.create(title="test_event", location="somewhere")
        self.url = reverse("events:csv-participants", args=[self.event.id])

        self.u1 = User.objects.create_user(
            username="toto_foo", first_name="toto", last_name="foo", email="toto@a.b"
        )
        self.u2 = User.objects.create_user(
            username="titi_bar", first_name="titi", last_name="bar", email="titi@a.b"
        )
        self.staff = make_staff_user("staff")
        self.client = Client()
        self.client.force_login(
            self.staff, backend="django.contrib.auth.backends.ModelBackend"
        )

    def test_simple_event(self):
        self.event.subscribers.set([self.u1, self.u2])

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

        self.assertCSVEqual(
            response,
            [
                {
                    "username": "toto_foo",
                    "prénom": "toto",
                    "nom de famille": "foo",
                    "email": "toto@a.b",
                },
                {
                    "username": "titi_bar",
                    "prénom": "titi",
                    "nom de famille": "bar",
                    "email": "titi@a.b",
                },
            ],
        )

    def test_complex_event(self):
        registration = Registration.objects.create(event=self.event, user=self.u1)
        # Set up some options
        option1 = Option.objects.create(
            event=self.event, name="abc", multi_choices=False
        )
        option2 = Option.objects.create(
            event=self.event, name="def", multi_choices=True
        )
        OptionChoice.objects.bulk_create(
            [
                OptionChoice(option=option1, choice="a"),
                OptionChoice(option=option1, choice="b"),
                OptionChoice(option=option1, choice="c"),
                OptionChoice(option=option2, choice="d"),
                OptionChoice(option=option2, choice="e"),
                OptionChoice(option=option2, choice="f"),
            ]
        )
        registration.options_choices.set(
            OptionChoice.objects.filter(choice__in=["d", "f"])
        )
        registration.options_choices.add(OptionChoice.objects.get(choice="a"))
        # And an extra field
        field = ExtraField.objects.create(event=self.event, name="remarks")
        ExtraFieldContent.objects.create(
            field=field, registration=registration, content="hello"
        )

        response = self.client.get(self.url)
        self.assertCSVEqual(
            response,
            [
                {
                    "username": "toto_foo",
                    "prénom": "toto",
                    "nom de famille": "foo",
                    "email": "toto@a.b",
                    "abc": "a",
                    "def": "d & f",
                    "remarks": "hello",
                }
            ],
        )
