# -*- coding: utf-8 -*-

from __future__ import unicode_literals

import time

from json import loads

from django.contrib.auth import get_user_model

from django.core.urlresolvers import reverse
from django.test import override_settings
from django.utils.encoding import force_text
from django.utils.six import BytesIO

from rest_framework import status
from rest_framework.test import APITestCase

from user_management.tests.literals import (
    TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)

from .literals import (
    TEST_DOCUMENT_FILENAME, TEST_DOCUMENT_DESCRIPTION_EDITED,
    TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_CHECKSUM,
    TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_VERSION_COMMENT_EDITED
)
from ..models import Document, DocumentType, HASH_FUNCTION


class DocumentTypeAPITestCase(APITestCase):
    """
    Test the document type API endpoints
    """

    def setUp(self):
        self.admin_user = get_user_model().objects.create_superuser(
            username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
            password=TEST_ADMIN_PASSWORD
        )

        self.client.login(
            username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
        )

    def tearDown(self):
        self.admin_user.delete()

    def test_document_type_create(self):
        self.assertEqual(DocumentType.objects.all().count(), 0)

        response = self.client.post(
            reverse('rest_api:documenttype-list'), data={
                'label': TEST_DOCUMENT_TYPE
            }
        )

        self.assertEqual(response.status_code, 201)
        self.assertEqual(DocumentType.objects.all().count(), 1)
        self.assertEqual(
            DocumentType.objects.all().first().label, TEST_DOCUMENT_TYPE
        )

    def test_document_type_edit_via_put(self):
        document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE)

        self.client.put(
            reverse('rest_api:documenttype-detail', args=(document_type.pk,)),
            {'label': TEST_DOCUMENT_TYPE + 'edited'}
        )

        document_type = DocumentType.objects.get(pk=document_type.pk)
        self.assertEqual(document_type.label, TEST_DOCUMENT_TYPE + 'edited')

    def test_document_type_edit_via_patch(self):
        document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE)

        self.client.patch(
            reverse('rest_api:documenttype-detail', args=(document_type.pk,)),
            {'label': TEST_DOCUMENT_TYPE + 'edited'}
        )

        document_type = DocumentType.objects.get(pk=document_type.pk)
        self.assertEqual(document_type.label, TEST_DOCUMENT_TYPE + 'edited')

    def test_document_type_delete(self):
        document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE)

        self.client.delete(
            reverse('rest_api:documenttype-detail', args=(document_type.pk,))
        )

        self.assertEqual(DocumentType.objects.all().count(), 0)


@override_settings(OCR_AUTO_OCR=False)
class DocumentAPITestCase(APITestCase):
    def setUp(self):
        self.admin_user = get_user_model().objects.create_superuser(
            username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
            password=TEST_ADMIN_PASSWORD
        )

        self.client.login(
            username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
        )

        self.document_type = DocumentType.objects.create(
            label=TEST_DOCUMENT_TYPE
        )

    def tearDown(self):
        self.admin_user.delete()
        self.document_type.delete()

    def _upload_document(self):
        with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
            self.document = self.document_type.new_document(
                file_object=file_object,
            )

    def test_document_upload(self):
        with open(TEST_DOCUMENT_PATH) as file_descriptor:
            response = self.client.post(
                reverse('rest_api:document-list'), {
                    'document_type': self.document_type.pk,
                    'file': file_descriptor
                }
            )

        document_data = loads(response.content)

        self.assertEqual(
            response.status_code, status.HTTP_201_CREATED
        )
        self.assertEqual(Document.objects.count(), 1)

        document = Document.objects.first()

        self.assertEqual(document.pk, document_data['id'])

        self.assertEqual(document.versions.count(), 1)

        self.assertEqual(document.exists(), True)
        self.assertEqual(document.size, 272213)

        self.assertEqual(document.file_mimetype, 'application/pdf')
        self.assertEqual(document.file_mime_encoding, 'binary')
        self.assertEqual(document.label, TEST_DOCUMENT_FILENAME)
        self.assertEqual(
            document.checksum,
            'c637ffab6b8bb026ed3784afdb07663fddc60099853fae2be93890852a69ecf3'
        )
        self.assertEqual(document.page_count, 47)

    def test_document_new_version_upload(self):
        with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
            document = self.document_type.new_document(
                file_object=file_object,
            )

        # Artifical delay since MySQL doesn't store microsecond data in
        # timestamps. Version timestamp is used to determine which version
        # is the latest.
        time.sleep(1)
        with open(TEST_DOCUMENT_PATH) as file_descriptor:
            response = self.client.post(
                reverse(
                    'rest_api:document-version-list', args=(document.pk,)
                ), {
                    'comment': '',
                    'file': file_descriptor,
                }
            )

        self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)

        self.assertEqual(document.versions.count(), 2)
        self.assertEqual(document.exists(), True)
        self.assertEqual(document.size, 272213)
        self.assertEqual(document.file_mimetype, 'application/pdf')
        self.assertEqual(document.file_mime_encoding, 'binary')
        self.assertEqual(
            document.checksum,
            'c637ffab6b8bb026ed3784afdb07663fddc60099853fae2be93890852a69ecf3'
        )
        self.assertEqual(document.page_count, 47)

    def test_document_version_revert(self):
        with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
            document = self.document_type.new_document(
                file_object=file_object,
            )

        # Needed by MySQL as milliseconds value is not store in timestamp field
        time.sleep(1)

        with open(TEST_DOCUMENT_PATH) as file_object:
            document.new_version(file_object=file_object)

        self.assertEqual(document.versions.count(), 2)

        document_version = document.versions.first()

        self.client.post(
            reverse(
                'rest_api:documentversion-revert', args=(document_version.pk,)
            )
        )

        self.assertEqual(document.versions.count(), 1)

        self.assertEqual(document_version, document.latest_version)

    def test_document_download(self):
        with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
            document = self.document_type.new_document(
                file_object=file_object,
            )

        response = self.client.get(
            reverse(
                'rest_api:document-download', args=(document.pk,)
            )
        )
        buf = BytesIO()
        buf.write(response.content)

        self.assertEqual(
            HASH_FUNCTION(buf.getvalue()), TEST_SMALL_DOCUMENT_CHECKSUM
        )

        del(buf)

    def test_document_version_download(self):
        with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
            document = self.document_type.new_document(
                file_object=file_object,
            )

        response = self.client.get(
            reverse(
                'rest_api:documentversion-download',
                args=(document.latest_version.pk,)
            )
        )
        buf = BytesIO()
        buf.write(response.content)

        self.assertEqual(
            HASH_FUNCTION(buf.getvalue()), TEST_SMALL_DOCUMENT_CHECKSUM
        )

        del(buf)

    def test_document_version_edit_via_patch(self):
        self._upload_document()
        response = self.client.patch(
            reverse(
                'rest_api:documentversion-detail',
                args=(self.document.latest_version.pk,)
            ), data={'comment': TEST_DOCUMENT_VERSION_COMMENT_EDITED}
        )

        self.assertEqual(response.status_code, 200)
        self.document.latest_version.refresh_from_db()
        self.assertEqual(self.document.versions.count(), 1)
        self.assertEqual(
            self.document.latest_version.comment,
            TEST_DOCUMENT_VERSION_COMMENT_EDITED
        )

    def test_document_version_edit_via_put(self):
        self._upload_document()
        response = self.client.put(
            reverse(
                'rest_api:documentversion-detail',
                args=(self.document.latest_version.pk,)
            ), data={'comment': TEST_DOCUMENT_VERSION_COMMENT_EDITED}
        )

        self.assertEqual(response.status_code, 200)
        self.document.latest_version.refresh_from_db()
        self.assertEqual(self.document.versions.count(), 1)
        self.assertEqual(
            self.document.latest_version.comment,
            TEST_DOCUMENT_VERSION_COMMENT_EDITED
        )

    def test_document_comment_edit_via_patch(self):
        self._upload_document()
        response = self.client.patch(
            reverse(
                'rest_api:document-detail',
                args=(self.document.pk,)
            ), data={'description': TEST_DOCUMENT_DESCRIPTION_EDITED}
        )

        self.assertEqual(response.status_code, 200)
        self.document.refresh_from_db()
        self.assertEqual(
            self.document.description,
            TEST_DOCUMENT_DESCRIPTION_EDITED
        )

    def test_document_comment_edit_via_put(self):
        self._upload_document()
        response = self.client.put(
            reverse(
                'rest_api:document-detail',
                args=(self.document.pk,)
            ), data={'description': TEST_DOCUMENT_DESCRIPTION_EDITED}
        )

        self.assertEqual(response.status_code, 200)
        self.document.refresh_from_db()
        self.assertEqual(
            self.document.description,
            TEST_DOCUMENT_DESCRIPTION_EDITED
        )


@override_settings(OCR_AUTO_OCR=False)
class TrashedDocumentAPITestCase(APITestCase):
    def setUp(self):
        self.admin_user = get_user_model().objects.create_superuser(
            username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
            password=TEST_ADMIN_PASSWORD
        )

        self.client.login(
            username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
        )

        self.document_type = DocumentType.objects.create(
            label=TEST_DOCUMENT_TYPE
        )

    def tearDown(self):
        self.admin_user.delete()
        self.document_type.delete()

    def _upload_document(self):
        with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
            document = self.document_type.new_document(
                file_object=file_object,
            )

        return document

    def test_document_move_to_trash(self):
        document = self._upload_document()

        self.client.delete(
            reverse('rest_api:document-detail', args=(document.pk,))
        )

        self.assertEqual(Document.objects.count(), 0)
        self.assertEqual(Document.trash.count(), 1)

    def test_trashed_document_delete_from_trash(self):
        document = self._upload_document()
        document.delete()

        self.assertEqual(Document.objects.count(), 0)
        self.assertEqual(Document.trash.count(), 1)

        self.client.delete(
            reverse('rest_api:trasheddocument-detail', args=(document.pk,))
        )

        self.assertEqual(Document.trash.count(), 0)

    def test_trashed_document_detail_view(self):
        document = self._upload_document()
        document.delete()

        response = self.client.get(
            reverse('rest_api:trasheddocument-detail', args=(document.pk,))
        )

        self.assertEqual(response.data['uuid'], force_text(document.uuid))

    def test_trashed_document_list_view(self):
        document = self._upload_document()
        document.delete()

        response = self.client.get(
            reverse('rest_api:trasheddocument-list')
        )

        self.assertEqual(response.data['results'][0]['uuid'], force_text(document.uuid))

    def test_trashed_document_restore(self):
        document = self._upload_document()
        document.delete()

        self.client.post(
            reverse('rest_api:trasheddocument-restore', args=(document.pk,))
        )

        self.assertEqual(Document.trash.count(), 0)
        self.assertEqual(Document.objects.count(), 1)

    # TODO: def test_document_set_document_type(self):
    #    pass
