# coding=utf-8
from __future__ import unicode_literals

import pytz

from datetime import date, datetime
from django.http import HttpRequest
from enum import Enum
from hypothesis import given
import hypothesis.strategies as st
from uuid import UUID

from casepro.test import BaseCasesTest

from . import (
    safe_max, normalize, match_keywords, truncate, str_to_bool, json_encode,
    datetime_to_microseconds, microseconds_to_datetime, month_range,
    uuid_to_int)
from .middleware import JSONMiddleware


class UtilsTest(BaseCasesTest):

    def test_safe_max(self):
        self.assertEqual(safe_max(1, 2, 3), 3)
        self.assertEqual(safe_max(None, 2, None), 2)
        self.assertEqual(safe_max(None, None), None)
        self.assertEqual(safe_max(date(2012, 3, 6), date(2012, 5, 2), None), date(2012, 5, 2))

    def test_normalize(self):
        self.assertEqual(normalize("Mary  had\ta little lamb"), "mary had a little lamb")  # remove multiple spaces
        self.assertEqual(normalize("Gar\u00e7on"), "garc\u0327on")  # decomposed combined unicode chars (U+E7 = ç)

    def test_match_keywords(self):
        text = "Mary had a little lamb"
        self.assertFalse(match_keywords(text, []))
        self.assertFalse(match_keywords(text, ['sheep']))
        self.assertFalse(match_keywords(text, ['lambburger']))  # complete word matches only

        self.assertTrue(match_keywords(text, ['mary']))  # case-insensitive and start of string
        self.assertTrue(match_keywords(text, ['lamb']))  # end of string
        self.assertTrue(match_keywords(text, ['big', 'little']))  # one match, one mis-match
        self.assertTrue(match_keywords(text, ['little lamb']))  # spaces ok

    def test_truncate(self):
        self.assertEqual(truncate("Hello World", 8), "Hello...")
        self.assertEqual(truncate("Hello World", 8, suffix="_"), "Hello W_")
        self.assertEqual(truncate("Hello World", 98), "Hello World")

    def test_str_to_bool(self):
        self.assertFalse(str_to_bool("0"))
        self.assertFalse(str_to_bool("fALSe"))
        self.assertFalse(str_to_bool("N"))
        self.assertFalse(str_to_bool("No"))
        self.assertFalse(str_to_bool("x"))

        self.assertTrue(str_to_bool("1"))
        self.assertTrue(str_to_bool("TrUE"))
        self.assertTrue(str_to_bool("Y"))
        self.assertTrue(str_to_bool("YeS"))

    def test_microseconds_to_datetime(self):
        d1 = datetime(2015, 10, 9, 14, 48, 30, 123456, tzinfo=pytz.utc).astimezone(pytz.timezone("Africa/Kigali"))
        ms = datetime_to_microseconds(d1)
        d2 = microseconds_to_datetime(ms)
        self.assertEqual(d2, datetime(2015, 10, 9, 14, 48, 30, 123456, tzinfo=pytz.utc))

    def test_json_encode(self):
        class MyEnum(Enum):
            bar = 1

        class MyClass(object):
            def to_json(self):
                return dict(bar='X')

        data = [
            "string",
            datetime(2015, 10, 9, 14, 48, 30, 123456, tzinfo=pytz.utc).astimezone(pytz.timezone("Africa/Kigali")),
            MyEnum.bar,
            MyClass()
        ]

        self.assertEqual(json_encode(data), '["string", "2015-10-09T14:48:30.123456", "bar", {"bar": "X"}]')

    def test_month_range(self):
        d1 = datetime(2015, 10, 9, 14, 48, 30, 123456, tzinfo=pytz.utc).astimezone(pytz.timezone("Africa/Kigali"))

        self.assertEqual(month_range(0, now=d1), (datetime(2015, 10, 1, 0, 0, 0, 0, pytz.UTC),
                                                  datetime(2015, 11, 1, 0, 0, 0, 0, pytz.UTC)))
        self.assertEqual(month_range(1, now=d1), (datetime(2015, 11, 1, 0, 0, 0, 0, pytz.UTC),
                                                  datetime(2015, 12, 1, 0, 0, 0, 0, pytz.UTC)))
        self.assertEqual(month_range(-1, now=d1), (datetime(2015, 9, 1, 0, 0, 0, 0, pytz.UTC),
                                                   datetime(2015, 10, 1, 0, 0, 0, 0, pytz.UTC)))

    def test_uuid_to_int_range(self):
        '''Ensures that the integer returned will always be in the range
        [-2147483648, 2147483647].'''
        self.assertEqual(
            uuid_to_int(UUID(int=(2147483648 + 2147483647)).hex),
            2147483647)
        self.assertEqual(
            uuid_to_int(UUID(int=(2147483648 + 2147483647 + 1)).hex),
            -2147483648)

    @given(st.uuids())
    def test_uuid_to_int_property(self, uuid):
        '''Property based testing to ensure that the output of the function
        is always within the limits.'''
        self.assertTrue(uuid_to_int(uuid.hex) <= 2147483647)
        self.assertTrue(uuid_to_int(uuid.hex) >= -2147483648)


class MiddlewareTest(BaseCasesTest):
    def test_json(self):
        middleware = JSONMiddleware()

        # test with no content type header
        request = HttpRequest()
        request._body = '{"a":["b"]}'
        middleware.process_request(request)
        self.assertFalse(hasattr(request, 'json'))

        # test with JSON content type header
        request = HttpRequest()
        request._body = '{"a":["b"]}'
        request.META = {'CONTENT_TYPE': "application/json;charset=UTF-8"}
        middleware.process_request(request)
        self.assertEqual(request.json, {'a': ["b"]})

        # test with non-JSON content type header
        request = HttpRequest()
        request._body = '<b></b>'
        request.META = {'CONTENT_TYPE': "text/html"}
        middleware.process_request(request)
        self.assertFalse(hasattr(request, 'json'))


class ViewsTest(BaseCasesTest):
    def test_partials(self):
        response = self.url_get('unicef', '/partials/modal_confirm.html')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "[[ title ]]")

@@ -4040,27 +4040,17 @@
        %5B
--2147483648
+0
 , 214748
@@ -4115,37 +4115,24 @@
 t(UUID(int=(
-2147483648 + 
 2147483647))
@@ -4236,25 +4236,8 @@
 3648
- + 2147483647 + 1
 )).h
@@ -4253,27 +4253,17 @@
         
--2147483648
+0
 )%0A%0A    @
@@ -4557,19 +4557,9 @@
  %3E= 
--2147483648
+0
 )%0A%0A%0A

Update tests to test for >=0 lower limit