import os
import csv
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'grady.settings')

import django
import xkcdpass.xkcd_password as xp
import getpass
import json
import argparse
from collections import namedtuple
django.setup()

INFO = 0

HTML_DIR = 'html'
SOLUTION_DIR = 'code/code-lsg'

wordfile = xp.locate_wordfile()
wordlist = xp.generate_wordlist(wordfile=wordfile, min_length=5, max_length=8)


from django.contrib.auth.models import Group, User

from core.models import Student, Submission, SubmissionType, Feedback

if INFO:
    info = print
else:
    info = lambda _: 0

def parseme():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--superuser',
        help='Superuser will be created users be created',
        action='store_true')
    parser.add_argument(
        'DATADIR',
        help='a folder containing a predefined set of files with information',
        default='data')
    parser.add_argument(
        '-s', '--submissions',
        help='A file with submission code and student user names',
        default='submissions.json',
        metavar='FILE')
    parser.add_argument(
        '-t', '--tutors',
        help='A list of tutor names',
        default='tutors',
        metavar="FILE")
    parser.add_argument(
        '-r', '--reviewers',
        help='A list of reviewer names',
        default='reviewers',
        metavar="FILE")
    parser.add_argument(
        '-st', '--submission_types',
        help='some kind of descriptions for all the submission types',
        default='submission_types.csv',
        metavar="FILE")

    args = parser.parse_args()

    args.tutors             = os.path.join(args.DATADIR, args.tutors)
    args.reviewers          = os.path.join(args.DATADIR, args.reviewers)
    args.submissions        = os.path.join(args.DATADIR, args.submissions)
    args.submission_types   = os.path.join(args.DATADIR, args.submission_types)

    return args


def add_submission_type(name,
                        score,
                        task_description="__task_description: what the student saw",
                        possible_solution="__possible_solution: a sample solution",
                        correction_guideline="__possible_solution: a way to correct the task",):
    task, created = SubmissionType.objects.get_or_create(name=name)
    task.full_score = int(score)
    task.task_description = task_description
    task.possible_solution = possible_solution
    task.correction_guideline = correction_guideline
    task.save()
    if created:
        info(f"- Created Task {task.name}")
    else:
        info(f"- Got Task {task.name}")
    return task


def student_has_all_submissions(student):
    return Submission.objects.filter(student=student).count() \
        == SubmissionType.objects.all().count()


def add_submission(type, text, student, compiler_output):
    if student_has_all_submissions(student):
        return None

    sub = Submission()
    sub.type = type
    sub.text = text
    sub.student = student
    sub.pre_corrections = compiler_output
    sub.save()
    add_auto_feedback(sub, compiler_output)
    info(f"- Created Submission of Type {sub.type}")
    return sub


def add_auto_feedback(submission, compiler_output):
    if submission.text and not compiler_output:
        return # let the tutor do his job

    def deduct_feedback_type() -> (str, str):
        if not submission.text:
            return Feedback.WAS_EMPTY, Feedback.ACCEPTED
        elif compiler_output.endswith('DID NOT COMPILE'):
            return Feedback.DID_NOT_COMPILE, Feedback.NEEDS_REVIEW
        elif compiler_output.endswith('COULD NOT LINK'):
            return Feedback.COULD_NOT_LINK, Feedback.NEEDS_REVIEW
        else:
            return None, None

    auto_correct, _ = User.objects.get_or_create(username='auto_correct')
    feedback = Feedback()
    feedback.text = "--- Was generated automatically ---"
    feedback.origin, feedback.status = deduct_feedback_type()
    if feedback.origin is None and feedback.status is None:
        return
    feedback.of_submission = submission
    feedback.of_tutor = auto_correct
    feedback.save()
    if feedback.origin == Feedback.WAS_EMPTY:
        submission.final_feedback = feedback
        submission.save()
    info(f"- Created {feedback.origin} Feedback for Submission {submission}")
    return feedback


def add_student(username, name, matrikel_no):
    student_group, _ = Group.objects.get_or_create(name='Students')
    student, created = Student.objects.get_or_create(
        matrikel_no=matrikel_no,
        user=add_user(username, student_group)
    )
    if created:
        student.name = name
        student.matrikel_no = matrikel_no
        student.save()
    return student


def add_user(username, group):
    user, created = User.objects.get_or_create(username=username)

    if created:
        password = xp.generate_xkcdpassword(wordlist, numwords=2)
        login_writer.writerow([username, password])
        user.set_password(password)
        group.user_set.add(user)
        info(f"- Created user {user} and added him to group {group}")
        user.save()
    else:
        info(f"- User {user} of group {group} was already created.")

    return user


def add_group(group_name):
    group, _ = Group.objects.get_or_create(name=group_name)
    info(f"- Created group {group}")
    return group


def create_superuser():
    try:
        User.objects.create_superuser(
            username='doncamillo', password=getpass.getpass(), email='a@b.com')
    except Exception as e:
        info("- Superuser was already created.")
        return


class PopulateDatabase:

    """docstring for PopulateDatabase"""

    __slots__ = (
        'args',
        'type_dict',
        'student_group',
        'tutor_group',
        'reviewer_group',
    )

    def __init__(self, args):
        self.args = args
        self.create_groups()
        self.create_user_accounts()
        self.create_submission_types()
        self.populate_submissions()

    def create_groups(self):
        self.student_group  = add_group('Students')
        self.tutor_group    = add_group('Tutors')
        self.reviewer_group = add_group('Reviewers')

    def create_user_accounts(self):
        with open(self.args.tutors) as tutors:
            for tutor in tutors:
                add_user(tutor.strip(), self.tutor_group)

        with open(self.args.reviewers) as reviewers:
            for reviewer in reviewers:
                add_user(reviewer.strip(), self.reviewer_group)

    def create_submission_types(self):
        submission_type = namedtuple('submission_type', 'id name score')
        with open(args.submission_types) as data:
            types = list(submission_type(*line.strip().split(', '))
                         for line in data if line)

        self.type_dict = {}
        for t in types:
            with \
                    open(os.path.join(self.args.DATADIR, SOLUTION_DIR, t.id + '-lsg.c' )) as lsg, \
                    open(os.path.join(self.args.DATADIR, HTML_DIR, t.id + '.html' )) as desc:
                self.type_dict[t.id] = add_submission_type(
                    f"[{t.id}] {t.name}",
                    t.score,
                    desc.read(),
                    lsg.read(),
                )

    def populate_submissions(self):
        with open(self.args.submissions) as data:
            stud_data = json.JSONDecoder().decode(data.read())

        for user, userdata in stud_data.items():
            student = add_student(
                user, userdata['name'], userdata['matrikel_no'])
            for s, code in userdata['submissions'].items():
                add_submission(
                    self.type_dict[s], code, student,
                    userdata['compiler_output'][s]
                )


# Start execution here!
if __name__ == '__main__':
    args = parseme()
    print("Starting population script...")
    try:
        login_data_f = open('login_data.csv', 'w')
        login_writer = csv.writer(login_data_f)
        login_writer.writerow(['username', 'password'])
        PopulateDatabase(args)
    finally:
        login_data_f.close()
    create_superuser()