import copy
import itertools
import logging

logger = logging.getLogger(__name__)


class OperationProxy(object):
    def __init__(self, service_proxy, operation_name):
        self._proxy = service_proxy
        self._op_name = operation_name

    @property
    def __doc__(self):
        return str(self._proxy._binding._operations[self._op_name])

    def __call__(self, *args, **kwargs):
        """Call the operation with the given args and kwargs.

        :rtype: zeep.xsd.CompoundValue

        """

        # Merge the default _soapheaders with the passed _soapheaders
        if self._proxy._client._default_soapheaders:
            op_soapheaders = kwargs.get('_soapheaders')
            if op_soapheaders:
                soapheaders = copy.deepcopy(self._proxy._client._default_soapheaders)
                if type(op_soapheaders) != type(soapheaders):
                    raise ValueError("Incompatible soapheaders definition")

                if isinstance(soapheaders, list):
                    soapheaders.extend(op_soapheaders)
                else:
                    soapheaders.update(op_soapheaders)
            else:
                soapheaders = self._proxy._client._default_soapheaders
            kwargs['_soapheaders'] = soapheaders

        return self._proxy._binding.send(
            self._proxy._client, self._proxy._binding_options,
            self._op_name, args, kwargs)


class ServiceProxy(object):
    def __init__(self, client, binding, **binding_options):
        self._client = client
        self._binding_options = binding_options
        self._binding = binding
        self._operations = {
            name: OperationProxy(self, name) for name in self._binding.all()
        }

    def __getattr__(self, key):
        """Return the OperationProxy for the given key.

        :rtype: OperationProxy()

        """
        return self[key]

    def __getitem__(self, key):
        """Return the OperationProxy for the given key.

        :rtype: OperationProxy()

        """
        try:
            return self._operations[key]
        except KeyError:
            raise AttributeError('Service has no operation %r' % key)

    def __iter__(self):
        """ Return iterator over the services and their callables. """
        return iter(self._operations.items())

    def __dir__(self):
        """ Return the names of the operations. """
        return list(itertools.chain(
            dir(super(ServiceProxy, self)), self._operations))
