'''
Created on Mar 25, 2013

@author: dmitchell
'''
import datetime
import subprocess
import unittest
import uuid
from importlib import import_module

from xblock.fields import Scope
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.exceptions import InsufficientSpecificationError, ItemNotFoundError, VersionConflictError, \
    DuplicateItemError
from xmodule.modulestore.locator import CourseLocator, BlockUsageLocator, VersionTree, DefinitionLocator
from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.x_module import XModuleMixin
from pytz import UTC
from path import path
import re
import random


class SplitModuleTest(unittest.TestCase):
    '''
    The base set of tests manually populates a db w/ courses which have
    versions. It creates unique collection names and removes them after all
    tests finish.
    '''
    # Snippets of what would be in the django settings envs file
    DOC_STORE_CONFIG = {
        'host': 'localhost',
        'db': 'test_xmodule',
        'collection': 'modulestore{0}'.format(uuid.uuid4().hex[:5]),
    }
    modulestore_options = {
        'default_class': 'xmodule.raw_module.RawDescriptor',
        'fs_root': '',
        'xblock_mixins': (InheritanceMixin, XModuleMixin)
    }

    MODULESTORE = {
        'ENGINE': 'xmodule.modulestore.split_mongo.SplitMongoModuleStore',
        'DOC_STORE_CONFIG': DOC_STORE_CONFIG,
        'OPTIONS': modulestore_options
    }

    # don't create django dependency; so, duplicates common.py in envs
    match = re.search(r'(.*?/common)(?:$|/)', path(__file__))
    COMMON_ROOT = match.group(1)

    modulestore = None

    # These version_guids correspond to values hard-coded in fixture files
    # used for these tests. The files live in mitx/fixtures/splitmongo_json/*

    GUID_D0 = "1d00000000000000dddd0000"  # v12345d
    GUID_D1 = "1d00000000000000dddd1111"  # v12345d1
    GUID_D2 = "1d00000000000000dddd2222"  # v23456d
    GUID_D3 = "1d00000000000000dddd3333"  # v12345d0
    GUID_D4 = "1d00000000000000dddd4444"  # v23456d0
    GUID_D5 = "1d00000000000000dddd5555"  # v345679d
    GUID_P = "1d00000000000000eeee0000"  # v23456p

    @staticmethod
    def bootstrapDB():
        '''
        Loads the initial data into the db ensuring the collection name is
        unique.
  
      '''
        collection_
prefix = SplitModuleTest.MODULESTORE['DOC_STORE_CONFIG']['collection'] + '.'
        dbname = SplitModuleTest.MODULESTORE['DOC_STORE_CONFIG']['db']
        processes = [
            subprocess.Popen([
                'mongoimport', '-d', dbname, '-c',
                collection_prefix + collection, '--jsonArray',
                '--file',
                SplitModuleTest.COMMON_ROOT + '/test/data/splitmongo_json/' + collection + '.json'
                ],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
            )
            for collection in ('active_versions', 'structures', 'definitions')]
        for p in processes:
            stdout, stderr = p.communicate()
            if p.returncode != 0:
                print "Couldn't run mongoimport:"
                print stdout
                print stderr
                raise Exception("DB did not init correctly")

    @classmethod
    def tearDownClass(cls):
        collection_prefix = SplitModuleTest.MODULESTORE['DOC_STORE_CONFIG']['collection'] + '.'
        if SplitModuleTest.modulestore:
            for collection in ('active_versions', 'structures', 'definitions'):
                modulestore().db.drop_collection(collection_prefix + collection)
            # drop the modulestore to force re init
            SplitModuleTest.modulestore = None

    def findByIdInResult(self, collection, _id):
        """
        Result is a collection of descriptors. Find the one whose block id
        matches the _id.
        """
        for element in collection:
            if element.location.block_id == _id:
                return element


class SplitModuleCourseTests(SplitModuleTest):
    '''
    Course CRUD operation tests
    '''

    def test_get_courses(self):
        courses = modulestore().get_courses(branch='draft')
        # should have gotten 3 draft courses
        self.assertEqual(len(courses), 3, "Wrong number of courses")
        # check metadata -- NOTE no promised order
        course = self.findByIdInResult(courses, "head12345")
        self.assertEqual(course.location.package_id, "GreekHero")
        self.assertEqual(
            str(course.location.version_guid), self.GUID_D0,
            "course version mismatch"
        )
        self.assertEqual(course.category, 'course', 'wrong category')
        self.assertEqual(len(course.tabs), 6, "wrong number of tabs")
        self.assertEqual(
            course.display_name, "The Ancient Greek Hero",
            "wrong display name"
        )
        self.assertEqual(
            course.advertised_start, "Fall 2013",
            "advertised_start"
        )
        self.assertEqual(
            len(course.children), 3,
            "children")
        self.assertEqual(str(course.definition_locator.definition_id), "ad00000000000000dddd0000")
        # check dates and graders--forces loading of descriptor
        self.assertEqual(course.edited_by, "testassist@edx.org")
        self.assertEqual(str(course.previous_version), self.GUID_D1)
        self.assertDictEqual(course.grade_cutoffs, {"Pass": 0.45})

    def test_branch_requests(self):
        # query w/ branch qualifier (both draft and published)
        def _verify_published_course(courses_published):
            """ Helper function for verifying published course. """
            self.assertEqual(len(courses_published), 1, len(courses_published))
            course = self.findByIdInResult(courses_published, "head23456")
            self.assertIsNotNone(course, "published courses")
            self.assertEqual(course.location.package_id, "wonderful")
            self.assertEqual(str(course.location.version_guid), self.GUID_P,
                             course.location.version_guid)
            self.assertEqual(course.category, 'course', 'wrong category')
            self.assertEqual(len(course.tabs), 4, "wrong number of tabs")
            self.assertEqual(course.display_name, "The most wonderful course",
                             course.display_name)
            self.assertIsNone(course.advertised_start)
            self.assertEqual(len(course.children), 0,
                             "children")

        _verify_published_course(modulestore().get_courses(branch='published'))
        # default for branch is 'published'.
        _verify_published_course(modulestore().get_courses())

    def test_search_qualifiers(self):
        # query w/ search criteria
        courses = modulestore().get_courses(branch='draft', qualifiers={'org': 'testx'})
        self.assertEqual(len(courses), 2)
        self.assertIsNotNone(self.findByIdInResult(courses, "head12345"))
        self.assertIsNotNone(self.findByIdInResult(courses, "head23456"))

        courses = modulestore().get_courses(
            branch='draft',
            qualifiers={'edited_on': {"$lt": datetime.datetime(2013, 3, 28, 15)}})
        self.assertEqual(len(courses), 2)

        courses = modulestore().get_courses(
            branch='draft',
            qualifiers={'org': 'testx', "prettyid": "test_course"})
        self.assertEqual(len(courses), 1)
        self.assertIsNotNone(self.findByIdInResult(courses, "head12345"))

    def test_get_course(self):
        '''
        Test the various calling forms for get_course
        '''
        locator = CourseLocator(version_guid=self.GUID_D1)
        course = modulestore().get_course(locator)
        self.assertIsNone(course.location.package_id)
        self.assertEqual(str(course.location.version_guid), self.GUID_D1)
        self.assertEqual(course.category, 'course')
        self.assertEqual(len(course.tabs), 6)
        self.assertEqual(course.display_name, "The Ancient Greek Hero")
        self.assertEqual(course.graceperiod, datetime.timedelta(hours=2))
        self.assertIsNone(course.advertised_start)
        self.assert