package org.restler.spring.data.proxy;

import com.google.common.collect.ImmutableMultimap;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
import org.restler.client.Call;
import org.restler.client.CallExecutor;
import org.restler.client.RestlerException;
import org.restler.http.HttpCall;
import org.restler.http.HttpMethod;
import org.restler.util.UriBuilder;
import org.springframework.beans.BeanUtils;
import sun.plugin.dom.exception.InvalidStateException;

import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

public class ResourceProxyMaker {
    public Object make(Class<?> aClass, Object object, HashMap<String, String> hrefs) {
        net.sf.cglib.proxy.InvocationHandler handler = new ResourceInvocationHandler(object, hrefs);

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(aClass);
        enhancer.setInterfaces(new Class<?>[]{ResourceProxy.class});
        enhancer.setCallback(handler);

        Object proxy = enhancer.create();

        try {
            for(Field objectField : aClass.getDeclaredFields()) {
                objectField.setAccessible(true);
                objectField.set(proxy, objectField.get(object));
                objectField.setAccessible(false);
            }
        } catch (IllegalAccessException e) {
            throw new RestlerException("Illegal access to field.", e);
        }

        return proxy;
    }

    private Object getWrappedId(Object object, String id) {
        Field[] fields = object.getClass().getDeclaredFields();
        Class fieldClass;

        for (Field field : fields) {
            if (field.getDeclaredAnnotation(Id.class) != null || field.getDeclaredAnnotation(EmbeddedId.class) != null) {
                fieldClass = field.getType();

                field.setAccessible(true);

                try {
                    return fieldClass.getConstructor(String.class).newInstance(id);
                } catch (IllegalAccessException e) {
                    throw new RestlerException("Access denied to change id", e);
                } catch (InvocationTargetException e) {
                    throw new RestlerException("Can't create id wrapper", e);
                } catch (NoSuchMethodException | InstantiationException e) {
                    throw new RestlerException("Could not instantiate id object", e);
                }
            }
        }

        return null;
    }

    private String getHrefByMethod(Method method, HashMap<String, String> hrefs) {
        String methodName = method.getName();

        if(methodName.startsWith("get")) {
            String hrefName = methodName.substring(3).toLowerCase();
            return hrefs.get(hrefName);
        }

        return null;
    }

    private class ResourceInvocationHandler implements InvocationHandler {
        private Object object;
        private HashMap<String, String> hrefs;
        private CallExecutor executor;

        public ResourceInvocationHandler(Object object, HashMap<String, String> hrefs) {
            this.object = object;
            this.hrefs = hrefs;
            this.executor = null;
        }

        @Override
        public Object invoke(Object o, Method method, Object[] args) throws Throwable {
            if(method.equals(ResourceProxy.class.getMethod("getResourceId"))) {
                String self = hrefs.get("self");
                return getWrappedId(object, self.substring(self.lastIndexOf("/")+1));
            }else if(method.equals(ResourceProxy.class.getMethod("getRepositoryUri"))) {
                String self = hrefs.get("self");
                return self.substring(0, self.lastIndexOf("/"));
            }else if(method.equals(ResourceProxy.class.getMethod("getSelfUri"))) {
                return hrefs.get("self");
            }else if(method.equals(ResourceProxy.class.getMethod("getObject"))) {
                return object;
            }else if(method.equals(ResourceProxy.class.getMethod("getHrefs"))) {
                return hrefs;
            }else if(method.equals(ResourceProxy.class.getMethod("setExecutor", CallExecutor.class))) {
                executor = (CallExecutor) args[0];
                return null;
            }

            if(executor == null) {
                throw new InvalidStateException("Executor must be initialized. For initialize executor use ProxyCallEnhancer.");
            }

            String uri = getHrefByMethod(method, hrefs);

            if(uri != null) {
                String fieldName = BeanUtils.findPropertyForMethod(method).getName();
                Field field = object.getClass().getDeclaredField(fieldName);

                field.setAccessible(true);


                Call httpCall = new HttpCall(new UriBuilder(uri).build(), HttpMethod.GET, null, ImmutableMultimap.of(), method.getGenericReturnType());

                Object newValue = executor.execute(httpCall);
                field.set(object, newValue);
                field.set(o, newValue);

                return field.get(object);
            }

            return method.invoke(object, args);
        }
    }
}

@@ -435,63 +435,8 @@
 ils;
-%0Aimport sun.plugin.dom.exception.InvalidStateException;
 %0A%0Aim
@@ -4378,14 +4378,14 @@
 ew I
-nvalid
+llegal
 Stat

Fix invalid exception.