import wrapt
import inspect

from .deprecation import deprecated


def unwrap(obj, attr):
    f = getattr(obj, attr, None)
    if f and isinstance(f, wrapt.ObjectProxy) and hasattr(f, '__wrapped__'):
        setattr(obj, attr, f.__wrapped__)


@deprecated('`wrapt` library is used instead', version='1.0.0')
def safe_patch(patchable, key, patch_func, service, meta, tracer):
    """ takes patch_func (signature: takes the orig_method that is
    wrapped in the monkey patch == UNBOUND + service and meta) and
    attach the patched result to patchable at patchable.key


      - if this is the module/class we can rely on methods being unbound, and just have to
      update the __dict__

      - if this is an instance, we have to unbind the current and rebind our
      patched method

      - If patchable is an instance and if we've already patched at the module/class level
      then patchable[key] contains an already patched command!
      To workaround this, check if patchable or patchable.__class__ are _dogtraced
      If is isn't, nothing to worry about, patch the key as usual
      But if it is, search for a "__dd_orig_{key}" method on the class, which is
      the original unpatched method we wish to trace.

    """
    def _get_original_method(thing, key):
        orig = None
        if hasattr(thing, '_dogtraced'):
            # Search for original method
            orig = getattr(thing, "__dd_orig_{}".format(key), None)
        else:
            orig = getattr(thing, key)
            # Set it for the next time we attempt to patch `thing`
            setattr(thing, "__dd_orig_{}".format(key), orig)

        return orig

    if inspect.isclass(patchable) or inspect.ismodule(patchable):
        orig = _get_original_method(patchable, key)
        if not orig:
            # Should never happen
            return
    elif hasattr(patchable, '__class__'):
        orig = _get_original_method(patchable.__class__, key)
        if not orig:
            # Should never happen
            return
    else:
        return

    dest = patch_func(orig, service, meta, tracer)

    if inspect.isclass(patchable) or inspect.ismodule(patchable):
        setattr(patchable, key, dest)
    elif hasattr(patchable, '__class__'):
        setattr(patchable, key, dest.__get__(patchable, patchable.__class__))

