import base64
import httplib
from soaplib import client
from django.test.client import Client as DjangoTestClient


def Client(url, impl, username=None, password=None):
    """ soaplib test client

        url:        protocol://[username:password@]host[:port]/path
        impl:       soaplib web service description
        username:   for basic auth
        password:   for basic auth

        Usage example (with django soaplib webservice snippet)

        The service (app/view.py):
        from soaplib_handler import DjangoSoapApp, soapmethod, soap_types
        class HelloWorldService(DjangoSoapApp):
            __tns__ = 'http://my.namespace.org/soap/

            @soapmethod(soap_types.String, _returns soap_types.String)
            def hello(who):
                return 'Hello %s!'
        hello_world_service = HelloWorldService()        

        Your urls.py:
        urlpatterns = patterns(
            '',
            (r'^hello_world/', 'app.views.hello_world_service'),
            (r'^hello_world/service.wsdl', 'app.views.hello_world_service'),
        )

        Accessing the web service with patched soaplib client        
        >>> from django_soaplib_client import Client
        >>> cl = Client('http://localhost:8000/hello_service/', HelloWorldService())
        >>> cl.hello('buddy')
        Hello buddy!
        >>> cl = Client('http://localhost:8000/hello_service/', HelloWorldService(), 
        ...        'username', 'password') # with authentication
        >>>
    """
    if username:
        protocol, rest = url.split('://')
        url = '%s://%s:%s@%s' % (protocol, username, password or '', rest)
    return client.make_service_client(url, impl)


class FakeHTTPConnection(object):
    """ Replacement to root all requests to Django test client.
        host is something like: [username:password@]host[:port]
        soaplib client may not work with https.
    """

    username = ''
    password = ''

    def __init__(self, host):
        """ Initializer.

            host:port part is ignored
        """
        self._connection = DjangoTestClient()
        self._response = None
        if '@' in host:
            self.username, self.password = host.split('@')[0].split(':')

    def request(self, method, path, body=None, headers=None):
        """ Route request to Django test client.

            method:     GET, POST, etc.
            path:       path part of URL
            body:       text to send to the host
            headers:    headers dict: { header_name: (header, value), ...}
        """
        extra = {}
        if headers:
            for key, value in headers.items():
                key = 'HTTP_' + key.upper().replace('-', '_')
                extra[key] = value
        extra['REQUEST_METHOD'] = method
        if self.username:
            auth = '%s:%s' % (self.username, self.password)
            auth = 'Basic %s' % base64.encodestring(auth)
            auth = auth.strip()
            extra['HTTP_AUTHORIZATION'] = auth
        if body:
            resp = self._connection.post(path, body,
                content_type='dummy', **extra)
        else:
            resp = self._connection.get(path, **extra)
        self._response = resp

    def getresponse(self):
        """ Get reponse from Django. """
        return FakeHTTPResponse(self._response)

    def close(self):
        """ Close request and related response object. """
        if self._response:
            self._response.close()


class FakeHTTPResponse(object):
    """ Replacement to get response from Django test client. """

    def __init__(self, response):
        """ Initializer.

            response: Django HttpResponse object.
        """
        self._response = response
        self.status = response.status_code
        self.reason = httplib.responses[self.status]

    def read(self):
        """ Read response body (from Django test client). """
        return self._response.content

    def getheaders(self):
        """ Get response headers. """
        return self._response._headers.values()

class httplib_replacement:
    """ Replacement for monkey patching httplib.

        Web only need HTTPConnection.
    """
    HTTPConnection = FakeHTTPConnection


# monkey patch httlib in soaplib client with our fake class
client.httplib = httplib_replacement