from __future__ import unicode_literals
import os
import json
import mock
import httpretty
from httpretty import GET, POST
from ddt import ddt, data
try:
    import unittest2 as unittest # Python2.6
except ImportError:
    import unittest

import trovebox

class GetOrPost(object):
    """Helper class to call the correct (GET/POST) method"""
    def __init__(self, client, method):
        self.client = client
        self.method = method

    def call(self, *args, **kwds):
        if self.method == GET:
            return self.client.get(*args, **kwds)
        elif self.method == POST:
            return self.client.post(*args, **kwds)
        else:
            raise ValueError("unknown method: %s" % self.method)

@ddt
class TestHttp(unittest.TestCase):
    test_host = "test.example.com"
    test_endpoint = "test.json"
    test_uri = "http://%s/%s" % (test_host, test_endpoint)
    test_data = {"message": "Test Message",
                 "code": 200,
                 "result": "Test Result"}
    test_oauth = {"consumer_key": "dummy",
                  "consumer_secret": "dummy",
                  "token": "dummy",
                  "token_secret": "dummy"}
    test_file = os.path.join("tests", "unit", "data", "test_file.txt")


    def setUp(self):
        self.client = trovebox.Trovebox(host=self.test_host,
                                        **self.test_oauth)

    def _register_uri(self, method, uri=test_uri, data=None, body=None,
                      **kwds):
        """Convenience wrapper around httpretty.register_uri"""
        if data is None:
            data = self.test_data
        if body is None:
            body = json.dumps(data)
        httpretty.register_uri(method, uri=uri, body=body, **kwds)

    @staticmethod
    def _last_request():
        """This is a temporary measure until httpretty PR#59 is merged"""
        return httpretty.httpretty.last_request

    def test_attributes(self):
        """Check that the host attribute has been set correctly"""
        self.assertEqual(self.client.host, self.test_host)
        self.assertEqual(self.client.auth.host, self.test_host)

    @httpretty.activate
    @data(GET, POST)
    def test_http_scheme(self, method):
        """Check that we can access hosts starting with 'http://'"""
        self._register_uri(method,
                           uri="http://test.example.com/%s" % self.test_endpoint)

        self.client = trovebox.Trovebox(host="http://test.example.com",
                                        **self.test_oauth)
        response = GetOrPost(self.client, method).call(self.test_endpoint)
        self.assertIn("OAuth", self._last_request().headers["authorization"])
        self.assertEqual(response, self.test_data)
        self.assertEqual(self.client.last_url,
                         "http://test.example.com/%s" % self.test_endpoint)
        self.assertEqual(self.client.last_response.json(), self.test_data)

    @httpretty.activate
    @data(GET, POST)
    def test_no_scheme(self, method):
        """Check that we can access hosts without a 'http://' prefix"""
        self._register_uri(method,
                           uri="http://test.example.com/%s" % self.test_endpoint)

        self.client = trovebox.Trovebox(host="test.example.com",
                                        **self.test_oauth)
        response = GetOrPost(self.client, method).call(self.test_endpoint)
        self.assertIn("OAuth", self._last_request().headers["authorization"])
        self.assertEqual(response, self.test_data)
        self.assertEqual(self.client.last_url,
                         "http://test.example.com/%s" % self.test_endpoint)
        self.assertEqual(self.client.last_response.json(), self.test_data)

    @httpretty.activate
    @data(GET, POST)
    def test_https_scheme(self, method):
        """Check that we can access hosts starting with 'https://'"""
        self._register_uri(method,
                           uri="https://test.example.com/%s" % self.test_endpoint)

        self.client = trovebox.Trovebox(host="https://test.example.com",
                                        **self.test_oauth)
        response = GetOrPost(self.client, method).call(self.test_endpoint)
        self.assertIn("OAuth", self._last_request().headers["authorization"])
        self.assertEqual(response, self.test_data)
        self.assertEqual(self.client.last_url,
                         "https://test.example.com/%s" % self.test_endpoint)
        self.assertEqual(self.client.last_response.json(), self.test_data)

    @httpretty.activate
    @data(GET, POST)
    def test_endpoint_leading_slash(self, method):
        """Check that an endpoint with a leading slash is constructed correctly"""
        self._register_uri(method,
                           uri="http://test.example.com/%s" % self.test_endpoint)

        self.client = trovebox.Trovebox(host="http://test.example.com",
                                        **self.test_oauth)
        response = GetOrPost(self.client, method).call("/" + self.test_endpoint)
        self.assertIn("OAuth", self._last_request().headers["authorization"])
        self.assertEqual(response, self.test_data)
        self.assertEqual(self.client.last_url,
                         "http://test.example.com/%s" % self.test_endpoint)
        self.assertEqual(self.client.last_response.json(), self.test_data)

    @httpretty.activate
    def test_get_with_parameters(self):
        """Check that the get method accepts parameters correctly"""
        self._register_uri(httpretty.GET)
        response = self.client.get(self.test_endpoint,
                                   foo="bar", spam="eggs")
        self.assertIn("OAuth", self._last_request().headers["authorization"])
        self.assertEqual(self._last_request().querystring["foo"], ["bar"])
        self.assertEqual(self._last_request().querystring["spam"], ["eggs"])
        self.assertEqual(response, self.test_data)
        self.assertEqual(self.client.last_url, self.test_uri)
        self.assertEqual(self.client.last_params, {"foo": b"bar",
                                                   "spam": b"eggs"})
        self.assertEqual(self.client.last_response.json(), self.test_data)

    @httpretty.activate
    def test_post_with_parameters(self):
        """Check that the post method accepts parameters correctly"""
        self._register_uri(httpretty.POST)
        response = self.client.post(self.test_endpoint,
                                   foo="bar", spam="eggs")
        self.assertIn(b"spam=eggs", self._last_request().body)
        self.assertIn(b"foo=bar", self._last_request().body)
        self.assertEqual(response, self.test_data)
        self.assertEqual(self.client.last_url, self.test_uri)
        self.assertEqual(self.client.last_params, {"foo": b"bar",
                                                   "spam": b"eggs"})
        self.assertEqual(self.client.last_response.json(), self.test_data)

    @httpretty.activate
    def test_get_without_oauth(self):
        """Check that the get method works without OAuth parameters"""
        self.client = trovebox.Trovebox(host=self.test_host)
        self._register_uri(httpretty.GET)
        response = self.client.get(self.test_endpoint)
        self.assertNotIn("authorization", self._last_request().headers)
        self.assertEqual(response, self.test_data)

    @httpretty.activate
    def test_post_without_oauth(self):
        """Check that the post method fails without OAuth parameters"""
        self.client = trovebox.Trovebox(host=self.test_host)
        self._register_uri(httpretty.POST)
        with self.assertRaises(trovebox.TroveboxError):
            self.client.post(self.test_endpoint)

    @httpretty.activate
    @data(GET, POST)
    def test_no_response_processing(self, method):
        """Check that get/post methods work with response processing disabled"""
        self._register_uri(method)
        response = GetOrPost(self.client, method).call(self.test_endpoint,
                                                       process_response=False)
        self.assertEqual(response, json.dumps(self.test_data))

    @httpretty.activate
    def test_get_parameter_processing(self):
        """Check that the parameter processing function is working"""
        self._register_uri(httpretty.GET)
        photo = trovebox.objects.photo.Photo(None, {"id": "photo_id"})
        album = trovebox.objects.album.Album(None, {"id": "album_id"})
        tag = trovebox.objects.tag.Tag(None, {"id": "tag_id"})
        self.client.get(self.test_endpoint,
                        photo=photo, album=album, tag=tag,
                        list_=[photo, album, tag],
                        list2=["1", False, 3],
                        unicode_list=["1", "2", "\xfcmlaut"],
                        boolean=True,
                        unicode_="\xfcmlaut")
        params = self._last_request().querystring
        self.assertEqual(params["photo"], ["photo_id"])
        self.assertEqual(params["album"], ["album_id"])
        self.assertEqual(params["tag"], ["tag_id"])
        self.assertEqual(params["list_"], ["photo_id,album_id,tag_id"])
        self.assertEqual(params["list2"], ["1,0,3"])
        self.assertIn(params["unicode_list"], [["1,2,\xc3\xbcmlaut"], ["1,2,\xfcmlaut"]])
        self.assertEqual(params["boolean"], ["1"])
        self.assertIn(params["unicode_"], [["\xc3\xbcmlaut"], ["\xfcmlaut"]])

    @httpretty.activate
    @data(GET, POST)
    def test_api_version(self, method):
        """Check that an API version can be specified"""
        self.client = trovebox.Trovebox(host=self.test_host, **self.test_oauth)
        self.client.configure(api_version=1)
        self._register_uri(method,
                           uri="http://%s/v1/%s" % (self.test_host,
                                                    self.test_endpoint))
        GetOrPost(self.client, method).call(self.test_endpoint)

    @mock.patch.object(trovebox.http.requests, 'Session')
    @data(GET, POST)
    def test_ssl_verify_disabled(self, method, mock_session):
        """Check that SSL verification can be disabled for the get method"""
        session = mock_session.return_value.__enter__.return_value
        session.get.return_value.text = "response text"
        session.get.return_value.status_code = 200
        session.get.return_value.json.return_value = self.test_data
        # Handle either post or get
        session.post = session.get

        self.client = trovebox.Trovebox(host=self.test_host, **self.test_oauth)
        self.client.configure(ssl_verify=False)
        GetOrPost(self.client, method).call(self.test_endpoint)
        self.assertEqual(session.verify, False)

    @httpretty.activate
    def test_post_file(self):
        """Check that a file can be posted"""
        self._register_uri(httpretty.POST)
        with open(self.test_file, 'rb') as in_file:
            response = self.client.post(self.test_endpoint,
                                        files={"file": in_file})
        self.assertEqual(response, self.test_data)
        body = str(self._last_request().body)
        self.assertIn("Content-Disposition: form-data; "+
                      "name=\"file\"; filename=\"test_file.txt\"", body)
        self.assertIn("Test File", str(body))


    @httpretty.activate
    def test_post_file_parameters_are_sent_as_querystring(self):
        """
        Check that parameters are send as a query string
        when a file is posted
        """
        self._register_uri(httpretty.POST)
        with open(self.test_file, 'rb') as in_file:
            response = self.client.post(self.test_endpoint, foo="bar",
                                        files={"file": in_file})
        self.assertEqual(response, self.test_data)
        self.assertEqual(self._last_request().querystring["foo"], ["bar"])
