"""
Test the CCXModulestoreWrapper
"""
import datetime
from collections import deque
from itertools import chain, izip_longest

import pytz
from ccx_keys.locator import CCXLocator

from lms.djangoapps.ccx.models import CustomCourseForEdX
from student.tests.factories import AdminFactory, UserFactory
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory


class TestCCXModulestoreWrapper(SharedModuleStoreTestCase):
    """tests for a modulestore wrapped by CCXModulestoreWrapper
    """
    MODULESTORE = TEST_DATA_SPLIT_MODULESTORE

    @classmethod
    def setUpClass(cls):
        super(TestCCXModulestoreWrapper, cls).setUpClass()
        cls.course = CourseFactory.create()
        start = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=pytz.UTC)
        due = datetime.datetime(2010, 7, 7, 0, 0, tzinfo=pytz.UTC)
        # Create a course outline
        cls.chapters = chapters = [
            ItemFactory.create(start=start, parent=cls.course) for _ in xrange(2)
        ]
        cls.sequentials = sequentials = [
            ItemFactory.create(parent=c) for _ in xrange(2) for c in chapters
        ]
        cls.verticals = verticals = [
            ItemFactory.create(
                due=due, parent=s, graded=True, format='Homework'
            ) for _ in xrange(2) for s in sequentials
        ]
        cls.blocks = [
            ItemFactory.create(parent=v, category='html') for _ in xrange(2) for v in verticals
        ]

    @classmethod
    def setUpTestData(cls):
        """
        Set up models for the whole TestCase.
        """
        cls.user = UserFactory.create()
        # Create instructor account
        cls.coach = AdminFactory.create()

    def setUp(self):
        """
        Set up tests
        """
        super(TestCCXModulestoreWrapper, self).setUp()
        self.ccx = ccx = CustomCourseForEdX(
            course_id=self.course.id,
            display_name='Test CCX',
            coach=self.coach
        )
        ccx.save()

        self.ccx_locator = CCXLocator.from_course_locator(self.course.id, ccx.id)

    def get_all_children_bf(self, block):
        """traverse the children of block in a breadth-first order"""
        queue = deque([block])
        while queue:
            item = queue.popleft()
            yield item
            queue.extend(item.get_children())

    def get_course(self, key):
        """get a course given a key"""
        with self.store.bulk_operations(key):
            course = self.store.get_course(key)
        return course

    def test_get_course(self):
        """retrieving a course with a ccx key works"""
        expected = self.get_course(self.ccx_locator.to_course_locator())
        actual = self.get_course(self.ccx_locator)
        self.assertEqual(
            expected.location.course_key,
            actual.location.course_key.to_course_locator())
        self.assertEqual(expected.display_name, actual.display_name)

    def test_get_children(self):
        """the children of retrieved courses should be the same with course and ccx keys
        """
        course_key = self.ccx_locator.to_course_locator()
        course = self.get_course(course_key)
        ccx = self.get_course(self.ccx_locator)
        test_fodder = izip_longest(
            self.get_all_children_bf(course), self.get_all_children_bf(ccx)
        )
        for expected, actual in test_fodder:
            if expected is None:
                self.fail('course children exhausted before ccx children')
            if actual is None:
                self.fail('ccx children exhausted before course children')
            self.assertEqual(expected.display_name, actual.display_name)
            self.assertEqual(expected.location.course_key, course_key)
            self.assertEqual(actual.location.course_key, self.ccx_locator)

    def test_has_item(self):
        """can verify that a location exists, using ccx block usage key"""
        for item in chain(self.chapters, self.sequentials, self.verticals, self.blocks):
            block_key = self.ccx_locator.make_usage_key(
                item.location.block_type, item.location.block_id
            )
            self.assertTrue(self.store.has_item(block_key))

    def test_get_item(self):
        """can retrieve an item by a location key, using a ccx block usage key

        the retrieved item should be the same as the the one read without ccx
        info
        """
        for expected in chain(self.chapters, self.sequentials, self.verticals, self.blocks):
            block_key = self.ccx_locator.make_usage_key(
                expected.location.block_type, expected.location.block_id
            )
            actual = self.store.get_item(block_key)
            self.assertEqual(expected.display_name, actual.display_name)
            self.assertEqual(expected.location, actual.location.to_block_locator())

    def test_publication_api(self):
        """verify that we can correctly discern a published item by ccx key"""
        with self.store.bulk_operations(self.ccx_locator):
            for expected in self.blocks:
                block_key = self.ccx_locator.make_usage_key(
                    expected.location.block_type, expected.location.block_id
                )
                self.assertTrue(self.store.has_published_version(expected))
                self.store.unpublish(block_key, self.user.id)
                self.assertFalse(self.store.has_published_version(expected))
                self.store.publish(block_key, self.user.id)
                self.assertTrue(self.store.has_published_version(expected))