import json
import mock

from django.core.urlresolvers import reverse

from pymongo.errors import ServerSelectionTimeoutError


from analytics.models import CourseReport
from core.common.mongo import c_onboarding_status, _conn
from core.common import onboarding
from ct.models import UnitLesson, StudentError
from ctms.tests import MyTestCase


HEALTH_URL = reverse('api:v0:health-check')


def test_health_positive(client, db):
    result = client.get(HEALTH_URL)

    assert result.status_code == 200
    assert 'ok' in json.loads(result.content)


def test_health_non_ok(client, db, mocker):
    """
    Ping and Stats Mongo command return non ok results.
    """
    do_health = mocker.patch('api.v0.views.do_health')
    do_health.return_value = {}, {}

    result = client.get(HEALTH_URL)

    assert result.status_code == 503


def test_health_exception(client, db, mocker):
    """
    Mongo query raises exception.
    """
    do_health = mocker.patch('api.v0.views.do_health')
    do_health.side_effect = ServerSelectionTimeoutError()

    result = client.get(HEALTH_URL)

    assert result.status_code == 503


class TestOnboardingStatus(MyTestCase):

    namespace = 'api:v0:onboarding-status'

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

        # # Hack: remove all test_ databases before test
        # for db in _conn.connector.list_databases():
        #     if 'test_' in db.get('name') and:
        #         _conn.connector.drop_database(db.get('name'))

        self.data = {
            onboarding.USER_ID: self.user.id,
            onboarding.STEP_1: False,
            onboarding.STEP_2: False,
            onboarding.STEP_3: False,
            onboarding.STEP_4: False,
        }

    def test_put_valid_data(self):

        data_to_update = {onboarding.STEP_2: True}

        c_onboarding_status().remove()

        c_onboarding_status().insert(self.data.copy())

        ensure_saved = c_onboarding_status().find_one({onboarding.USER_ID: self.user.id}, {'_id': False})

        self.assertEqual(ensure_saved, self.data)

        self.assertEqual(self.client.login(username=self.username, password=self.password), True)
        response = self.client.put(
            reverse('api:v0:onboarding-status'),
            data=json.dumps(data_to_update),
            content_type="application/json"
        )
        data = self.data.copy()
        self.assertEqual(response.status_code, 200)
        data.update(data_to_update)
        mongo_data = c_onboarding_status().find_one({onboarding.USER_ID: self.user.id}, {'_id': False})

        self.assertEqual(mongo_data, data)

    def test_put_invalid_keys(self):

        data_to_update = {'invalid_key': True}

        c_onboarding_status().remove()

        c_onboarding_status().insert(self.data.copy())

        ensure_saved = c_onboarding_status().find_one({onboarding.USER_ID: self.user.id}, {'_id': False})

        self.assertEqual(ensure_saved, self.data)

        response = self.client.put(
            reverse('api:v0:onboarding-status'),
            data=json.dumps(data_to_update),
            content_type="application/json"
        )
        self.assertEqual(response.status_code, 400)

    def test_wo_user_403(self):
        c_onboarding_status().remove()
        self.client.logout()
        response = self.client.get(reverse(self.namespace))
        self.assertEqual(response.status_code, 403)

    def test_get_with_user_200(self):

        c_onboarding_status().remove()

        c_onboarding_status().insert(self.data.copy())

        response = self.client.get(reverse(self.namespace))
        expected_data = {
            "done": True,
        }
        response_data = json.loads(response.content)['data']
        for key in response_data.keys():
            self.assertSetEqual(set(expected_data), set(response_data[key]))


class ApiAccessMixinTest(object):

    def test_permissions_instructor_allowed(self):
        response = self.client.get(reverse(self.namespace, kwargs={'course_id': self.course.id}))
        self.assertEqual(response.status_code, 200)

    def test_permissions_not_instructor_disallowed(self):
        self.client.login(username=self.username2, password=self.password2)
        response = self.client.get(reverse(self.namespace, kwargs={'course_id': self.course.id}))
        self.assertEqual(response.status_code, 403)

    def test_permissions_user_not_authenticated(self):
        self.client.logout()
        response = self.client.get(reverse(self.namespace, kwargs={'course_id': self.course.id}))
        self.assertEqual(response.status_code, 403)

    def test_course_doesnt_exist(self):
        response = self.client.get(reverse(self.namespace, kwargs={'course_id': 100}))
        self.assertEqual(response.status_code, 404)


class TestResponseViewSet(ApiAccessMixinTest, MyTestCase):

    namespace = 'api:v0:responses'

    def test_serializer_author_name(self):
        response = self.client.get(reverse(self.namespace, kwargs={'course_id': self.course.id}))
        self.assertEqual(
            json.loads(response.content)[0].get('author_name'),
            self.user.get_full_name() or self.user.username
        )


class TestErrorViewSet(ApiAccessMixinTest, MyTestCase):

    namespace = 'api:v0:errors'

    def setUp(self):
        super(TestErrorViewSet, self).setUp()
        self.unit_lesson_error = UnitLesson(
            unit=self.unit, order=0,
            lesson=self.lesson, addedBy=self.user,
            treeID=self.lesson.id
        )
        self.unit_lesson_error.save()

        self.student_error = StudentError(
            response=self.resp1,
            errorModel=self.unit_lesson_error,
            author=self.user
        )
        self.student_error.save()

    def test_serializer_em_data(self):
        response = self.client.get(reverse(self.namespace, kwargs={'course_id': self.course.id}))
        fields_set = set([
            'id', 'lesson_concept_id', 'lesson_concept_isAbort', 'lesson_concept_isFail', 'lesson_text', 'treeID'
        ])
        em_data_set = set(json.loads(response.content)[0]['em_data'])
        self.assertSetEqual(fields_set, em_data_set)


class TestGenReportView(MyTestCase):
    namespace = 'api:v0:gen-report'

    def test_missed_course_id(self):
        response = self.client.get(reverse(self.namespace))
        self.assertEqual(response.status_code, 400)

    def test_course_doesnt_exist(self):
        response = self.client.get(reverse(self.namespace), data={'course_id': 100})
        self.assertEqual(response.status_code, 404)

    def test_not_allowed(self):
        self.client.login(username=self.username2, password=self.password2)
        response = self.client.get(reverse(self.namespace), data={'course_id': self.course.id})
        self.assertEqual(response.status_code, 403)

    @mock.patch('api.v0.views.report.delay')
    def test_report_generated(self, report):
        response = self.client.get(reverse(self.namespace), data={'course_id': self.course.id})
        self.assertEqual(response.status_code, 200)
        report.assert_called_with(str(self.course.id), self.user.id)


class TestCourseReportViewSet(ApiAccessMixinTest, MyTestCase):

    namespace = 'api:v0:reports'

    def test_serializer_data(self):
        report = CourseReport(
            course=self.course
        )
        report.save()
        response = self.client.get(reverse(self.namespace, kwargs={'course_id': self.course.id}))
        fields_set = {'date', 'response_report'}
        data_set = set(json.loads(response.content)[0])
        self.assertSetEqual(fields_set, data_set)


class TestEchoDataView(MyTestCase):

    namespace = 'api:v0:echo-data'

    def test_echo_405(self):
        get_response = self.client.get(reverse(self.namespace))
        self.assertEqual(get_response.status_code, 405)

    def test_echo_200(self):

        post_response = self.client.post(reverse(self.namespace))
        self.assertEqual(post_response.status_code, 200)

        self.client.logout()

        post_response = self.client.post(reverse(self.namespace))
        self.assertEqual(post_response.status_code, 200)

[((16, 13, 16, 43), 'django.core.urlresolvers.reverse', 'reverse', ({(16, 21, 16, 42): '"""api:v0:health-check"""'}, {}), "('api:v0:health-check')", False, 'from django.core.urlresolvers import reverse\n'), ((43, 28, 43, 57), 'pymongo.errors.ServerSelectionTimeoutError', 'ServerSelectionTimeoutError', ({}, {}), '()', False, 'from pymongo.errors import ServerSelectionTimeoutError\n'), ((213, 5, 213, 44), 'mock.patch', 'mock.patch', ({(213, 16, 213, 43): '"""api.v0.views.report.delay"""'}, {}), "('api.v0.views.report.delay')", False, 'import mock\n'), ((23, 19, 23, 45), 'json.loads', 'json.loads', ({(23, 30, 23, 44): 'result.content'}, {}), '(result.content)', False, 'import json\n'), ((174, 33, 178, 9), 'ct.models.UnitLesson', 'UnitLesson', (), '', False, 'from ct.models import UnitLesson, StudentError\n'), ((181, 29, 185, 9), 'ct.models.StudentError', 'StudentError', (), '', False, 'from ct.models import UnitLesson, StudentError\n'), ((225, 17, 227, 9), 'analytics.models.CourseReport', 'CourseReport', (), '', False, 'from analytics.models import CourseReport\n'), ((84, 12, 84, 47), 'django.core.urlresolvers.reverse', 'reverse', ({(84, 20, 84, 46): '"""api:v0:onboarding-status"""'}, {}), "('api:v0:onboarding-status')", False, 'from django.core.urlresolvers import reverse\n'), ((108, 12, 108, 47), 'django.core.urlresolvers.reverse', 'reverse', ({(108, 20, 108, 46): '"""api:v0:onboarding-status"""'}, {}), "('api:v0:onboarding-status')", False, 'from django.core.urlresolvers import reverse\n'), ((117, 35, 117, 58), 'django.core.urlresolvers.reverse', 'reverse', ({(117, 43, 117, 57): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((126, 35, 126, 58), 'django.core.urlresolvers.reverse', 'reverse', ({(126, 43, 126, 57): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((130, 24, 130, 52), 'json.loads', 'json.loads', ({(130, 35, 130, 51): 'response.content'}, {}), '(response.content)', False, 'import json\n'), ((138, 35, 138, 96), 'django.core.urlresolvers.reverse', 'reverse', (), '', False, 'from django.core.urlresolvers import reverse\n'), ((143, 35, 143, 96), 'django.core.urlresolvers.reverse', 'reverse', (), '', False, 'from django.core.urlresolvers import reverse\n'), ((148, 35, 148, 96), 'django.core.urlresolvers.reverse', 'reverse', (), '', False, 'from django.core.urlresolvers import reverse\n'), ((152, 35, 152, 85), 'django.core.urlresolvers.reverse', 'reverse', (), '', False, 'from django.core.urlresolvers import reverse\n'), ((161, 35, 161, 96), 'django.core.urlresolvers.reverse', 'reverse', (), '', False, 'from django.core.urlresolvers import reverse\n'), ((189, 35, 189, 96), 'django.core.urlresolvers.reverse', 'reverse', (), '', False, 'from django.core.urlresolvers import reverse\n'), ((201, 35, 201, 58), 'django.core.urlresolvers.reverse', 'reverse', ({(201, 43, 201, 57): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((205, 35, 205, 58), 'django.core.urlresolvers.reverse', 'reverse', ({(205, 43, 205, 57): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((210, 35, 210, 58), 'django.core.urlresolvers.reverse', 'reverse', ({(210, 43, 210, 57): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((215, 35, 215, 58), 'django.core.urlresolvers.reverse', 'reverse', ({(215, 43, 215, 57): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((229, 35, 229, 96), 'django.core.urlresolvers.reverse', 'reverse', (), '', False, 'from django.core.urlresolvers import reverse\n'), ((240, 39, 240, 62), 'django.core.urlresolvers.reverse', 'reverse', ({(240, 47, 240, 61): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((245, 41, 245, 64), 'django.core.urlresolvers.reverse', 'reverse', ({(245, 49, 245, 63): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((250, 41, 250, 64), 'django.core.urlresolvers.reverse', 'reverse', ({(250, 49, 250, 63): 'self.namespace'}, {}), '(self.namespace)', False, 'from django.core.urlresolvers import reverse\n'), ((74, 8, 74, 29), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((76, 8, 76, 29), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((78, 23, 78, 44), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((85, 17, 85, 43), 'json.dumps', 'json.dumps', ({(85, 28, 85, 42): 'data_to_update'}, {}), '(data_to_update)', False, 'import json\n'), ((91, 21, 91, 42), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((99, 8, 99, 29), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((101, 8, 101, 29), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((103, 23, 103, 44), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((109, 17, 109, 43), 'json.dumps', 'json.dumps', ({(109, 28, 109, 42): 'data_to_update'}, {}), '(data_to_update)', False, 'import json\n'), ((115, 8, 115, 29), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((122, 8, 122, 29), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((124, 8, 124, 29), 'core.common.mongo.c_onboarding_status', 'c_onboarding_status', ({}, {}), '()', False, 'from core.common.mongo import c_onboarding_status, _conn\n'), ((231, 23, 231, 51), 'json.loads', 'json.loads', ({(231, 34, 231, 50): 'response.content'}, {}), '(response.content)', False, 'import json\n'), ((193, 26, 193, 54), 'json.loads', 'json.loads', ({(193, 37, 193, 53): 'response.content'}, {}), '(response.content)', False, 'import json\n'), ((163, 12, 163, 40), 'json.loads', 'json.loads', ({(163, 23, 163, 39): 'response.content'}, {}), '(response.content)', False, 'import json\n')]