package com.topsunit.query.binding;

import com.huigou.data.query.executor.SQLExecutorDao;
import com.topsunit.query.annotations.Param;
import com.topsunit.query.annotations.ResultType;
import com.topsunit.query.annotations.Select;
import org.apache.commons.lang.ArrayUtils;


import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yonghuan
 * @see Select
 * @since 1.1.3
 */
final class SelectSqlInvokeStrategy extends ObtainSqlInvokeStrategy {

    @Override
    public boolean supports(InvokeContext invokeContext, Method method, Object[] args) {
        return method.getAnnotation(Select.class) != null;
    }

    @Override
    public Object invoke(InvokeContext invokeContext, Method method, Object[] args) {
        final String sql = (String) super.invoke(invokeContext, method, args);
        final Class<?> returnType = method.getReturnType();
        final SQLExecutorDao sqlExecutor = invokeContext.getSqlExecutor();
        final ResultType resultType = method.getAnnotation(ResultType.class);
        if (ArrayUtils.isEmpty(args)) {
            // 无查询参数
            if (Collection.class.isAssignableFrom(returnType)) {
                return sqlExecutor.queryToList(sql, resultType.value());
            }
            if (Map.class.isAssignableFrom(returnType)) {
                return sqlExecutor.queryToMap(sql);
            }
            return resultType != null
                    ? sqlExecutor.queryOneToObject(sql, resultType.value())
                    : sqlExecutor.queryOneToObject(sql, returnType);

        }
        if (args.length == 1) {
            if (args[0] instanceof Map) {
                // 查询参数用Map传递
                return queryByMapParam(sqlExecutor, sql, returnType, resultType, (Map<String, Object>) args[0]);
            } else {
                // 单个查询参数
                final Object singletonParam = args[0];
                if (Collection.class.isAssignableFrom(returnType)) {
                    return sqlExecutor.queryToList(sql, resultType.value(), singletonParam);
                }
                if (Map.class.isAssignableFrom(returnType)) {
                    return sqlExecutor.queryOneToMap(sql, singletonParam);
                }
                return resultType != null
                        ? sqlExecutor.queryOneToObject(sql, resultType.value())
                        : sqlExecutor.queryOneToObject(sql, returnType, singletonParam);
            }
        } else {
            Parameter[] parameters = method.getParameters();
            if (parameters[0].getAnnotation(Param.class) == null) {
                if (Collection.class.isAssignableFrom(returnType)) {
                    return sqlExecutor.queryToList(sql, returnType, args);
                }
                if (Map.class.isAssignableFrom(returnType)) {
                    sqlExecutor.queryOneToMap(sql, args);
                }
                return resultType != null
                        ? sqlExecutor.queryOneToObject(sql, resultType.value(), args)
                        : sqlExecutor.queryOneToObject(sql, returnType, args);
            } else {
                // 指定了查询参数的名称
                final Map<String, Object> mapParam = new HashMap<>(parameters.length);
                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    String paramName = parameter.getAnnotation(Param.class).value();
                    mapParam.put(paramName, args[i]);
                }
                return queryByMapParam(sqlExecutor, sql, returnType, resultType, mapParam);
            }
        }
    }

    private Object queryByMapParam(SQLExecutorDao sqlExecutor, String sql, Class<?> returnType, ResultType resultType, Map<String, Object> mapParam) {
        if (Collection.class.isAssignableFrom(returnType)) {
            return sqlExecutor.queryToListByMapParam(sql, returnType, mapParam);
        }
        if (Map.class.isAssignableFrom(returnType)) {
            sqlExecutor.queryOneToMapByMapParam(sql, mapParam);
        }
        return resultType != null
                ? sqlExecutor.queryOneToObjectByMapParam(sql, resultType.value(), mapParam)
                : sqlExecutor.queryOneToObjectByMapParam(sql, returnType, mapParam);
    }
}
