package com.topsunit.query.binding;

import com.huigou.cache.DictUtil;
import com.huigou.context.ThreadLocalUtil;
import com.huigou.data.query.executor.SQLExecutorDao;
import com.huigou.util.Constants;
import com.topsunit.query.annotations.Dictionary;
import com.topsunit.query.annotations.ResultType;
import com.topsunit.query.annotations.Select;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Method;
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 {

    private final SqlTranslator sqlTranslator;
    private final ParameterParser parameterParser;

    public SelectSqlInvokeStrategy(SqlTranslator sqlTranslator, ParameterParser parameterParser) {
        this.sqlTranslator = sqlTranslator;
        this.parameterParser = parameterParser;
    }

    @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) {
        String sql = (String) super.invoke(invokeContext, method, args);
        // 优先解析参数
        Object[] parsedArgs = parameterParser.parse(sql, method, args);
        // 然后再翻译SQL
        sql = sqlTranslator.translate(sql, method, args);
        final Class<?> returnType = method.getReturnType();
        final SQLExecutorDao sqlExecutor = invokeContext.getSqlExecutor();
        final ResultType resultType = method.getAnnotation(ResultType.class);

        // 解析字典配置
        Dictionary[] dictionaries = method.getAnnotationsByType(Dictionary.class);
        if (dictionaries.length > 0) {
            Map<String, Object> dictionaryMap = new HashMap<>(dictionaries.length);
            for (Dictionary dictionary : dictionaries) {
                if (StringUtils.isNotEmpty(dictionary.value()) && StringUtils.isNotEmpty(dictionary.field())) {
                    dictionaryMap.put(dictionary.field(), DictUtil.getDictionary(dictionary.value()));
                }
            }
            ThreadLocalUtil.putVariable(Constants.DICTIONARY_MAP, dictionaryMap);
        }

        if (ArrayUtils.isEmpty(parsedArgs)) {
            // 无查询参数
            if (Collection.class.isAssignableFrom(returnType)) {
                return sqlExecutor.queryToList(sql, resultType.value());
            } else if (Map.class.isAssignableFrom(returnType)) {
                return sqlExecutor.queryToMap(sql);
            } else {
                if (resultType != null) {
                    return sqlExecutor.queryOneToObject(sql, resultType.value());
                } else {
                    return Map.class.isAssignableFrom(returnType) ? sqlExecutor.queryToMap(sql) : sqlExecutor.queryOneToObject(sql, returnType);
                }
            }
        }
        if (parsedArgs.length == 1) {
            if (parsedArgs[0] instanceof Map) {
                // 查询参数用Map传递
                return queryByMapParam(sqlExecutor, sql, returnType, resultType, (Map<String, Object>) parsedArgs[0]);
            } else {
                // 单个查询参数
                final Object singletonParam = parsedArgs[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 {
            if (Collection.class.isAssignableFrom(returnType)) {
                return sqlExecutor.queryToList(sql, resultType.value(), parsedArgs);
            }
            if (Map.class.isAssignableFrom(returnType)) {
                sqlExecutor.queryOneToMap(sql, parsedArgs);
            }
            return resultType != null
                    ? sqlExecutor.queryOneToObject(sql, resultType.value(), parsedArgs)
                    : sqlExecutor.queryOneToObject(sql, returnType, parsedArgs);

        }
    }

    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);
    }
}
