import base64
import unittest
import tempfile
import shutil
import io
import os

from unittest.mock import patch
from flask_testing import TestCase

import main


class Mat2WebTestCase(TestCase):
    def create_app(self):
        os.environ.setdefault('MAT2_ALLOW_ORIGIN_WHITELIST', 'origin1.gnu origin2.gnu')
        self.upload_folder = tempfile.mkdtemp()
        app = main.create_app(
            test_config={
                'TESTING': True,
                'UPLOAD_FOLDER': self.upload_folder
            }
        )
        return app

    def tearDown(self):
        shutil.rmtree(self.upload_folder)

    def test_get_root(self):
        rv = self.client.get('/')
        self.assertIn(b'mat2-web', rv.data)

    def test_check_mimetypes(self):
        rv = self.client.get('/')
        self.assertIn(b'.torrent', rv.data)
        self.assertIn(b'.ods', rv.data)

    def test_get_download_dangerous_file(self):
        rv = self.client.get('/download/1337/aabb/\..\filename')
        self.assertEqual(rv.status_code, 302)

    def test_get_download_without_key_file(self):
        rv = self.client.get('/download/non_existant')
        self.assertEqual(rv.status_code, 404)

    def test_get_download_nonexistant_file(self):
        rv = self.client.get('/download/1337/aabb/non_existant')
        self.assertEqual(rv.status_code, 302)

    def test_get_upload_without_file(self):
        rv = self.client.post('/')
        self.assertEqual(rv.status_code, 302)

    def test_get_upload_empty_file(self):
        rv = self.client.post('/',
                data=dict(
                    file=(io.BytesIO(b""), 'test.pdf'),
                    ), follow_redirects=False)
        self.assertEqual(rv.status_code, 302)

    def test_get_upload_bad_file_type(self):
        rv = self.client.post('/',
                data=dict(
                    file=(io.BytesIO(b"1,2,3 \n 4,5,6"), 'test.csv'),
                    ), follow_redirects=False)
        self.assertEqual(rv.status_code, 200)
        self.assertIn(b'The type text/csv could not be cleaned', rv.data)

    def test_get_upload_empty_file_redir(self):
        rv = self.client.post('/',
                data=dict(
                    file=(io.BytesIO(b""), 'test.pdf'),
                    ), follow_redirects=True)
        self.assertIn(b'The filetype is not supported',
                rv.data)
        self.assertEqual(rv.status_code, 200)

    def test_get_upload_no_selected_file(self):
        rv = self.client.post('/',
                           data=dict(
                               file=(io.BytesIO(b""), ''),
                           ), follow_redirects=True)
        self.assertIn(b'No selected file',
                      rv.data)
        self.assertEqual(rv.status_code, 200)

    def test_failed_cleaning(self):
        zip_file_bytes = base64.b64decode(
            'UEsDBBQACAAIAPicPE8AAAAAAAAAAAAAAAAXACAAZmFpbGluZy5ub3Qtd29ya2luZy1le'
            'HRVVA0AB+Saj13kmo9d5JqPXXV4CwABBOkDAAAE6QMAAAMAUEsHCAAAAAACAAAAAAAAAFBL'
            'AwQUAAgACAD6nDxPAAAAAAAAAAAAAAAACQAgAHRlc3QuanNvblVUDQAH6JqPXeiaj13omo9d'
            'dXgLAAEE6QMAAATpAwAAAwBQSwcIAAAAAAIAAAAAAAAAUEsBAhQDFAAIAAgA+Jw8TwAAAAACA'
            'AAAAAAAABcAIAAAAAAAAAAAAKSBAAAAAGZhaWxpbmcubm90LXdvcmtpbmctZXh0VVQNAAfkmo9'
            'd5JqPXeSaj111eAsAAQTpAwAABOkDAABQSwECFAMUAAgACAD6nDxPAAAAAAIAAAAAAAAACQAgA'
            'AAAAAAAAAAApIFnAAAAdGVzdC5qc29uVVQNAAfomo9d6JqPXeiaj111eAsAAQTpAwAABOkDAAB'
            'QSwUGAAAAAAIAAgC8AAAAwAAAAAAA'
        )
        rv = self.client.post('/',
                           data=dict(
                               file=(io.BytesIO(zip_file_bytes), 'test.zip'),
                           ), follow_redirects=True)
        self.assertIn(b'Unable to clean', rv.data)
        self.assertEqual(rv.status_code, 200)

    def test_get_upload_no_file_name(self):
        rv = self.client.post('/',
                data=dict(
                    file=(io.BytesIO(b"aaa")),
                    ), follow_redirects=True)
        self.assertIn(b'No file part', rv.data)
        self.assertEqual(rv.status_code, 200)

    def test_get_upload_harmless_file(self):
        rv = self.client.post(
            '/',
            data=dict(
                file=(io.BytesIO(b"Some text"), 'test.txt'),
            ),
            follow_redirects=True
        )
        download_uri = self.get_context_variable('download_uri')
        self.assertIn('/test.cleaned.txt', download_uri)
        self.assertEqual(rv.status_code, 200)
        self.assertNotIn('Access-Control-Allow-Origin', rv.headers)

        rv = self.client.get(download_uri)
        self.assertEqual(rv.status_code, 200)

        rv = self.client.get(download_uri)
        self.assertEqual(rv.status_code, 302)

    def test_upload_wrong_hash_or_secret(self):
        rv = self.client.post(
            '/',
            data=dict(
                file=(io.BytesIO(b"Some text"), 'test.txt'),
            ),
            follow_redirects=True
        )

        download_uri = self.get_context_variable('download_uri')

        self.assertIn('/test.cleaned.txt', download_uri)
        self.assertIn('/download', download_uri)
        self.assertEqual(rv.status_code, 200)

        uri_parts = download_uri.split("/")
        self.assertEqual(len(uri_parts[2]), len(uri_parts[3]))
        self.assertEqual(64, len(uri_parts[2]))

        key_uri_parts = uri_parts
        key_uri_parts[2] = '70623619c'
        rv = self.client.get("/".join(key_uri_parts))
        self.assertEqual(rv.status_code, 302)

        key_uri_parts = uri_parts
        key_uri_parts[3] = '70623619c'
        rv = self.client.get("/".join(key_uri_parts))
        self.assertEqual(rv.status_code, 302)

    @patch('matweb.file_removal_scheduler.random.randint')
    def test_upload_leftover(self, randint_mock):
        randint_mock.return_value = 0
        os.environ['MAT2_MAX_FILE_AGE_FOR_REMOVAL'] = '0'
        app = main.create_app()
        self.upload_folder = tempfile.mkdtemp()
        app.config.update(
            TESTING=True,
            UPLOAD_FOLDER=self.upload_folder
        )
        app = app.test_client()

        request = self.client.post('/',
                           data=dict(
                               file=(io.BytesIO(b"Some text"), 'test.txt'),
                           ), follow_redirects=True)
        self.assertEqual(request.status_code, 200)

        request = app.get(self.get_context_variable('download_uri'))
        self.assertEqual(302, request.status_code)
        os.environ['MAT2_MAX_FILE_AGE_FOR_REMOVAL'] = str(15*60)

    def test_info_page(self):
        rv = self.client.get('/info')
        self.assertIn(b'What are metadata?', rv.data)
        self.assertIn(b'.jpg', rv.data)
        self.assertIn(b'.mp2', rv.data)
        self.assertEqual(rv.status_code, 200)

    def test_get_upload_naughty_input(self):
        rv = self.client.post(
            '/',
            data=dict(
                file=(io.BytesIO(b"a"), '﷽'),
            ),
            follow_redirects=True
        )
        self.assertEqual(rv.status_code, 200)
        self.assertIn(b'Invalid Filename', rv.data)


if __name__ == '__main__':
    unittest.main()