"""
Code shared by LocalCKAN, RemoteCKAN and TestCKAN
"""

import json

from ckanapi.errors import (CKANAPIError, NotAuthorized, NotFound,
    ValidationError, SearchQueryError, SearchError, SearchIndexError)

class ActionShortcut(object):
    """
    ActionShortcut(foo).bar(baz=2) <=> foo.call_action('bar', {'baz':2})

    An instance of this class is used as the .action attribute of
    LocalCKAN and RemoteCKAN instances to provide a short way to call
    actions, e.g::

        demo = RemoteCKAN('http://demo.ckan.org')
        pkg = demo.action.package_show(id='adur_district_spending')

    instead of::

        demo = RemoteCKAN('http://demo.ckan.org')
        pkg = demo.call_action('package_show', {'id':'adur_district_spending'})

    file-like values (objects with a 'read' attribute) are
    sent as file-uploads::

        demo = RemoteCKAN('http://demo.ckan.org', apikey='mykey')
        pkg = demo.action.resource_update(package_id='foo', upload=open(..))

    becomes::

        demo = RemoteCKAN('http://demo.ckan.org', apikey='mykey')
        pkg = demo.call_action('resource_update',
            {'package_id': 'foo'}, files={'upload': open(..)})

    """
    def __init__(self, ckan):
        self._ckan = ckan

    def __getattr__(self, name):
        def action(apikey=None, **kwargs):
            files = {}
            for k, v in kwargs.items():
                if hasattr(v, 'read'):
                    files[k] = v
            if files:
                nonfiles = dict((k, v) for k, v in kwargs.items()
                    if k not in files)
                return self._ckan.call_action(name, data_dict=nonfiles,
                    apikey=apikey, files=files)
            return self._ckan.call_action(name, data_dict=kwargs,
                                          apikey=apikey)
        return action


def prepare_action(action, data_dict=None, apikey=None, files=None):
    """
    Return action_url, data_json, http_headers
    """
    if not data_dict:
        data_dict = {}
    headers = {}
    if not files:
        data_dict = json.dumps(data_dict).encode('ascii')
        headers['Content-Type'] = 'application/json'
    if apikey:
        apikey = str(apikey)
        headers['X-CKAN-API-Key'] = apikey
        headers['Authorization'] = apikey
    url = 'api/action/' + action
    return url, data_dict, headers


def reverse_apicontroller_action(url, status, response):
    """
    Make an API call look like a direct action call by reversing the
    exception -> HTTP response translation that ApiController.action does
    """
    try:
        parsed = json.loads(response)
        if parsed.get('success'):
            return parsed['result']
        if hasattr(parsed, 'get'):
            err = parsed.get('error', {})
        else:
            err = {}
    except (AttributeError, ValueError):
        err = {}

    etype = err.get('__type')
    emessage = err.get('message', '').split(': ', 1)[-1]
    if etype == 'Search Query Error':
        # I refuse to eval(emessage), even if it would be more correct
        raise SearchQueryError(emessage)
    elif etype == 'Search Error':
        # I refuse to eval(emessage), even if it would be more correct
        raise SearchError(emessage)
    elif etype == 'Search Index Error':
        raise SearchIndexError(emessage)
    elif etype == 'Validation Error':
        raise ValidationError(err)
    elif etype == 'Not Found Error':
        raise NotFound(emessage)
    elif etype == 'Authorization Error':
        raise NotAuthorized(err)

    # don't recognize the error
    raise CKANAPIError(url, status, response)

@@ -475,58 +475,8 @@
 ::%0A%0A
-        demo = RemoteCKAN('http://demo.ckan.org')%0A
     
@@ -562,58 +562,8 @@
 ::%0A%0A
-        demo = RemoteCKAN('http://demo.ckan.org')%0A
     
@@ -643,17 +643,17 @@
 %7D)%0A%0A    
-f
+F
 ile-like
@@ -730,74 +730,8 @@
 ::%0A%0A
-        demo = RemoteCKAN('http://demo.ckan.org', apikey='mykey')%0A
     
@@ -823,74 +823,8 @@
 ::%0A%0A
-        demo = RemoteCKAN('http://demo.ckan.org', apikey='mykey')%0A
     
@@ -1050,29 +1050,16 @@
  action(
-apikey=None, 
 **kwargs
@@ -1366,32 +1366,52 @@
 all_action(name,
+%0A                   
  data_dict=nonfi
@@ -1438,23 +1438,8 @@
     
- apikey=apikey,
  fil
@@ -1516,65 +1516,8 @@
 args
-,%0A                                          apikey=apikey
 )%0A  

remove apikey from ActionShortcut (still available on call_action)