#!/usr/bin/env python
# -*- coding: utf8 -*-
import json
import dateutil.parser
import smtplib

import mock
from mock import mock_open, patch
import requests

from django.test import TestCase
from paying_for_college.models import School, Contact, Program, Alias, Nickname
from paying_for_college.models import ConstantCap, ConstantRate, Disclosure
from paying_for_college.models import Notification, print_vals
from paying_for_college.models import get_region, make_divisible_by_6


class MakeDivisibleTest(TestCase):

    def test_make_divisible(self):
        test_value = ''
        self.assertTrue(make_divisible_by_6(test_value) == test_value)
        test_value = 0
        self.assertTrue(make_divisible_by_6(test_value) == test_value)
        test_value = 1
        self.assertTrue(make_divisible_by_6(test_value) == 6)
        test_value = 3
        self.assertTrue(make_divisible_by_6(test_value) == 6)
        test_value = 45
        self.assertTrue(make_divisible_by_6(test_value) == 48)


class SchoolRegionTest(TestCase):

    def test_get_region(self):
        school = School(school_id='123456', state='NE')
        self.assertTrue(get_region(school) == 'MW')

    def test_get_region_failure(self):
        school = School(school_id='123456', state='')
        self.assertTrue(get_region(school) == '')


class SchoolModelsTest(TestCase):

    def create_school(self, ID=999999,
                      data_json='',
                      accreditor="Almighty Wizard",
                      city="Emerald City",
                      degrees_highest="3",
                      state="OZ",
                      ope6=5555,
                      ope8=555500):
        return School.objects.create(school_id=ID,
                                     data_json=data_json,
                                     accreditor=accreditor,
                                     degrees_highest=degrees_highest,
                                     degrees_predominant=degrees_highest,
                                     city=city,
                                     state=state,
                                     ope6_id=ope6,
                                     ope8_id=ope8)

    def create_alias(self, alias, school):
        return Alias.objects.create(alias=alias,
                                    is_primary=True,
                                    institution=school)

    def create_contact(self):
        return Contact.objects.create(contacts='hack@hackey.edu',
                                      name='Hackey Sack',
                                      endpoint=u'endpoint.hackey.edu')

    def create_nickname(self, school):
        return Nickname.objects.create(institution=school,
                                       nickname='Hackers')

    def create_program(self, school):
        return Program.objects.create(institution=school,
                                      program_name='Hacking',
                                      level='3')

    def create_disclosure(self, school):
        return Disclosure.objects.create(institution=school,
                                         name='Regional transferability',
                                         text="Your credits won't transfer")

    def create_notification(self,
                            school,
                            oid='f38283b5b7c939a058889f997949efa566c616c5',
                            time='2016-01-13T20:06:18.913112+00:00'):
        return Notification.objects.create(institution=school,
                                           oid=oid,
                                           timestamp=dateutil.parser.parse(time),
                                           errors='none')

    def test_school_related_models(self):
        s = self.create_school()
        self.assertTrue(isinstance(s, School))
        self.assertEqual(s.primary_alias, "Not Available")
        d = self.create_disclosure(s)
        self.assertTrue(isinstance(d, Disclosure))
        self.assertTrue(d.name in d.__unicode__())
        a = self.create_alias('Wizard U', s)
        self.assertTrue(isinstance(a, Alias))
        self.assertTrue(a.alias in a.__unicode__())
        self.assertEqual(s.primary_alias, a.alias)
        self.assertEqual(s.__unicode__(), a.alias + u" (%s)" % s.school_id)
        c = self.create_contact()
        self.assertTrue(isinstance(c, Contact))
        self.assertTrue(c.contacts in c.__unicode__())
        n = self.create_nickname(s)
        self.assertTrue(isinstance(n, Nickname))
        self.assertTrue(n.nickname in n.__unicode__())
        self.assertTrue(n.nickname in s.nicknames)
        p = self.create_program(s)
        self.assertTrue(isinstance(p, Program))
        self.assertTrue(p.program_name in p.__unicode__())
        self.assertTrue(p.program_name in p.as_json())
        self.assertTrue('Bachelor' in p.get_level())
        noti = self.create_notification(s)
        self.assertTrue(isinstance(noti, Notification))
        self.assertTrue(noti.oid in noti.__unicode__())
        self.assertIsInstance(print_vals(s, noprint=True), basestring)
        self.assertTrue(
            'Emerald City' in print_vals(s, val_list=True, noprint=True)
        )
        self.assertTrue("Emerald City" in print_vals(s, val_dict=True, noprint=True)['city'])
        self.assertTrue("Emerald City" in print_vals(s, noprint=True))

        print_patcher = mock.patch('paying_for_college.models.print')
        with print_patcher as mock_print:
            self.assertIsInstance(print_vals(s, val_list=True), list)
            self.assertTrue(mock_print.called)

        with print_patcher as mock_print:
            self.assertIsNone(print_vals(s))
            self.assertTrue(mock_print.called)

        self.assertTrue(s.convert_ope6() == '005555')
        self.assertTrue(s.convert_ope8() == '00555500')
        self.assertTrue('Bachelor' in s.get_highest_degree())
        s.ope6_id = 555555
        s.ope8_id = 55555500
        self.assertTrue(s.convert_ope6() == '555555')
        self.assertTrue(s.convert_ope8() == '55555500')
        s.ope6_id = None
        s.ope8_id = None
        self.assertTrue(s.convert_ope6() == '')
        self.assertTrue(s.convert_ope8() == '')

    def test_constant_models(self):
        cr = ConstantRate(name='cr test', slug='crTest', value='0.1')
        self.assertTrue(cr.__unicode__() == u'cr test (crTest), updated None')
        cc = ConstantCap(name='cc test', slug='ccTest', value='0')
        self.assertTrue(cc.__unicode__() == u'cc test (ccTest), updated None')

    @mock.patch('paying_for_college.models.send_mail')
    def test_email_notification(self, mock_mail):
        skul = self.create_school()
        skul.settlement_school = 'edmc'
        noti = self.create_notification(skul)
        msg = noti.notify_school()
        self.assertTrue('failed' in msg)
        contact = self.create_contact()
        contact.endpoint = ''
        contact.save()
        skul.contact = contact
        skul.save()
        noti2 = self.create_notification(skul)
        msg1 = noti2.notify_school()
        self.assertTrue(mock_mail.call_count == 1)
        self.assertTrue('email' in msg1)
        noti3 = self.create_notification(skul)
        mock_mail.side_effect = smtplib.SMTPException('fail')
        msg = noti3.notify_school()
        self.assertTrue(mock_mail.call_count == 2)

    @mock.patch('paying_for_college.models.requests.post')
    def test_endpoint_notification(self, mock_post):
        skul = self.create_school()
        skul.settlement_school = 'edmc'
        contact = self.create_contact()
        contact.endpoint = u'fake-api.fakeschool.edu'
        contact.save()
        skul.contact = contact
        skul.save()
        noti = self.create_notification(skul)
        msg = noti.notify_school()
        self.assertTrue(mock_post.call_count == 1)
        self.assertTrue('endpoint' in msg)
        mock_return = mock.Mock()
        mock_return.ok = False
        mock_post.return_value = mock_return
        fail_msg = noti.notify_school()
        # print("notification mock_post.call_count is {0}".format(mock_post.call_count))
        # print("fail msg is {0}\n\n\n".format(fail_msg))
        self.assertTrue('fail' in fail_msg)

    def test_endpoint_notification_blank_contact(self):
        skul = self.create_school()
        skul.settlement_school = 'edmc'
        contact = self.create_contact()
        contact.contacts = ''
        contact.endpoint = ''
        contact.save()
        skul.contact = contact
        skul.save()
        noti = self.create_notification(skul)
        msg = noti.notify_school()
        self.assertTrue('failed' in msg)

    @mock.patch('paying_for_college.models.requests.post')
    def test_notification_request_errors(self, mock_post):
        skul = self.create_school()
        skul.settlement_school = 'edmc'
        contact = self.create_contact()
        skul.contact = contact
        skul.save()
        noti = self.create_notification(skul)
        mock_post.side_effect = requests.exceptions.ConnectionError
        msg = noti.notify_school()
        self.assertTrue('Error' in msg)
        mock_post.side_effect = requests.exceptions.Timeout
        msg = noti.notify_school()
        self.assertTrue('Error' in msg)
        mock_post.side_effect = requests.exceptions.RequestException
        msg = noti.notify_school()
        self.assertTrue('Error' in msg)


class NonSettlementNotificaion(TestCase):
    fixtures = ['test_fixture.json']

    def create_notification(self,
                            school,
                            oid='f38283b5b7c939a058889f997949efa566c616c5',
                            time='2016-01-13T20:06:18.913112+00:00'):
        return Notification.objects.create(institution=school,
                                           oid=oid,
                                           timestamp=dateutil.parser.parse(time),
                                           errors='none')

    def test_nonsettlement_notification(self):
        skul = School.objects.get(pk=155317)  # a non-settlement school
        notification = self.create_notification(skul)
        non_msg = notification.notify_school()
        self.assertTrue('No notification required' in non_msg)


class ProgramExport(TestCase):
    fixtures = ['test_program.json']

    def test_program_as_csv(self):
        p = Program.objects.get(pk=1)
        m = mock_open()
        with patch("__builtin__.open", m, create=True):
            p.as_csv('/tmp.csv')
        self.assertTrue(m.call_count == 1)
