package com.topsunit.query.binding;

import com.topsunit.query.annotations.Param;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author yonghuan
 */
public class DefaultParameterParser implements ParameterParser {

    private final static Pattern NAMED_PARAMETER_PATTERN = Pattern.compile(":([a-zA-Z][a-zA-Z0-9]*)");

    @Override
    public Object[] parse(String sql, Method method, Object[] args) {
        if (useNamedParameter(method, args)) {
            Map<String, Object> params = new HashMap<>(args.length);
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                Param param = parameter.getAnnotation(Param.class);
                params.put(param.value(), args[i]);
            }
            List<Object> parsedArgs = new ArrayList<>(args.length);
            for (Matcher matcher = NAMED_PARAMETER_PATTERN.matcher(sql); matcher.find(); ) {
                String paramName = matcher.group(1);
                Object paramValue = params.get(paramName);
                if (paramValue instanceof Collection) {
                    parsedArgs.addAll((Collection<?>) paramValue);
                } else {
                    parsedArgs.add(paramValue);
                }
            }
            return parsedArgs.toArray(new Object[parsedArgs.size()]);
        } else {
            return args;
        }
    }

    private boolean useNamedParameter(Method method, Object[] args) {
        long paramAnnotationCount = Arrays.stream(method.getParameters())
                .map(parameter -> parameter.getAnnotation(Param.class))
                .filter(Objects::nonNull)
                .count();
        if (paramAnnotationCount == 0) {
            return false;
        }
        if (paramAnnotationCount != args.length) {
            throw new IllegalArgumentException("Param注解数量与实参数量不一致");
        }
        return true;
    }
}
