from __future__ import unicode_literals

import os
from datetime import datetime
from itertools import islice

from django.http import FileResponse, HttpResponse, HttpResponseRedirect, JsonResponse, StreamingHttpResponse
from six import BytesIO

from channels import Channel
from channels.handler import AsgiHandler
from channels.signals import consumer_finished
from channels.tests import ChannelTestCase


class FakeAsgiHandler(AsgiHandler):
    """
    Handler subclass that just returns a premade response rather than
    go into the view subsystem.
    """

    chunk_size = 30

    def __init__(self, response):
        assert isinstance(response, (HttpResponse, StreamingHttpResponse))
        self._response = response
        consumer_finished.send(sender=self.__class__)
        super(FakeAsgiHandler, self).__init__()

    def get_response(self, request):
        return self._response


class HandlerTests(ChannelTestCase):
    """
    Tests that the handler works correctly and round-trips things into a
    correct response.
    """

    def test_basic(self):
        """
        Tests a simple request
        """
        # Make stub request and desired response
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = HttpResponse(b"Hi there!", content_type="text/plain")
        # Run the handler
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        # Make sure we got the right number of messages
        self.assertEqual(len(reply_messages), 1)
        reply_message = reply_messages[0]
        # Make sure the message looks correct
        self.assertEqual(reply_message["content"], b"Hi there!")
        self.assertEqual(reply_message["status"], 200)
        self.assertEqual(reply_message.get("more_content", False), False)
        self.assertEqual(
            reply_message["headers"],
            [
                (b"Content-Type", b"text/plain"),
            ],
        )

    def test_cookies(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = HttpResponse(b"Hi there!", content_type="text/plain")
        response.set_signed_cookie('foo', '1', expires=datetime.now())
        # Run the handler
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        # Make sure we got the right number of messages
        self.assertEqual(len(reply_messages), 1)
        reply_message = reply_messages[0]
        # Make sure the message looks correct
        self.assertEqual(reply_message["content"], b"Hi there!")
        self.assertEqual(reply_message["status"], 200)
        self.assertEqual(reply_message.get("more_content", False), False)
        self.assertEqual(reply_message["headers"][0], (b'Content-Type', b'text/plain'))
        self.assertIn('foo=', reply_message["headers"][1][1].decode())

    def test_headers(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = HttpResponse(b"Hi there!", content_type="text/plain")
        response['foo'] = 1
        response['bar'] = 1
        del response['bar']
        del response['nonexistant_key']
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        # Make sure we got the right number of messages
        self.assertEqual(len(reply_messages), 1)
        reply_message = reply_messages[0]
        # Make sure the message looks correct
        self.assertEqual(reply_message["content"], b"Hi there!")
        header_dict = dict(reply_messages[0]['headers'])
        self.assertEqual(header_dict[b'foo'].decode(), '1')
        self.assertNotIn('bar', header_dict)

    def test_large(self):
        """
        Tests a large response (will need chunking)
        """
        # Make stub request and desired response
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = HttpResponse(
            b"Thefirstthirtybytesisrighthereandhereistherest")
        # Run the handler
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        # Make sure we got the right number of messages
        self.assertEqual(len(reply_messages), 2)
        # Make sure the messages look correct
        self.assertEqual(reply_messages[0][
                         "content"], b"Thefirstthirtybytesisrighthere")
        self.assertEqual(reply_messages[0]["status"], 200)
        self.assertEqual(reply_messages[0]["more_content"], True)
        self.assertEqual(reply_messages[1]["content"], b"andhereistherest")
        self.assertEqual(reply_messages[1].get("more_content", False), False)

    def test_empty(self):
        """
        Tests an empty response
        """
        # Make stub request and desired response
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = HttpResponse(b"", status=304)
        # Run the handler
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True))
        )
        # Make sure we got the right number of messages
        self.assertEqual(len(reply_messages), 1)
        # Make sure the messages look correct
        self.assertEqual(reply_messages[0].get("content", b""), b"")
        self.assertEqual(reply_messages[0]["status"], 304)
        self.assertEqual(reply_messages[0]["more_content"], False)

    def test_empty_streaming(self):
        """
        Tests an empty streaming response
        """
        # Make stub request and desired response
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = StreamingHttpResponse([], status=304)
        # Run the handler
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True))
        )
        # Make sure we got the right number of messages
        self.assertEqual(len(reply_messages), 1)
        # Make sure the messages look correct
        self.assertEqual(reply_messages[0].get("content", b""), b"")
        self.assertEqual(reply_messages[0]["status"], 304)
        self.assertEqual(reply_messages[0]["more_content"], False)

    def test_chunk_bytes(self):
        """
        Makes sure chunk_bytes works correctly
        """
        # Empty string should still return one chunk
        result = list(FakeAsgiHandler.chunk_bytes(b""))
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0][0], b"")
        self.assertEqual(result[0][1], True)
        # Below chunk size
        result = list(FakeAsgiHandler.chunk_bytes(
            b"12345678901234567890123456789"))
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0][0], b"12345678901234567890123456789")
        self.assertEqual(result[0][1], True)
        # Exactly chunk size
        result = list(FakeAsgiHandler.chunk_bytes(
            b"123456789012345678901234567890"))
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0][0], b"123456789012345678901234567890")
        self.assertEqual(result[0][1], True)
        # Just above chunk size
        result = list(FakeAsgiHandler.chunk_bytes(
            b"123456789012345678901234567890a"))
        self.assertEqual(len(result), 2)
        self.assertEqual(result[0][0], b"123456789012345678901234567890")
        self.assertEqual(result[0][1], False)
        self.assertEqual(result[1][0], b"a")
        self.assertEqual(result[1][1], True)

    def test_iterator(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = HttpResponse(range(10))
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        self.assertEqual(len(reply_messages), 1)
        self.assertEqual(reply_messages[0]["content"], b"0123456789")

    def test_streaming_data(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = StreamingHttpResponse('Line: %s' % i for i in range(10))
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        self.assertEqual(len(reply_messages), 11)
        self.assertEqual(reply_messages[0]["content"], b"Line: 0")
        self.assertEqual(reply_messages[9]["content"], b"Line: 9")

    def test_real_file_response(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        current_dir = os.path.realpath(os.path.join(
            os.getcwd(), os.path.dirname(__file__)))
        response = FileResponse(
            open(os.path.join(current_dir, 'a_file'), 'rb'))
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        self.assertEqual(len(reply_messages), 2)
        self.assertEqual(response.getvalue(), b'')

    def test_bytes_file_response(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = FileResponse(BytesIO(b'sadfdasfsdfsadf'))
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        self.assertEqual(len(reply_messages), 2)

    def test_string_file_response(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = FileResponse('abcd')
        handler = FakeAsgiHandler(response)
        reply_messages = list(
            handler(self.get_next_message("test", require=True)))
        self.assertEqual(len(reply_messages), 5)

    def test_non_streaming_file_response(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = FileResponse(BytesIO(b'sadfdasfsdfsadf'))
        # This is to test the exception handling. This would only happening if
        # the StreamingHttpResponse was incorrectly subclassed.
        response.streaming = False

        handler = FakeAsgiHandler(response)
        with self.assertRaises(AttributeError):
            list(handler(self.get_next_message("test", require=True)))

    def test_unclosable_filelike_object(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })

        # This is a readable object that cannot be closed.
        class Unclosable:

            def read(self, n=-1):
                # Nothing to see here
                return b""

        response = FileResponse(Unclosable())
        handler = FakeAsgiHandler(response)
        reply_messages = list(islice(handler(self.get_next_message("test", require=True)), 5))
        self.assertEqual(len(reply_messages), 1)
        response.close()

    def test_json_response(self):
        Channel("test").send({
            "reply_channel": "test",
            "http_version": "1.1",
            "method": "GET",
            "path": b"/test/",
        })
        response = JsonResponse({'foo': (1, 2)})
        handler = FakeAsgiHandler(response)
        reply_messages = list(handler(self.get_next_message("test", require=True)))
        self.assertEqual(len(reply_messages), 1)
        self.assertEqual(reply_messages[0]['content'], b'{"foo": [1, 2]}')

    def test_redirect(self):
        for redirect_to in ['/', '..', 'https://example.com']:
            Channel("test").send({
                "reply_channel": "test",
                "http_version": "1.1",
                "method": "GET",
                "path": b"/test/",
            })
            response = HttpResponseRedirect(redirect_to)
            handler = FakeAsgiHandler(response)
            reply_messages = list(handler(self.get_next_message("test", require=True)))
            self.assertEqual(reply_messages[0]['status'], 302)
            header_dict = dict(reply_messages[0]['headers'])
            self.assertEqual(header_dict[b'Location'].decode(), redirect_to)
