# Copyright 2010-2017, The University of Melbourne
# Copyright 2010-2017, Brian May
#
# This file is part of Karaage.
#
# Karaage is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Karaage is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Karaage  If not, see <http://www.gnu.org/licenses/>.

import django
import pytest
from captcha.models import CaptchaStore
from django.conf import settings
from django.core import mail
from django.core.management import call_command
from django.test import TestCase
from django.urls import reverse

from karaage.people.models import Person
from karaage.tests import fixtures

from ..models import Applicant, Application, ProjectApplication
from .fixtures import (
    ExistingProjectApplicationFactory,
    NewProjectApplicationFactory,
)


def set_admin():
    settings.ADMIN_IGNORED = False


def set_no_admin():
    settings.ADMIN_IGNORED = True


@pytest.mark.django_db
class UserApplicationTestCase(TestCase):
    def setUp(self):
        call_command("loaddata", "test_karaage", **{"verbosity": 0})

    def tearDown(self):
        set_admin()

    def test_register_account(self):
        if django.VERSION >= (1, 9):
            url_prefix = ""
        else:
            url_prefix = "http://testserver"

        set_no_admin()

        with self.assertRaises(Person.DoesNotExist):
            Person.objects.get(username="jimbob")

        self.assertEqual(len(mail.outbox), 0)
        response = self.client.get(reverse("kg_application_new"))
        self.assertEqual(response.status_code, 200)
        a = response.content.find(b'name="captcha_0" value="') + 24
        b = a + 40
        hash_ = response.content[a:b].decode("ascii")

        captcha_text = CaptchaStore.objects.get(hashkey=hash_).response

        # OPEN APPLICATION
        form_data = {
            "email": "jim.bob@example.com",
            "captcha_0": hash_,
            "captcha_1": captcha_text,
        }

        response = self.client.post(reverse("kg_application_new"), form_data, follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.redirect_chain[0][0], url_prefix + reverse("index"))
        token = Application.objects.get().secret_token
        self.assertEqual(len(mail.outbox), 1)
        self.assertTrue(mail.outbox[0].subject.startswith("TestOrg invitation"))
        self.assertEqual(mail.outbox[0].from_email, settings.ACCOUNTS_EMAIL)
        self.assertEqual(mail.outbox[0].to[0], "jim.bob@example.com")

        # SUBMIT APPLICANT DETAILS
        form_data = {
            "title": "Mr",
            "short_name": "Jim",
            "full_name": "Jim Bob",
            "position": "Researcher",
            "institute": 1,
            "department": "Maths",
            "telephone": "4444444",
            "username": "jimbob",
            "next": "string",
        }

        response = self.client.post(
            reverse("kg_application_unauthenticated", args=[token, "O", "applicant"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0],
            url_prefix + reverse("kg_application_unauthenticated", args=[token, "O", "project"]),
        )

        # SUBMIT PROJECT DETAILS
        form_data = {
            "application_type": "U",
            "project": 1,
            "aup": True,
            "make_leader": False,
            "additional_req": "Meow",
            "needs_account": False,
            "submit": "string",
        }

        response = self.client.post(
            reverse("kg_application_unauthenticated", args=[token, "O", "project"]), form_data, follow=True
        )
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_unauthenticated", args=[token, "L"])
        )
        self.assertEqual(response.status_code, 200)
        applicant = Applicant.objects.get(username="jimbob")
        application = applicant.application
        self.assertEqual(application.state, ProjectApplication.WAITING_FOR_LEADER)
        self.assertEqual(len(mail.outbox), 2)
        self.assertTrue(mail.outbox[1].subject.startswith("TestOrg request"))
        self.assertEqual(mail.outbox[1].from_email, settings.ACCOUNTS_EMAIL)
        self.assertEqual(mail.outbox[1].to[0], "leader@example.com")

        # LEADER LOGS IN TO APPROVE
        logged_in = self.client.login(username="kgtestuser1", password="aq12ws")
        self.assertEqual(logged_in, True)

        # LEADER GET DETAILS
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "L"]))
        self.assertEqual(response.status_code, 200)

        # LEADER GET DECLINE PAGE
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "L", "cancel"]))
        self.assertEqual(response.status_code, 200)

        # LEADER GET APPROVE PAGE
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "L", "approve"]))
        self.assertEqual(response.status_code, 200)

        # LEADER APPROVE
        form_data = {
            "make_leader": False,
            "additional_req": "Meow",
            "needs_account": False,
            "approve": True,
        }
        response = self.client.post(
            reverse("kg_application_detail", args=[application.pk, "L", "approve"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_detail", args=[application.pk, "K"])
        )
        application = Application.objects.get(pk=application.id)
        self.assertEqual(application.state, Application.WAITING_FOR_ADMIN)
        self.assertEqual(len(mail.outbox), 3)
        self.client.logout()

        # ADMIN LOGS IN TO APPROVE
        set_admin()
        logged_in = self.client.login(username="kgsuper", password="aq12ws")
        self.assertEqual(logged_in, True)

        # ADMIN GET DETAILS
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "K"]))
        self.assertEqual(response.status_code, 200)

        # ADMIN GET DECLINE PAGE
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "K", "cancel"]))
        self.assertEqual(response.status_code, 200)

        # ADMIN GET APPROVE PAGE
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "K", "approve"]))
        self.assertEqual(response.status_code, 200)

        # ADMIN APPROVE
        form_data = {
            "make_leader": False,
            "additional_req": "Woof",
            "needs_account": False,
            "approve": True,
        }
        response = self.client.post(
            reverse("kg_application_detail", args=[application.pk, "K", "approve"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_detail", args=[application.pk, "P"])
        )
        application = Application.objects.get(pk=application.id)
        self.assertEqual(application.state, ProjectApplication.PASSWORD)
        self.assertEqual(len(mail.outbox), 4)
        self.client.logout()
        set_no_admin()

        # APPLICANT GET PASSWORD
        response = self.client.get(reverse("kg_application_unauthenticated", args=[token, "P"]))
        self.assertEqual(response.status_code, 200)

        # APPLICANT SET PASSWORD
        form_data = {
            "new_password1": "Exaiquouxei0",
            "new_password2": "Exaiquouxei0",
            "submit": "string",
        }

        response = self.client.post(
            reverse("kg_application_unauthenticated", args=[token, "P"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_unauthenticated", args=[token, "C"])
        )

        # APPLICANT GET COMPLETE
        response = self.client.get(reverse("kg_application_unauthenticated", args=[token, "C"]))
        self.assertEqual(response.status_code, 200)

        # APPLICANT SET ARCHIVE
        form_data = {
            "archive": "string",
        }
        response = self.client.post(
            reverse("kg_application_unauthenticated", args=[token, "C"]), form_data, follow=False
        )
        # applicant not allowed to do this
        self.assertEqual(response.status_code, 400)
        self.client.logout()

        # ADMIN ARCHIVE
        set_admin()
        logged_in = self.client.login(username="kgsuper", password="aq12ws")
        self.assertEqual(logged_in, True)
        response = self.client.post(
            reverse("kg_application_detail", args=[application.pk, "C"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_detail", args=[application.pk, "A"])
        )
        self.client.logout()
        set_no_admin()

        # APPLICANT GET ARCHIVE
        response = self.client.get(reverse("kg_application_unauthenticated", args=[token, "A"]))
        self.assertEqual(response.status_code, 200)

        person = Person.objects.get(username="jimbob")
        self.assertEqual(person.title, "Mr")
        self.assertEqual(person.short_name, "Jim")
        self.assertEqual(person.full_name, "Jim Bob")
        self.assertEqual(person.institute.name, "Example")
        self.assertEqual(person.email, "jim.bob@example.com")
        self.assertEqual(person.telephone, "4444444")
        self.assertEqual(person.approved_by.username, "kgsuper")
        self.assertEqual(person.account_set.count(), 0)
        self.assertIsNotNone(person.date_approved)


@pytest.mark.django_db
class ProjectApplicationTestCase(TestCase):
    def setUp(self):
        call_command("loaddata", "test_karaage", **{"verbosity": 0})

    def tearDown(self):
        set_admin()

    def test_register_account(self):
        if django.VERSION >= (1, 9):
            url_prefix = ""
        else:
            url_prefix = "http://testserver"

        set_no_admin()

        with self.assertRaises(Person.DoesNotExist):
            Person.objects.get(username="jimbob")

        self.assertEqual(len(mail.outbox), 0)
        response = self.client.get(reverse("kg_application_new"))
        self.assertEqual(response.status_code, 200)
        a = response.content.find(b'name="captcha_0" value="') + 24
        b = a + 40
        hash_ = response.content[a:b].decode("ascii")

        captcha_text = CaptchaStore.objects.get(hashkey=hash_).response

        # OPEN APPLICATION
        form_data = {
            "email": "jim.bob@example.com",
            "captcha_0": hash_,
            "captcha_1": captcha_text,
        }

        response = self.client.post(reverse("kg_application_new"), form_data, follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.redirect_chain[0][0], url_prefix + reverse("index"))
        token = Application.objects.get().secret_token
        self.assertEqual(len(mail.outbox), 1)
        self.assertTrue(mail.outbox[0].subject.startswith("TestOrg invitation"))
        self.assertEqual(mail.outbox[0].from_email, settings.ACCOUNTS_EMAIL)
        self.assertEqual(mail.outbox[0].to[0], "jim.bob@example.com")

        # SUBMIT APPLICANT DETAILS
        form_data = {
            "title": "Mr",
            "short_name": "Jim",
            "full_name": "Jim Bob",
            "position": "Researcher",
            "institute": 1,
            "department": "Maths",
            "telephone": "4444444",
            "username": "jimbob",
            "next": "string",
        }

        response = self.client.post(
            reverse("kg_application_unauthenticated", args=[token, "O", "applicant"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0],
            url_prefix + reverse("kg_application_unauthenticated", args=[token, "O", "project"]),
        )

        # SUBMIT PROJECT DETAILS
        form_data = {
            "application_type": "P",
            "name": "NewProject1",
            "description": "I like chocoloate.",
            "aup": True,
            "additional_req": "Meow",
            "needs_account": False,
            "machine_categories": [1],
            "submit": "string",
        }

        response = self.client.post(
            reverse("kg_application_unauthenticated", args=[token, "O", "project"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_unauthenticated", args=[token, "D"])
        )
        applicant = Applicant.objects.get(username="jimbob")
        application = applicant.application
        self.assertEqual(application.state, ProjectApplication.WAITING_FOR_DELEGATE)
        self.assertEqual(len(mail.outbox), 2)
        self.assertTrue(mail.outbox[1].subject.startswith("TestOrg request"))
        self.assertEqual(mail.outbox[1].from_email, settings.ACCOUNTS_EMAIL)
        self.assertEqual(mail.outbox[1].to[0], "leader@example.com")

        # DELEGATE LOGS IN TO APPROVE
        logged_in = self.client.login(username="kgtestuser1", password="aq12ws")
        self.assertEqual(logged_in, True)

        # DELEGATE GET DETAILS
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "D"]))
        self.assertEqual(response.status_code, 200)

        # DELEGATE GET DECLINE PAGE
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "D", "cancel"]))
        self.assertEqual(response.status_code, 200)

        # DELEGATE GET APPROVE PAGE
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "D", "approve"]))
        self.assertEqual(response.status_code, 200)

        # DELEGATE APPROVE
        form_data = {
            "additional_req": "Meow",
            "needs_account": False,
            "machine_categories": [1],
            "approve": True,
        }
        response = self.client.post(
            reverse("kg_application_detail", args=[application.pk, "D", "approve"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_detail", args=[application.pk, "K"])
        )
        application = Application.objects.get(pk=application.id)
        self.assertEqual(application.state, Application.WAITING_FOR_ADMIN)
        self.assertEqual(len(mail.outbox), 3)
        self.client.logout()

        # ADMIN LOGS IN TO APPROVE
        set_admin()
        logged_in = self.client.login(username="kgsuper", password="aq12ws")
        self.assertEqual(logged_in, True)

        # ADMIN GET DETAILS
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "K"]))
        self.assertEqual(response.status_code, 200)

        # ADMIN GET DECLINE PAGE
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "K", "cancel"]))
        self.assertEqual(response.status_code, 200)

        # ADMIN GET APPROVE PAGE
        response = self.client.get(reverse("kg_application_detail", args=[application.pk, "K", "approve"]))
        self.assertEqual(response.status_code, 200)

        # ADMIN APPROVE
        form_data = {
            "additional_req": "Woof",
            "needs_account": False,
            "machine_categories": [1],
            "approve": True,
        }
        response = self.client.post(
            reverse("kg_application_detail", args=[application.pk, "K", "approve"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_detail", args=[application.pk, "P"])
        )
        application = Application.objects.get(pk=application.id)
        self.assertEqual(application.state, ProjectApplication.PASSWORD)
        self.assertEqual(len(mail.outbox), 4)
        self.client.logout()
        set_no_admin()

        # APPLICANT GET PASSWORD
        response = self.client.get(reverse("kg_application_unauthenticated", args=[token, "P"]))
        self.assertEqual(response.status_code, 200)

        # APPLICANT SET PASSWORD
        form_data = {
            "new_password1": "Exaiquouxei0",
            "new_password2": "Exaiquouxei0",
            "submit": "string",
        }

        response = self.client.post(
            reverse("kg_application_unauthenticated", args=[token, "P"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_unauthenticated", args=[token, "C"])
        )

        # APPLICANT GET COMPLETE
        response = self.client.get(reverse("kg_application_unauthenticated", args=[token, "C"]))
        self.assertEqual(response.status_code, 200)

        # APPLICANT SET ARCHIVE
        form_data = {
            "archive": "string",
        }
        response = self.client.post(
            reverse("kg_application_unauthenticated", args=[token, "C"]), form_data, follow=False
        )
        # applicant not allowed to do this
        self.assertEqual(response.status_code, 400)
        self.client.logout()

        # ADMIN ARCHIVE
        set_admin()
        logged_in = self.client.login(username="kgsuper", password="aq12ws")
        self.assertEqual(logged_in, True)
        response = self.client.post(
            reverse("kg_application_detail", args=[application.pk, "C"]), form_data, follow=True
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.redirect_chain[0][0], url_prefix + reverse("kg_application_detail", args=[application.pk, "A"])
        )
        self.client.logout()
        set_no_admin()

        # APPLICANT GET ARCHIVE
        response = self.client.get(reverse("kg_application_unauthenticated", args=[token, "A"]))
        self.assertEqual(response.status_code, 200)

        person = Person.objects.get(username="jimbob")
        self.assertEqual(person.title, "Mr")
        self.assertEqual(person.short_name, "Jim")
        self.assertEqual(person.full_name, "Jim Bob")
        self.assertEqual(person.institute.name, "Example")
        self.assertEqual(person.telephone, "4444444")
        self.assertEqual(person.approved_by.username, "kgsuper")
        self.assertEqual(person.account_set.count(), 0)
        self.assertIsNotNone(person.date_approved)

    def _test_project_approval(self, application):
        admin = fixtures.PersonFactory(username="admin")
        admin.is_admin = True
        admin.save()
        application.approve(admin)

    def _test_project_make_leader(self, application):
        self._test_project_approval(application)
        if application.make_leader:
            self.assertIn(application.existing_person, application.project.leaders.all())
        else:
            self.assertNotIn(application.existing_person, application.project.leaders.all())

    def test_new_project_make_leader(self, make_leader=True):
        application = ExistingProjectApplicationFactory()
        application.make_leader = make_leader
        self._test_project_make_leader(application)

    def test_new_project_make_leader_false(self):
        self.test_new_project_make_leader(make_leader=False)

    def test_existing_project_make_leader(self, make_leader=True):
        application = NewProjectApplicationFactory()
        application.make_leader = make_leader
        self._test_project_make_leader(application)

    def test_existing_project_make_leader_false(self):
        self.test_existing_project_make_leader(make_leader=False)
