import requests
import json
import pprint


class BaseProxy(object):

    def __init__(self, host=None, port=None, session_source=None, debug=0):
        self.rebase_kwargs = {}
        self._tpl_url = getattr(self.__class__, 'url', '/')
        self.url = None if '{' in self._tpl_url else self._tpl_url
        self.session_source = session_source
        self.csrf = {'header': 'X-CSRFToken', 'cookie': 'csrftoken'}
        self.protocol = 'http'
        self.host = host if host is not None else 'localhost'
        self.port = port if port is not None else 8000
        self.result = None
        self.cookies = {}
        self.headers = {}
        self.debug = debug

    def _pull_cookie(self, cookie):
        if self.session_source:
            _cookie = self.session_source.cookies.get(cookie, None)
            if _cookie:
                return {cookie: _cookie}
        return {}

    def _pull_sessionid(self):
        return self._pull_cookie('sessionid')

    def _pull_csrftoken(self):
        return self._pull_cookie('csrftoken')

    def _csrf_header(self):
        ext_cookie = self._pull_csrftoken().get('csrftoken', None)
        csrfcookie = self.cookies.get('csrftoken', ext_cookie)
        if csrfcookie:
            return {self.csrf['header']: csrfcookie}
        return {}

    def _check_rebased(self):
        return self.url is not None

    def _create_request_kwargs(self, data, params=None, headers=None, cookies=None, files=None):
        kwargs = {
            'data': data,
            'headers': headers,
            'cookies': cookies,
            'files': files,
            'params': params,
        }
        if data is None or data == {}:
            kwargs.pop('data', None)
        else:
            kwargs['data'] = json.dumps(kwargs['data'])
        if files is None:
            kwargs.pop('files', None)
        if params is None:
            kwargs.pop('params', None)
        if headers is None:
            kwargs['headers'] = {}
        kwargs['headers'].update(self.headers)
        kwargs['headers'].update(self._csrf_header())
        if kwargs.get('data', None) is not None:
            kwargs['headers'].update(self.json_content_type())
        if cookies is None:
            kwargs['cookies'] = {}
        kwargs['cookies'].update(self.cookies)
        kwargs['cookies'].update(self._pull_sessionid())
        kwargs['cookies'].update(self._pull_csrftoken())
        return kwargs

    def _cache_rebase(self, *args, **kwargs):
        self.rebase_args = args
        self.rebase_kwargs.update(kwargs)

    def _operation(self, url, data=None, params=None, headers=None,
        cookies=None, files=None, fn=requests.get):
        self.validate()
        _kwargs = self._create_request_kwargs(data, params, headers, cookies, files)
        if self.debug > 0:
            print "{} {}".format(*[fn.func_name.upper(), url])
        if self.debug > 1:
            pprint.pprint(_kwargs.get('data', {}))
        if self.debug > 2:
            pprint.pprint(_kwargs)
        self.result = fn(url, **_kwargs)
        self.cookies = dict(self.result.cookies)
        if self.debug > 3:
            print self.result.content
        return self.result.status_code

    def json_content_type(self):
        return {'content-type': 'application/json'}

    def root(self):
        return self.protocol + '://' + self.host.strip('/') + ':' + str(self.port) + '/'

    def full_url(self):
        if self.url is None:
            return self.root()
        return self.root().rstrip('/') + '/' + self.url.lstrip('/')

    def validate(self):
        if not self._check_rebased():
            raise ValueError('proxy not properly rebased.')

    def rebase(self, *args, **kwargs):
        self._cache_rebase(*args, **kwargs)
        self.url = self._tpl_url.format(*args, **kwargs)
        return self

    def flush_session(self):
        self.cookies = {}

    def get(self, data=None, params=None, headers=None, cookies=None):
        return self._operation(self.full_url(), fn=requests.get,
            data=data, params=params, headers=headers, cookies=cookies)

    def post(self, data=None, params=None, headers=None, cookies=None, files=None):
        return self._operation(self.full_url(), fn=requests.post,
            data=data, params=params, headers=headers, cookies=cookies, files=files)

    def put(self, data=None, params=None, headers=None, cookies=None, files=None):
        return self._operation(self.full_url(), fn=requests.put,
            data=data, params=params, headers=headers, cookies=cookies, files=files)

    def patch(self, data=None, params=None, headers=None, cookies=None, files=None):
        return self._operation(self.full_url(), fn=requests.patch,
            data=data, params=params, headers=headers, cookies=cookies, files=files)

    def delete(self, data=None, params=None, headers=None, cookies=None):
        return self._operation(self.full_url(), fn=requests.delete,
            data=data, params=params, headers=headers, cookies=cookies)


class ResourceProxy(BaseProxy):

    def __init__(self, host=None, port=None, session_source=None, debug=0):
        super(ResourceProxy, self).__init__(
            host=host, port=port, session_source=session_source, debug=debug)
        self._list_tpl_url = self._tpl_url
        detail_url = getattr(self.__class__, 'detail', '/')
        self._detail_tpl_url = self._tpl_url.rstrip('/') + '/' + detail_url
        self.list_url = None
        self.detail_url = None

    def _check_rebased(self):
        return None not in (self.list_url, self.detail_url)

    def full_list_url(self):
        if self.list_url is None:
            return self.root()
        return self.root().rstrip('/') + '/' + self.list_url.lstrip('/')

    def full_detail_url(self):
        if self.detail_url is None:
            return self.full_list_url()
        return self.root().rstrip('/') + '/' + self.detail_url.lstrip('/')

    def full_url(self):
        return self.full_detail_url()

    def rebase(self, *args, **kwargs):
        self._cache_rebase(*args, **kwargs)
        _kwargs = self.rebase_kwargs
        _kwargs.update(kwargs)
        self.list_url = self._list_tpl_url.format(*args, **_kwargs)
        try:
            self.detail_url = self._detail_tpl_url.format(*args, **_kwargs)
        except KeyError:
            self.detail_url = ''
        return self

    def list(self, data=None, params=None, headers=None, cookies=None):
        return self._operation(self.full_list_url(), fn=requests.get,
            data=data, params=params, headers=headers, cookies=cookies)

    def post(self, data=None, params=None, headers=None, cookies=None, files=None):
        return self._operation(self.full_list_url(), fn=requests.post,
            data=data, params=params, headers=headers, cookies=cookies, files=files)
