from __future__ import print_function

import os
import sys
import traceback

import lxml.etree

from django.core.management.base import BaseCommand
from fs.osfs import OSFS
from path import Path as path
from xmodule.modulestore.xml import XMLModuleStore


def traverse_tree(course):
    """
    Load every descriptor in course.  Return bool success value.
    """
    queue = [course]
    while len(queue) > 0:
        node = queue.pop()
        queue.extend(node.get_children())

    return True


def export(course, export_dir):
    """
    Export the specified course to course_dir.  Creates dir if it doesn't
    exist.  Overwrites files, does not clean out dir beforehand.
    """
    fs = OSFS(export_dir, create=True)
    if not fs.isdirempty('.'):
        print('WARNING: Directory {dir} not-empty.  May clobber/confuse things'.format(dir=export_dir))

    try:
        course.runtime.export_fs = fs
        root = lxml.etree.Element('root')
        course.add_xml_to_node(root)
        with fs.open('course.xml', mode='w') as f:
            root.write(f)

        return True
    except:
        print('Export failed!')
        traceback.print_exc()

    return False


def import_with_checks(course_dir):
    all_ok = True

    print('Attempting to load "{}"'.format(course_dir))

    course_dir = path(course_dir)
    data_dir = course_dir.dirname()
    source_dirs = [course_dir.basename()]

    # No default class--want to complain if it doesn't find plugins for any
    # module.
    modulestore = XMLModuleStore(
        data_dir,
        default_class=None,
        source_dirs=source_dirs
    )

    def str_of_err(tpl):
        (msg, exc_str) = tpl
        return '{msg}\n{exc}'.format(msg=msg, exc=exc_str)

    courses = modulestore.get_courses()

    n = len(courses)
    if n != 1:
        print('ERROR: Expect exactly 1 course.  Loaded {n}: {lst}'.format(n=n, lst=courses))
        return (False, None)

    course = courses[0]
    errors = modulestore.get_course_errors(course.id)
    if len(errors) != 0:
        all_ok = False
        print(
            '\n' +
            '========================================' +
            'ERRORs during import:' +
            '\n'.join(map(str_of_err, errors)) +
            '========================================' +
            '\n'
        )

    # print course
    validators = (
        traverse_tree,
    )

    print('========================================')
    print('Running validators...')

    for validate in validators:
        print('Running {}'.format(validate.__name__))
        all_ok = validate(course) and all_ok

    if all_ok:
        print('Course passes all checks!')
    else:
        print('Course fails some checks.  See above for errors.')
    return all_ok, course


def check_roundtrip(course_dir):
    """
    Check that import->export leaves the course the same
    """

    print('====== Roundtrip import =======')
    (ok, course) = import_with_checks(course_dir)
    if not ok:
        raise Exception('Roundtrip import failed!')

    print('====== Roundtrip export =======')
    export_dir = course_dir + '.rt'
    export(course, export_dir)

    # dircmp doesn't do recursive diffs.
    # diff = dircmp(course_dir, export_dir, ignore=[], hide=[])
    print('======== Roundtrip diff: =========')
    sys.stdout.flush()  # needed to make diff appear in the right place
    os.system('diff -r {} {}'.format(course_dir, export_dir))
    print('======== ideally there is no diff above this =======')


class Command(BaseCommand):
    help = 'Imports specified course, validates it, then exports it in a canonical format.'

    def add_arguments(self, parser):
        parser.add_argument('course_dir',
                            help='path to the input course directory')
        parser.add_argument('output_dir',
                            help='path to the output course directory')
        parser.add_argument('--force',
                            action='store_true',
                            help='export course even if there were import errors')

    def handle(self, *args, **options):
        course_dir = options['course_dir']
        output_dir = options['output_dir']
        force = options['force']

        (ok, course) = import_with_checks(course_dir)
        if ok or force:
            if not ok:
                print('WARNING: Exporting despite errors')
            export(course, output_dir)
            check_roundtrip(output_dir)
        else:
            print('Did NOT export')
