"""
Tests for BadgrBackend
"""
from datetime import datetime

import ddt
from django.db.models.fields.files import ImageFieldFile
from django.test.utils import override_settings
from lazy.lazy import lazy
from mock import Mock, call, patch

from badges.backends.badgr import BadgrBackend
from badges.models import BadgeAssertion
from badges.tests.factories import BadgeClassFactory
from openedx.core.lib.tests.assertions.events import assert_event_matches
from student.tests.factories import CourseEnrollmentFactory, UserFactory
from track.tests import EventTrackingTestCase
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

BADGR_SETTINGS = {
    'BADGR_API_TOKEN': '12345',
    'BADGR_BASE_URL': 'https://example.com',
    'BADGR_ISSUER_SLUG': 'test-issuer',
}

# Should be the hashed result of test_slug as the slug, and test_component as the component
EXAMPLE_SLUG = '15bb687e0c59ef2f0a49f6838f511bf4ca6c566dd45da6293cabbd9369390e1a'


# pylint: disable=protected-access
@ddt.ddt
@override_settings(**BADGR_SETTINGS)
class BadgrBackendTestCase(ModuleStoreTestCase, EventTrackingTestCase):
    """
    Tests the BadgeHandler object
    """
    def setUp(self):
        """
        Create a course and user to test with.
        """
        super(BadgrBackendTestCase, self).setUp()
        # Need key to be deterministic to test slugs.
        self.course = CourseFactory.create(
            org='edX', course='course_test', run='test_run', display_name='Badged',
            start=datetime(year=2015, month=5, day=19),
            end=datetime(year=2015, month=5, day=20)
        )
        self.user = UserFactory.create(email='example@example.com')
        CourseEnrollmentFactory.create(user=self.user, course_id=self.course.location.course_key, mode='honor')
        # Need to empty this on each run.
        BadgrBackend.badges = []
        self.badge_class = BadgeClassFactory.create(course_id=self.course.location.course_key)
        self.legacy_badge_class = BadgeClassFactory.create(
            course_id=self.course.location.course_key, issuing_component=''
        )
        self.no_course_badge_class = BadgeClassFactory.create()

    @lazy
    def handler(self):
        """
        Lazily loads a BadgeHandler object for the current course. Can't do this on setUp because the settings
        overrides aren't in place.
        """
        return BadgrBackend()

    def test_urls(self):
        """
        Make sure the handler generates the correct URLs for different API tasks.
        """
        self.assertEqual(self.handler._base_url, 'https://example.com/v1/issuer/issuers/test-issuer')
        self.assertEqual(self.handler._badge_create_url, 'https://example.com/v1/issuer/issuers/test-issuer/badges')
        self.assertEqual(
            self.handler._badge_url('test_slug_here'),
            'https://example.com/v1/issuer/issuers/test-issuer/badges/test_slug_here'
        )
        self.assertEqual(
            self.handler._assertion_url('another_test_slug'),
            'https://example.com/v1/issuer/issuers/test-issuer/badges/another_test_slug/assertions'
        )

    def check_headers(self, headers):
        """
        Verify the a headers dict from a requests call matches the proper auth info.
        """
        self.assertEqual(headers, {'Authorization': 'Token 12345'})

    def test_get_headers(self):
        """
        Check to make sure the handler generates appropriate HTTP headers.
        """
        self.check_headers(self.handler._get_headers())

    @patch('requests.post')
    def test_create_badge(self, post):
        """
        Verify badge spec creation works.
        """
        self.handler._create_badge(self.badge_class)
        args, kwargs = post.call_args
        self.assertEqual(args[0], 'https://example.com/v1/issuer/issuers/test-issuer/badges')
        self.assertEqual(kwargs['files']['image'][0], self.badge_class.image.name)
        self.assertIsInstance(kwargs['files']['image'][1], ImageFieldFile)
        self.assertEqual(kwargs['files']['image'][2], 'image/png')
        self.check_headers(kwargs['headers'])
        self.assertEqual(
            kwargs['data'],
            {
                'name': 'Test Badge',
                'slug': EXAMPLE_SLUG,
                'criteria': 'https://example.com/syllabus',
                'description': "Yay! It's a test badge.",
            }
        )

    def test_ensure_badge_created_cache(self):
        """
        Make sure ensure_badge_created doesn't call create_badge if we know the badge is already there.
        """
        BadgrBackend.badges.append(EXAMPLE_SLUG)
        self.handler._create_badge = Mock()
        self.handler._ensure_badge_created(self.badge_class)
        self.assertFalse(self.handler._create_badge.called)

    @ddt.unpack
    @ddt.data(
        ('badge_class', EXAMPLE_SLUG),
        ('legacy_badge_class', 'test_slug'),
        ('no_course_badge_class', 'test_componenttest_slug')
    )
    def test_slugs(self, badge_class_type, slug):
        self.assertEqual(self.handler._slugify(getattr(self, badge_class_type)), slug)

    @patch('requests.get')
    def test_ensure_badge_created_checks(self, get):
        response = Mock()
        response.status_code = 200
        get.return_value = response
        self.assertNotIn('test_componenttest_slug', BadgrBackend.badges)
        self.handler._create_badge = Mock()
        self.handler._ensure_badge_created(self.badge_class)
        self.assertTrue(get.called)
        args, kwargs = get.call_args
        self.assertEqual(
            args[0],
            'https://example.com/v1/issuer/issuers/test-issuer/badges/' +
            EXAMPLE_SLUG
        )
        self.check_headers(kwargs['headers'])
        self.assertIn(EXAMPLE_SLUG, BadgrBackend.badges)
        self.assertFalse(self.handler._create_badge.called)

    @patch('requests.get')
    def test_ensure_badge_created_creates(self, get):
        response = Mock()
        response.status_code = 404
        get.return_value = response
        self.assertNotIn(EXAMPLE_SLUG, BadgrBackend.badges)
        self.handler._create_badge = Mock()
        self.handler._ensure_badge_created(self.badge_class)
        self.assertTrue(self.handler._create_badge.called)
        self.assertEqual(self.handler._create_badge.call_args, call(self.badge_class))
        self.assertIn(EXAMPLE_SLUG, BadgrBackend.badges)

    @patch('requests.post')
    def test_badge_creation_event(self, post):
        result = {
            'json': {'id': 'http://www.example.com/example'},
            'image': 'http://www.example.com/example.png',
            'badge': 'test_assertion_slug',
            'issuer': 'https://example.com/v1/issuer/issuers/test-issuer',
        }
        response = Mock()
        response.json.return_value = result
        post.return_value = response
        self.recreate_tracker()
        self.handler._create_assertion(self.badge_class, self.user, 'https://example.com/irrefutable_proof')
        args, kwargs = post.call_args
        self.assertEqual(
            args[0],
            'https://example.com/v1/issuer/issuers/test-issuer/badges/' +
            EXAMPLE_SLUG +
            '/assertions'
        )
        self.check_headers(kwargs['headers'])
        assertion = BadgeAssertion.objects.get(user=self.user, badge_class__course_id=self.course.location.course_key)
        self.assertEqual(assertion.data, result)
        self.assertEqual(assertion.image_url, 'http://www.example.com/example.png')
        self.assertEqual(assertion.assertion_url, 'http://www.example.com/example')
        self.assertEqual(kwargs['data'], {
            'email': 'example@example.com',
            'evidence': 'https://example.com/irrefutable_proof'
        })
        assert_event_matches({
            'name': 'edx.badge.assertion.created',
            'data': {
                'user_id': self.user.id,
                'course_id': unicode(self.course.location.course_key),
                'enrollment_mode': 'honor',
                'assertion_id': assertion.id,
                'badge_name': 'Test Badge',
                'badge_slug': 'test_slug',
                'issuing_component': 'test_component',
                'assertion_image_url': 'http://www.example.com/example.png',
                'assertion_json_url': 'http://www.example.com/example',
                'issuer': 'https://example.com/v1/issuer/issuers/test-issuer',
            }
        }, self.get_event())
