package org.springframework.security.integration.python;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.aopalliance.intercept.MethodInvocation;
import org.python.core.Py;
import org.python.core.PyObject;
import org.python.util.PythonInterpreter;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.security.access.prepost.PreInvocationAttribute;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
import org.springframework.security.core.Authentication;
import org.springframework.util.ClassUtils;

public class PythonInterpreterPreInvocationAdvice implements PreInvocationAuthorizationAdvice{
    private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    public boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute preAttr) {
        PythonInterpreterPreInvocationAttribute pythonAttr = (PythonInterpreterPreInvocationAttribute) preAttr;
        String script = pythonAttr.getScript();

        PythonInterpreter python = new PythonInterpreter();
        python.set("authentication", authentication);
        python.set("args", createArgumentMap(mi));
        python.set("method", mi.getMethod().getName());
        Resource scriptResource = new PathMatchingResourcePatternResolver().getResource(script);

        try {
            python.execfile(scriptResource.getInputStream());
        } catch (IOException e) {
            throw new IllegalArgumentException("Couldn't run python script, " + script, e);
        }

        PyObject allowed = python.get("allow");

        if (allowed == null) {
            throw new IllegalStateException("Python script did not set the permit flag");
        }

        return (Boolean)Py.tojava(allowed, Boolean.class);
    }

    private Map<String,Object> createArgumentMap(MethodInvocation mi) {
        Object[] args = mi.getArguments();
        Object targetObject = mi.getThis();
        Method method = ClassUtils.getMostSpecificMethod(mi.getMethod(), targetObject.getClass());
        String[] paramNames = parameterNameDiscoverer.getParameterNames(method);

        Map<String,Object> argMap = new HashMap<String,Object>();
        for(int i=0; i < args.length; i++) {
            argMap.put(paramNames[i], args[i]);
        }

        return argMap;
    }
}
