import datetime
import logging

from django.contrib.messages import get_messages
from django.urls import reverse
from django.utils import timezone

from web.forms import VisitDetailForm, VisitAddForm
from web.models import Visit, MailTemplate
from web.models.constants import MAIL_TEMPLATE_CONTEXT_VISIT
from web.tests import LoggedInTestCase
from web.tests.functions import create_study_subject, create_visit, create_appointment, create_appointment_type, \
    create_language, get_resource_path, format_form_field
from web.views.notifications import get_today_midnight_date

logger = logging.getLogger(__name__)


class VisitViewTests(LoggedInTestCase):
    def test_visit_details_request(self):
        visit = create_visit()
        create_appointment(visit)
        visit.subject.subject.address = "SUBJECT_ADDRESS_DATA"
        visit.subject.subject.save()
        visit.subject.comments = "STUDY_SUBJECT_COMMENTS_DATA"
        visit.subject.save()

        response = self.client.get(reverse('web.views.visit_details', kwargs={'visit_id': visit.id}))
        self.assertEqual(response.status_code, 200)
        self.assertTrue(visit.subject.comments.encode('utf8') in response.content,
                        msg="No study subject data in rendered response")
        self.assertTrue(visit.subject.subject.address.encode('utf8') in response.content,
                        msg="No subject data in rendered response")

    def test_save_visit_details(self):
        visit = create_visit()
        create_appointment(visit)

        form_data = self.create_visit_detail_form_data(visit)

        response = self.client.post(reverse('web.views.visit_details', kwargs={'visit_id': visit.id}), data=form_data)
        self.assertEqual(response.status_code, 200)
        self.assertNotContains(response, "error")

    @staticmethod
    def create_visit_detail_form_data(visit):
        visit_detail_form = VisitDetailForm(instance=visit)
        form_data = {}
        for key, value in list(visit_detail_form.initial.items()):
            form_data[key] = format_form_field(value)
        return form_data

    def test_render_visit_details_with_mail_templates(self):
        language = create_language(name="German")
        template_name = "german_template"
        template_file = get_resource_path('upcoming_appointment_FR.docx')
        visit = create_visit()
        visit.subject.subject.default_written_communication_language = language
        visit.subject.subject.save()

        MailTemplate(name=template_name, language=language, context=MAIL_TEMPLATE_CONTEXT_VISIT,
                     template_file=template_file).save()

        form_data = self.create_visit_detail_form_data(visit)

        response = self.client.post(reverse('web.views.visit_details', kwargs={'visit_id': visit.id}), data=form_data)

        self.assertEqual(response.status_code, 200)
        self.assertNotContains(response, "error")
        self.assertTrue(template_name.encode('utf8') in response.content)

    def test_render_add_visit(self):
        study_subject = create_study_subject()

        response = self.client.get(reverse('web.views.visit_add', kwargs={'subject_id': study_subject.id}))
        self.assertEqual(response.status_code, 200)

    def test_render_add_visit_for_deceased(self):
        study_subject = create_study_subject()
        study_subject.subject.dead=True
        study_subject.subject.save()

        response = self.client.get(reverse('web.views.visit_add', kwargs={'subject_id': study_subject.id}), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Visit cannot be added")

    def test_render_add_visit_for_subject_with_unfinished_visit(self):
        study_subject = create_study_subject()
        create_visit(study_subject)

        response = self.client.get(reverse('web.views.visit_add', kwargs={'subject_id': study_subject.id}), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "subject has unfinished visits")

    def test_save_add_visit(self):
        visit_count = Visit.objects.all().count()
        subject = create_study_subject()

        form_data = self.create_visit_detail_form_data(None)

        form_data["datetime_begin"] = "2017-01-01"
        form_data["datetime_end"] = "2017-04-01"
        form_data["subject"] = subject.id
        form_data["post_mail_sent"] = False

        form = VisitAddForm(data=form_data)
        self.assertTrue(form.is_valid())

        response = self.client.post(reverse('web.views.visit_add', kwargs={'subject_id': subject.id}),
                                    data=form_data,
                                    follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertFalse("error".encode('utf8') in response.content)

        visit_count_new = Visit.objects.all().count()

        self.assertEqual(visit_count + 1, visit_count_new)

    def test_mark_as_finished(self):
        visit = create_visit()

        self.assertFalse(visit.is_finished)

        response = self.client.get(reverse('web.views.visit_mark', args=[visit.id, "finished"]), follow=True)
        self.assertEqual(response.status_code, 200)

        new_visit = Visit.objects.get(id=visit.id)
        self.assertTrue(new_visit.is_finished)
        self.assertEqual(2, Visit.objects.count())

    def test_unfinish_visit_without_permissions(self):
        visit = create_visit()

        self.assertFalse(visit.is_finished)

        response = self.client.get(reverse('web.views.visit_mark', args=[visit.id, "finished"]))
        self.assertEqual(response.status_code, 302)

        new_visit = Visit.objects.get(id=visit.id)
        self.assertTrue(new_visit.is_finished)
        self.assertEqual(2, Visit.objects.count())

        response = self.client.get(reverse('web.views.visit_unfinish', args=[visit.id]), follow=True)
        messages = list(get_messages(response.wsgi_request))
        self.assertEqual(len(messages), 1)
        self.assertEqual(str(messages[0]), 'You are not authorized to view this page or perform this action. '
                                           'Request permissions to the system administrator.')
        new_visit = Visit.objects.get(id=visit.id)
        self.assertEqual(2, Visit.objects.count())
        self.assertTrue(new_visit.is_finished)

    def test_unfinish_visit_with_future_visit_without_appointments(self):
        self.login_as_admin()
        visit = create_visit()

        self.assertFalse(visit.is_finished)

        response = self.client.get(reverse('web.views.visit_mark', args=[visit.id, "finished"]), follow=True)
        self.assertEqual(response.status_code, 200)

        new_visit = Visit.objects.get(id=visit.id)
        self.assertTrue(new_visit.is_finished)
        self.assertEqual(2, Visit.objects.count())

        response = self.client.get(reverse('web.views.visit_unfinish', args=[visit.id]), follow=True)
        self.assertEqual(response.status_code, 200)

        messages = list(get_messages(response.wsgi_request))
        self.assertEqual(len(messages), 1)
        self.assertEqual(str(messages[0]), 'Visit has been unfinished.')
        new_visit = Visit.objects.get(id=visit.id)
        self.assertEqual(1, Visit.objects.count())
        self.assertFalse(new_visit.is_finished)

    def test_unfinish_visit_with_future_visit_with_appointments(self):
        self.login_as_admin()
        visit = create_visit()

        self.assertFalse(visit.is_finished)

        response = self.client.get(reverse('web.views.visit_mark', args=[visit.id, "finished"]))
        self.assertEqual(response.status_code, 302)

        new_visit = Visit.objects.get(id=visit.id)
        self.assertTrue(new_visit.is_finished)
        self.assertEqual(2, Visit.objects.count())

        create_appointment(new_visit.next_visit)

        response = self.client.get(reverse('web.views.visit_unfinish', args=[visit.id]), follow=True)
        messages = list(get_messages(response.wsgi_request))
        self.assertEqual(len(messages), 1)
        self.assertEqual(str(messages[0]), "Visit can't be unfinished. The next visit has appointments.")
        original_visit = Visit.objects.get(id=visit.id)
        self.assertTrue(original_visit.is_finished)

    def test_unfinish_visit_with_two_future_visits(self):
        self.login_as_admin()
        visit = create_visit()
        self.assertFalse(visit.is_finished)
        response = self.client.get(reverse('web.views.visit_mark', args=[visit.id, "finished"]))
        self.assertEqual(response.status_code, 302)

        new_visit = Visit.objects.get(id=visit.id)
        self.assertTrue(new_visit.is_finished)
        self.assertEqual(2, Visit.objects.count())

        next_visit = new_visit.next_visit
        response = self.client.get(reverse('web.views.visit_mark', args=[next_visit.id, "finished"]))
        self.assertEqual(response.status_code, 302)
        next_visit = Visit.objects.get(id=next_visit.id)
        self.assertTrue(new_visit.is_finished)
        self.assertEqual(3, Visit.objects.count())

        #try to unfinish the original visit
        response = self.client.get(reverse('web.views.visit_unfinish', args=[visit.id]), follow=True)
        messages = list(get_messages(response.wsgi_request))
        self.assertEqual(len(messages), 1)
        self.assertEqual(str(messages[0]), "Visit can't be unfinished. "
                                           "Only visits with one inmediate future visit "
                                           "(without appointments) can be unfinished.")
        self.assertEqual(3, Visit.objects.count())
        original_visit = Visit.objects.get(id=visit.id)
        self.assertTrue(original_visit.is_finished)

    def test_unfinish_visit_which_is_not_finished(self):
        self.login_as_admin()
        visit = create_visit()

        self.assertFalse(visit.is_finished)

        response = self.client.get(reverse('web.views.visit_unfinish', args=[visit.id]), follow=True)
        messages = list(get_messages(response.wsgi_request))
        self.assertEqual(len(messages), 1)
        self.assertEqual(str(messages[0]), "The visit is not finished.")

    def test_mark_as_finished_with_study_no_follow_up_rule(self):
        visit = create_visit()
        visit.subject.type.auto_create_follow_up = False
        visit.subject.type.save()

        self.assertFalse(visit.is_finished)

        response = self.client.get(reverse('web.views.visit_mark', args=[visit.id, "finished"]))
        self.assertEqual(response.status_code, 302)

        new_visit = Visit.objects.get(id=visit.id)
        self.assertTrue(new_visit.is_finished)
        self.assertEqual(1, Visit.objects.count())

    def test_visit_list(self):
        create_visit()

        response = self.client.get(reverse('web.views.visits'))
        self.assertEqual(response.status_code, 200)

    def test_visit_with_missing_appointment_list(self):
        visit = create_visit()
        visit.appointment_types.add(create_appointment_type())
        visit.save()

        response = self.client.get(reverse('web.views.visits_with_missing_appointments'))
        self.assertEqual(response.status_code, 200)

    def test_approaching_visits_without_appointments(self):
        visit = create_visit()
        visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=2)
        visit.save()

        response = self.client.get(reverse("web.views.approaching_visits_without_appointments"))
        self.assertEqual(response.status_code, 200)

    def test_approaching_visits_for_mail_contact(self):
        visit = create_visit()
        visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=100)
        visit.save()

        response = self.client.get(reverse("web.views.approaching_visits_for_mail_contact"))
        self.assertEqual(response.status_code, 200)

    def test_exceeded_visits(self):
        visit = create_visit()
        visit.datetime_end = timezone.now().replace(year=2011)
        visit.save()

        response = self.client.get(reverse("web.views.exceeded_visits"))
        self.assertEqual(response.status_code, 200)

    def test_unfinished_visits(self):
        visit = create_visit()
        visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=-10)
        visit.save()
        response = self.client.get(reverse("web.views.unfinished_visits"))
        self.assertEqual(response.status_code, 200)