"""
integration tests for xmodule

Contains:

    1. BaseTestXmodule class provides course and users
    for testing Xmodules with mongo store.
"""

from django.test.utils import override_settings
from django.core.urlresolvers import reverse
from django.test.client import Client

from edxmako.shortcuts import render_to_string
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_MODULESTORE
from xblock.field_data import DictFieldData
from xmodule.tests import get_test_system, get_test_descriptor_system
from opaque_keys.edx.locations import Location
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from lms.djangoapps.lms_xblock.runtime import quote_slashes


@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class BaseTestXmodule(ModuleStoreTestCase):
    """Base class for testing Xmodules with mongo store.

    This class prepares course and users for tests:
        1. create test course;
        2. create, enroll and login users for this course;

    Any xmodule should overwrite only next parameters for test:
        1. CATEGORY
        2. DATA or METADATA
        3. MODEL_DATA
        4. COURSE_DATA and USER_COUNT if needed

    This class should not contain any tests, because CATEGORY
    should be defined in child class.
    """
    USER_COUNT = 2
    COURSE_DATA = {}

    # Data from YAML common/lib/xmodule/xmodule/templates/NAME/default.yaml
    CATEGORY = "vertical"
    DATA = ''
    # METADATA must be overwritten for every instance that uses it. Otherwise,
    # if we'll change it in the tests, it will be changed for all other instances
    # of parent class.
    METADATA = {}
    MODEL_DATA = {'data': '<some_module></some_module>'}

    def new_module_runtime(self):
        """
        Generate a new ModuleSystem that is minimally set up for testing
        """
        runtime = get_test_system(course_id=self.course.id)

        # When asked for a module out of a descriptor, just create a new xmodule runtime,
        # and inject it into the descriptor
        def get_module(descr):
            descr.xmodule_runtime = self.new_module_runtime()
            return descr

        runtime.get_module = get_module

        return runtime

    def new_descriptor_runtime(self):
        runtime = get_test_descriptor_system()
        runtime.get_block = modulestore().get_item
        return runtime

    def initialize_module(self, **kwargs):
        kwargs.update({
            'parent_location': self.section.location,
            'category': self.CATEGORY
        })

        self.item_descriptor = ItemFactory.create(**kwargs)

        self.runtime = self.new_descriptor_runtime()

        field_data = {}
        field_data.update(self.MODEL_DATA)
        student_data = DictFieldData(field_data)
        self.item_descriptor._field_data = LmsFieldData(self.item_descriptor._field_data, student_data)

        self.item_descriptor.xmodule_runtime = self.new_module_runtime()

        #self.item_module = self.item_descriptor.xmodule_runtime.xmodule_instance
        #self.item_module is None at this time

        self.item_url = self.item_descriptor.location.to_deprecated_string()

    def setup_course(self):
        self.course = CourseFactory.create(data=self.COURSE_DATA)

        # Turn off cache.
        modulestore().request_cache = None
        modulestore().metadata_inheritance_cache_subsystem = None

        chapter = ItemFactory.create(
            parent_location=self.course.location,
            category="sequential",
        )
        self.section = ItemFactory.create(
            parent_location=chapter.location,
            category="sequential"
        )

        # username = robot{0}, password = 'test'
        self.users = [
            UserFactory.create()
            for i in range(self.USER_COUNT)
        ]

        for user in self.users:
            CourseEnrollmentFactory.create(user=user, course_id=self.course.id)

        # login all users for acces to Xmodule
        self.clients = {user.username: Client() for user in self.users}
        self.login_statuses = [
            self.clients[user.username].login(
                username=user.username, password='test')
            for user in self.users
        ]

        self.assertTrue(all(self.login_statuses))

    def setUp(self):
        super(BaseTestXmodule, self).setUp()
        self.setup_course()
        self.initialize_module(metadata=self.METADATA, data=self.DATA)

    def get_url(self, dispatch):
        """Return item url with dispatch."""
        return reverse(
            'xblock_handler',
            args=(self.course.id.to_deprecated_string(), quote_slashes(self.item_url), 'xmodule_handler', dispatch)
        )


class XModuleRenderingTestBase(BaseTestXmodule):

    def new_module_runtime(self):
        """
        Create a runtime that actually does html rendering
        """
        runtime = super(XModuleRenderingTestBase, self).new_module_runtime()
        runtime.render_template = render_to_string
        return runtime