package cc.smtweb.framework.core.mvc.controller.access;

import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.exception.BindBeanException;
import cc.smtweb.framework.core.exception.BizException;
import cc.smtweb.framework.core.exception.SwException;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.controller.binder.ParamEditor;
import lombok.Getter;
import org.apache.commons.lang3.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * API方法执行
 *
 * @author xkliu
 */
public class MethodAccess implements IMethodAccess {
    private final ControllerAccess controllerAccess;
    private final Method method;

    private final MethodParamAccess[] paramBinds;

    public MethodAccess(ControllerAccess controllerAccess, Method method, MethodParamAccess[] paramBinds) {
        this.controllerAccess = controllerAccess;
        this.method = method;
        this.paramBinds = paramBinds;
    }

    @Override
    public Class<?> getReturnType() {
        return this.method.getReturnType();
    }

    @Override
    public Class<?> getBodyType() {
        for (MethodParamAccess methodParamAccess : paramBinds) {
            if (methodParamAccess.getBindType() == ParamEditor.TYPE_BODY) {
                return methodParamAccess.getParamType();
            }
        }

        return null;
    }

    @Override
    public Class<?> getAttrType(String paramName) {
        for (MethodParamAccess methodParamAccess : paramBinds) {
            if (methodParamAccess.getBindType() == ParamEditor.TYPE_ATTR && paramName.equals(methodParamAccess.getParamName())) {
                return methodParamAccess.getParamType();
            }
        }

        return null;
    }

    @Override
    public Object invoke(IBeanContext context) {
        return invoke(context, null, null, null);
    }

    @Override
    public Object invoke(IBeanContext context, Map<String, Object> params, String body, HttpServletRequest request) {
        // 通过请求数据转换函数参数值
        Object[] args = null;

        if (!ObjectUtils.isEmpty(paramBinds)) {
            args = new Object[paramBinds.length];
            for (int i = 0; i < paramBinds.length; i++) {
                MethodParamAccess param = paramBinds[i];
                try {
                    args[i] = param.getParamValue(context, params, body, request);
                } catch (Exception e) {
                    throw new BindBeanException(String.format("%s error value: %s", param.getParamName(), param.toString()), e);
                }
            }
        }

        // 获取服务实例，然后执行方法
        try {
            Object instance = controllerAccess.getOrNewInstance(context, params);
            return method.invoke(instance, args);
        } catch (IllegalAccessException | InstantiationException e) {
            throw new SwException(e);
        } catch (InvocationTargetException e) {
            throw new SwException(e.getCause());
        } catch (BizException e) {
            return R.error(e.getMessage());
        }
    }

    @Override
    public String fullName() {
        return controllerAccess.getFullName() + "." + method.getName();
    }

    public Class<?> findParam(Class<?> clazz) {
        for (MethodParamAccess param : paramBinds) {
            Class<?> paramType = param.getParamType();
            if (clazz.equals(paramType)) {
                return paramType;
            }
        }

        return null;
    }

    public String controllerFullName() {
        return this.controllerAccess.getFullName();
    }
}
