"""
Test the lms/staticbook views.
"""

import textwrap

import mock
import requests

from django.core.urlresolvers import reverse, NoReverseMatch

from student.tests.factories import UserFactory, CourseEnrollmentFactory
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase


IMAGE_BOOK = ("An Image Textbook", "http://example.com/the_book/")

PDF_BOOK = {
    "tab_title": "Textbook",
    "title": "A PDF Textbook",
    "chapters": [
        {"title": "Chapter 1 for PDF", "url": "https://somehost.com/the_book/chap1.pdf"},
        {"title": "Chapter 2 for PDF", "url": "https://somehost.com/the_book/chap2.pdf"},
    ],
}

PORTABLE_PDF_BOOK = {
    "tab_title": "Textbook",
    "title": "A PDF Textbook",
    "chapters": [
        {"title": "Chapter 1 for PDF", "url": "/static/chap1.pdf"},
        {"title": "Chapter 2 for PDF", "url": "/static/chap2.pdf"},
    ],
}

HTML_BOOK = {
    "tab_title": "Textbook",
    "title": "An HTML Textbook",
    "chapters": [
        {"title": "Chapter 1 for HTML", "url": "https://somehost.com/the_book/chap1.html"},
        {"title": "Chapter 2 for HTML", "url": "https://somehost.com/the_book/chap2.html"},
    ],
}


class StaticBookTest(ModuleStoreTestCase):
    """
    Helpers for the static book tests.
    """

    def __init__(self, *args, **kwargs):
        super(StaticBookTest, self).__init__(*args, **kwargs)
        self.course = None

    def make_course(self, **kwargs):
        """
        Make a course with an enrolled logged-in student.
        """
        self.course = CourseFactory.create(**kwargs)
        user = UserFactory.create()
        CourseEnrollmentFactory.create(user=user, course_id=self.course.id)
        self.client.login(username=user.username, password='test')

    def make_url(self, url_name, **kwargs):
        """
        Make a URL for a `url_name` using keyword args for url slots.

        Automatically provides the course id.

        """
        kwargs['course_id'] = self.course.id.to_deprecated_string()
        url = reverse(url_name, kwargs=kwargs)
        return url


class StaticImageBookTest(StaticBookTest):
    """
    Test the image-based static book view.
    """

    def test_book(self):
        # We can access a book.
        with mock.patch.object(requests, 'get') as mock_get:
            mock_get.return_value.text = textwrap.dedent('''\
                <?xml version="1.0"?>
                <table_of_contents>
                <entry page="9" page_label="ix" name="Contents!?"/>
                <entry page="1" page_label="i" name="Preamble">
                    <entry page="4" page_label="iv" name="About the Elephants"/>
                </entry>
                </table_of_contents>
                ''')

            self.make_course(textbooks=[IMAGE_BOOK])
            url = self.make_url('book', book_index=0)
            response = self.client.get(url)

        self.assertContains(response, "Contents!?")
        self.assertContains(response, "About the Elephants")

    def test_bad_book_id(self):
        # A bad book id will be a 404.
        self.make_course(textbooks=[IMAGE_BOOK])
        with self.assertRaises(NoReverseMatch):
            self.make_url('book', book_index='fooey')

    def test_out_of_range_book_id(self):
        self.make_course()
        url = self.make_url('book', book_index=0)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_bad_page_id(self):
        # A bad page id will cause a 404.
        self.make_course(textbooks=[IMAGE_BOOK])
        with self.assertRaises(NoReverseMatch):
            self.make_url('book', book_index=0, page='xyzzy')


class StaticPdfBookTest(StaticBookTest):
    """
    Test the PDF static book view.
    """

    def test_book(self):
        # We can access a book.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0)
        response = self.client.get(url)
        self.assertContains(response, "Chapter 1 for PDF")
        self.assertNotContains(response, "options.chapterNum =")
        self.assertNotContains(response, "page=")

    def test_book_chapter(self):
        # We can access a book at a particular chapter.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0, chapter=2)
        response = self.client.get(url)
        self.assertContains(response, "Chapter 2 for PDF")
        self.assertContains(response, "file={}".format(PDF_BOOK['chapters'][1]['url']))
        self.assertNotContains(response, "page=")

    def test_book_page(self):
        # We can access a book at a particular page.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0, page=17)
        response = self.client.get(url)
        self.assertContains(response, "Chapter 1 for PDF")
        self.assertNotContains(response, "options.chapterNum =")
        self.assertContains(response, "page=17")

    def test_book_chapter_page(self):
        # We can access a book at a particular chapter and page.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0, chapter=2, page=17)
        response = self.client.get(url)
        self.assertContains(response, "Chapter 2 for PDF")
        self.assertContains(response, "file={}".format(PDF_BOOK['chapters'][1]['url']))
        self.assertContains(response, "page=17")

    def test_bad_book_id(self):
        # If the book id isn't an int, we'll get a 404.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        with self.assertRaises(NoReverseMatch):
            self.make_url('pdf_book', book_index='fooey', chapter=1)

    def test_out_of_range_book_id(self):
        # If we have one book, asking for the second book will fail with a 404.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=1, chapter=1)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_no_book(self):
        # If we have no books, asking for the first book will fail with a 404.
        self.make_course()
        url = self.make_url('pdf_book', book_index=0, chapter=1)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_chapter_xss(self):
        # The chapter in the URL used to go right on the page.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        # It's no longer possible to use a non-integer chapter.
        with self.assertRaises(NoReverseMatch):
            self.make_url('pdf_book', book_index=0, chapter='xyzzy')

    def test_page_xss(self):
        # The page in the URL used to go right on the page.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        # It's no longer possible to use a non-integer page.
        with self.assertRaises(NoReverseMatch):
            self.make_url('pdf_book', book_index=0, page='xyzzy')

    def test_chapter_page_xss(self):
        # The page in the URL used to go right on the page.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        # It's no longer possible to use a non-integer page and a non-integer chapter.
        with self.assertRaises(NoReverseMatch):
            self.make_url('pdf_book', book_index=0, chapter='fooey', page='xyzzy')

    def test_static_url_map_contentstore(self):
        """
        This ensure static  URL mapping is happening properly for
        a course that uses the contentstore
        """
        self.make_course(pdf_textbooks=[PORTABLE_PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0, chapter=1)
        response = self.client.get(url)
        self.assertNotContains(response, 'file={}'.format(PORTABLE_PDF_BOOK['chapters'][0]['url']))
        self.assertContains(response, 'file=/c4x/{0.org}/{0.course}/asset/{1}'.format(
            self.course.location,
            PORTABLE_PDF_BOOK['chapters'][0]['url'].replace('/static/', '')))

    def test_static_url_map_static_asset_path(self):
        """
        Like above, but used when the course has set a static_asset_path
        """
        self.make_course(pdf_textbooks=[PORTABLE_PDF_BOOK], static_asset_path='awesomesauce')
        url = self.make_url('pdf_book', book_index=0, chapter=1)
        response = self.client.get(url)
        self.assertNotContains(response, 'file={}'.format(PORTABLE_PDF_BOOK['chapters'][0]['url']))
        self.assertNotContains(response, 'file=/c4x/{0.org}/{0.course}/asset/{1}'.format(
            self.course.location,
            PORTABLE_PDF_BOOK['chapters'][0]['url'].replace('/static/', '')))
        self.assertContains(response, 'file=/static/awesomesauce/{}'.format(
            PORTABLE_PDF_BOOK['chapters'][0]['url'].replace('/static/', '')))

    def test_invalid_chapter_id(self):
        """
        Test that 1st chapter is displayed to the user when an invalid chapter id is provided
        """
        self.make_course(pdf_textbooks=[PDF_BOOK])
        invalid_chapter = len(PDF_BOOK['chapters']) + 1
        url = self.make_url('pdf_book', book_index=0, chapter=invalid_chapter)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Chapter 1 for PDF")


class StaticHtmlBookTest(StaticBookTest):
    """
    Test the HTML static book view.
    """

    def test_book(self):
        # We can access a book.
        self.make_course(html_textbooks=[HTML_BOOK])
        url = self.make_url('html_book', book_index=0)
        response = self.client.get(url)
        self.assertContains(response, "Chapter 1 for HTML")
        self.assertNotContains(response, "options.chapterNum =")

    def test_book_chapter(self):
        # We can access a book at a particular chapter.
        self.make_course(html_textbooks=[HTML_BOOK])
        url = self.make_url('html_book', book_index=0, chapter=2)
        response = self.client.get(url)
        self.assertContains(response, "Chapter 2 for HTML")
        self.assertContains(response, "options.chapterNum = 2;")

    def test_bad_book_id(self):
        # If we have one book, asking for the second book will fail with a 404.
        self.make_course(html_textbooks=[HTML_BOOK])
        url = self.make_url('html_book', book_index=1, chapter=1)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_no_book(self):
        # If we have no books, asking for the first book will fail with a 404.
        self.make_course()
        url = self.make_url('html_book', book_index=0, chapter=1)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_chapter_xss(self):
        # The chapter in the URL used to go right on the page.
        self.make_course(pdf_textbooks=[HTML_BOOK])
        # It's no longer possible to use a non-integer chapter.
        with self.assertRaises(NoReverseMatch):
            self.make_url('html_book', book_index=0, chapter='xyzzy')