"""
Tests for the course import API views
"""
import os
import shutil
import tarfile
import tempfile
from datetime import datetime
from urllib import urlencode

from django.core.urlresolvers import reverse
from path import Path as path
from mock import patch
from rest_framework import status
from rest_framework.test import APITestCase

from lms.djangoapps.courseware.tests.factories import GlobalStaffFactory, StaffFactory
from student.tests.factories import UserFactory
from user_tasks.models import UserTaskStatus
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory


class CourseImportViewTest(SharedModuleStoreTestCase, APITestCase):
    """
    Test importing courses via a RESTful API (POST method only)
    """
    MODULESTORE = TEST_DATA_SPLIT_MODULESTORE

    @classmethod
    def setUpClass(cls):
        super(CourseImportViewTest, cls).setUpClass()

        cls.course = CourseFactory.create(display_name='test course', run="Testing_course")
        cls.course_key = cls.course.id

        cls.restricted_course = CourseFactory.create(display_name='restricted test course', run="Restricted_course")
        cls.restricted_course_key = cls.restricted_course.id

        cls.password = 'test'
        cls.student = UserFactory(username='dummy', password=cls.password)
        cls.staff = StaffFactory(course_key=cls.course.id, password=cls.password)
        cls.restricted_staff = StaffFactory(course_key=cls.restricted_course.id, password=cls.password)

        cls.content_dir = path(tempfile.mkdtemp())

        # Create tar test files -----------------------------------------------
        # OK course:
        good_dir = tempfile.mkdtemp(dir=cls.content_dir)
        # test course being deeper down than top of tar file
        embedded_dir = os.path.join(good_dir, "grandparent", "parent")
        os.makedirs(os.path.join(embedded_dir, "course"))
        with open(os.path.join(embedded_dir, "course.xml"), "w+") as f:
            f.write('<course url_name="2013_Spring" org="EDx" course="0.00x"/>')

        with open(os.path.join(embedded_dir, "course", "2013_Spring.xml"), "w+") as f:
            f.write('<course></course>')

        cls.good_tar_filename = "good.tar.gz"
        cls.good_tar_fullpath = os.path.join(cls.content_dir, cls.good_tar_filename)
        with tarfile.open(cls.good_tar_fullpath, "w:gz") as gtar:
            gtar.add(good_dir)

    def get_url(self, course_id):
        """
        Helper function to create the url
        """
        return reverse(
            'courses_api:course_import',
            kwargs={
                'course_id': course_id
            }
        )

    def test_anonymous_import_fails(self):
        """
        Test that an anonymous user cannot access the API and an error is received.
        """
        with open(self.good_tar_fullpath, 'rb') as fp:
            resp = self.client.post(self.get_url(self.course_key), {'course_data': fp}, format='multipart')
        self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_student_import_fails(self):
        """
        Test that a student user cannot access the API and an error is received.
        """
        self.client.login(username=self.student.username, password=self.password)
        with open(self.good_tar_fullpath, 'rb') as fp:
            resp = self.client.post(self.get_url(self.course_key), {'course_data': fp}, format='multipart')
        self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)

    def test_staff_with_access_import_succeeds(self):
        """
        Test that a staff user can access the API and successfully upload a course
        """
        self.client.login(username=self.staff.username, password=self.password)
        with open(self.good_tar_fullpath, 'rb') as fp:
            resp = self.client.post(self.get_url(self.course_key), {'course_data': fp}, format='multipart')
        self.assertEqual(resp.status_code, status.HTTP_200_OK)

    def test_staff_has_no_access_import_fails(self):
        """
        Test that a staff user can't access another course via the API
        """
        self.client.login(username=self.staff.username, password=self.password)
        with open(self.good_tar_fullpath, 'rb') as fp:
            resp = self.client.post(self.get_url(self.restricted_course_key), {'course_data': fp}, format='multipart')
        self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)

    def test_student_get_status_fails(self):
        """
        Test that a student user cannot access the API and an error is received.
        """
        self.client.login(username=self.student.username, password=self.password)
        resp = self.client.get(self.get_url(self.course_key), {'task_id': '1234', 'filename': self.good_tar_filename})
        self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)

    def test_anonymous_get_status_fails(self):
        """
        Test that an anonymous user cannot access the API and an error is received.
        """
        resp = self.client.get(self.get_url(self.course_key), {'task_id': '1234', 'filename': self.good_tar_filename})
        self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_staff_get_status_succeeds(self):
        """
        Test that an import followed by a get status results in success

        Note: This relies on the fact that we process imports synchronously during testing
        """
        self.client.login(username=self.staff.username, password=self.password)
        with open(self.good_tar_fullpath, 'rb') as fp:
            resp = self.client.post(self.get_url(self.course_key), {'course_data': fp}, format='multipart')
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        resp = self.client.get(
            self.get_url(self.course_key),
            {'task_id': resp.data['task_id'], 'filename': self.good_tar_filename},
            format='multipart'
        )
        self.assertEqual(resp.data['state'], UserTaskStatus.SUCCEEDED)

    def test_staff_no_access_get_status_fails(self):
        """
        Test that an import followed by a get status as an unauthorized staff fails

        Note: This relies on the fact that we process imports synchronously during testing
        """
        self.client.login(username=self.staff.username, password=self.password)
        with open(self.good_tar_fullpath, 'rb') as fp:
            resp = self.client.post(self.get_url(self.course_key), {'course_data': fp}, format='multipart')
        self.assertEqual(resp.status_code, status.HTTP_200_OK)

        task_id = resp.data['task_id']
        resp = self.client.get(
            self.get_url(self.course_key),
            {'task_id': task_id, 'filename': self.good_tar_filename},
            format='multipart'
        )
        self.assertEqual(resp.data['state'], UserTaskStatus.SUCCEEDED)

        self.client.logout()

        self.client.login(username=self.restricted_staff.username, password=self.password)
        resp = self.client.get(
            self.get_url(self.course_key),
            {'task_id': task_id, 'filename': self.good_tar_filename},
            format='multipart'
        )
        self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)

    def test_course_task_mismatch_get_status_fails(self):
        """
        Test that an import followed by a get status as an unauthorized staff fails

        Note: This relies on the fact that we process imports synchronously during testing
        """
        self.client.login(username=self.staff.username, password=self.password)
        with open(self.good_tar_fullpath, 'rb') as fp:
            resp = self.client.post(self.get_url(self.course_key), {'course_data': fp}, format='multipart')
        self.assertEqual(resp.status_code, status.HTTP_200_OK)

        task_id = resp.data['task_id']
        resp = self.client.get(
            self.get_url(self.restricted_course_key),
            {'task_id': task_id, 'filename': self.good_tar_filename},
            format='multipart'
        )
        self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
