import json
import logging
import os

from django.views.generic import View
from django.http import HttpResponse

from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys import InvalidKeyError

from student.models import CourseEnrollment
from student.auth import has_studio_write_access

from xmodule.modulestore.django import modulestore

import xblock_jupyter_graded.nbgrader_utils as nbu
from xblock_jupyter_graded.exceptions import ValidationError
from xblock_jupyter_graded.config import (
    RELEASE, SUBMITTED, SOURCE, AUTOGRADED, EDX_ROOT, FEEDBACK
)

log = logging.getLogger(__name__)


class DownloadStudentNBView(View):
    def get(self, request, course_id, unit_id, filename):
        """Serve student notebook as file download"""
        return self.get_nb(request, RELEASE, course_id, unit_id, filename)

    def get_nb(self, request, path_dir, course_id, unit_id, filename, ext=".ipynb", username=None):
        """Return proper response based on path_dir"""
        # Normalize Course/Unit id's to path values
        course = nbu.normalize_course_id(course_id)
        unit = nbu.normalize_unit_id(unit_id)
        fn_with_ext = "{}{}".format(filename, ext)

        if username:
            path = os.path.join(EDX_ROOT, course, path_dir, 
                request.user.username, unit, fn_with_ext)
        else:
            path = os.path.join(EDX_ROOT, course, path_dir, unit, fn_with_ext)

        # Validate Request and Path
        try:
            self.validate_user(request, course_id)
            self.validate_path(path, course_id, unit_id, filename)
        except ValidationError as e:
            log.info(e.msg)
            return HttpResponse(e.msg, status=e.status_code)

        # Everything good - return file
        with open(path) as f:
            response = HttpResponse(
                content=f.read()
            )
            response['content-type'] = 'application/octet-stream'
            response['Content-Disposition'] = 'attachment;filename="{}.ipynb"'.format(filename)
            return response

    def validate_user(self, request, course_id):
        """Validate that course exists and user is enrolled in course"""
        try:
            course_key = CourseKey.from_string(course_id)
            c = CourseEnrollment.objects.get(user_id=request.user.id, course_id=course_key)
            if not c.is_active:
                msg = "Access Denied. {} is not currently enrolled in {}"\
                        .format(request.user.username, course_id)
                raise ValidationError(msg, 403)

        # Something wrong with CourseKey
        except InvalidKeyError as e:
            msg = "Course: {} not found".format(course_id)
            raise ValidationError(msg, 404)

        # Enrollment does not exist for user
        except CourseEnrollment.DoesNotExist:
            log.error("User: {} tried to access student notebook in: {}"\
                .format(request.user.username, course_id))
            msg = "Access Denied. Either course {} does not exist or user {} is not currently enrolled"\
                    .format(course_id, request.user.username)
            raise ValidationError(msg, 403)

    def validate_path(self, path, course_id, unit_id, filename):
        """Make sure requested path exists"""
        if not os.path.exists(path):
            msg = 'Content not found for:<br><br>course: {}<br>unit: {}<br>' \
                  'file: {}'.format(course_id, unit_id, filename)
            raise ValidationError(msg, 404)


class DownloadInstructorNBView(DownloadStudentNBView):
    def get(self, request, course_id, unit_id, filename):
        """Serve instructor notebook as file download"""
        return self.get_nb(request, SOURCE, course_id, unit_id, filename)

    def validate_user(self, request, course_id):
        """Validate user has studio access to this course"""
        try:
            course_key = CourseKey.from_string(course_id)
            if not has_studio_write_access(request.user, course_key):
                msg = "Access Denied. User: {} does not have instructor rights"\
                " in this course"\
                    .format(request.user.username)
                raise ValidationError(msg, 403)

        # Something wrong with CourseKey
        except InvalidKeyError as e:
            msg = "Course: {} not found".format(course_id)
            raise ValidationError(msg, 404)


class DownloadAutogradedNBView(DownloadStudentNBView):
    def get(self, request, course_id, unit_id, filename):
        """Serve autograded notebook as file download
        
        Denies access if instructor has not set `allow_graded_dl` to True
        """
        usage_key = UsageKey.from_string(unit_id)
        xblock = modulestore().get_item(usage_key)

        if xblock.allow_graded_dl:
            response = self.get_nb(request, FEEDBACK, course_id, unit_id, filename, 
                    ext=".html", username=request.user.username)
            response['Content-Disposition'] = 'attachment;'\
                'filename="{}_autograded.html"'.format(filename)
            return response
        else:
            msg = "Instructor has not enabled downloading of autograded notebooks"
            return HttpResponse(msg, status=403)