"""
Tests the transfer student management command
"""
from django.conf import settings
from mock import patch, call
from opaque_keys.edx import locator
import unittest
import ddt

from shoppingcart.models import Order, CertificateItem  # pylint: disable=import-error
from course_modes.models import CourseMode
from student.management.commands import transfer_students
from student.models import CourseEnrollment, UNENROLL_DONE, EVENT_NAME_ENROLLMENT_DEACTIVATED, \
    EVENT_NAME_ENROLLMENT_ACTIVATED, EVENT_NAME_ENROLLMENT_MODE_CHANGED
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory


@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@ddt.ddt
class TestTransferStudents(ModuleStoreTestCase):
    """Tests for transferring students between courses."""

    PASSWORD = 'test'
    signal_fired = False

    def setUp(self, **kwargs):
        """Connect a stub receiver, and analytics event tracking."""
        super(TestTransferStudents, self).setUp()

        UNENROLL_DONE.connect(self.assert_unenroll_signal)
        patcher = patch('student.models.tracker')
        self.mock_tracker = patcher.start()
        self.addCleanup(patcher.stop)

    def tearDown(self):
        """Disconnects the UNENROLL stub receiver."""
        UNENROLL_DONE.disconnect(self.assert_unenroll_signal)

    def assert_unenroll_signal(self, skip_refund=False, **kwargs):   # pylint: disable=unused-argument
        """ Signal Receiver stub for testing that the unenroll signal was fired. """
        self.assertFalse(self.signal_fired)
        self.assertTrue(skip_refund)
        self.signal_fired = True

    def test_transfer_students(self):
        """ Verify the transfer student command works as intended. """
        student = UserFactory.create()
        student.set_password(self.PASSWORD)  # pylint: disable=no-member
        student.save()   # pylint: disable=no-member
        mode = 'verified'
        # Original Course
        original_course_location = locator.CourseLocator('Org0', 'Course0', 'Run0')
        course = self._create_course(original_course_location)
        # Enroll the student in 'verified'
        CourseEnrollment.enroll(student, course.id, mode="verified")

        # Create and purchase a verified cert for the original course.
        self._create_and_purchase_verified(student, course.id)

        # New Course 1
        course_location_one = locator.CourseLocator('Org1', 'Course1', 'Run1')
        new_course_one = self._create_course(course_location_one)

        # New Course 2
        course_location_two = locator.CourseLocator('Org2', 'Course2', 'Run2')
        new_course_two = self._create_course(course_location_two)
        original_key = unicode(course.id)
        new_key_one = unicode(new_course_one.id)
        new_key_two = unicode(new_course_two.id)

        # Run the actual management command
        transfer_students.Command().handle(
            source_course=original_key, dest_course_list=new_key_one + "," + new_key_two
        )
        self.assertTrue(self.signal_fired)

        # Confirm the analytics event was emitted.
        self.mock_tracker.emit.assert_has_calls(  # pylint: disable=maybe-no-member
            [
                call(
                    EVENT_NAME_ENROLLMENT_ACTIVATED,
                    {'course_id': original_key, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_MODE_CHANGED,
                    {'course_id': original_key, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_DEACTIVATED,
                    {'course_id': original_key, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_ACTIVATED,
                    {'course_id': new_key_one, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_MODE_CHANGED,
                    {'course_id': new_key_one, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_ACTIVATED,
                    {'course_id': new_key_two, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_MODE_CHANGED,
                    {'course_id': new_key_two, 'user_id': student.id, 'mode': mode}
                )
            ]
        )
        self.mock_tracker.reset_mock()

        # Confirm the enrollment mode is verified on the new courses, and enrollment is enabled as appropriate.
        self.assertEquals((mode, False), CourseEnrollment.enrollment_mode_for_user(student, course.id))
        self.assertEquals((mode, True), CourseEnrollment.enrollment_mode_for_user(student, new_course_one.id))
        self.assertEquals((mode, True), CourseEnrollment.enrollment_mode_for_user(student, new_course_two.id))

        # Confirm the student has not be refunded.
        target_certs = CertificateItem.objects.filter(
            course_id=course.id, user_id=student, status='purchased', mode=mode
        )
        self.assertTrue(target_certs[0])
        self.assertFalse(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, 'purchased')

    def _create_course(self, course_location):
        """ Creates a course """
        return CourseFactory.create(
            org=course_location.org,
            number=course_location.course,
            run=course_location.run
        )

    def _create_and_purchase_verified(self, student, course_id):
        """ Creates a verified mode for the course and purchases it for the student. """
        course_mode = CourseMode(course_id=course_id,
                                 mode_slug="verified",
                                 mode_display_name="verified cert",
                                 min_price=50)
        course_mode.save()
        # When there is no expiration date on a verified mode, the user can always get a refund
        cart = Order.get_cart_for_user(user=student)
        CertificateItem.add_to_order(cart, course_id, 50, 'verified')
        cart.purchase()
