package com.topsunit.query.binding;

import com.huigou.cache.DictUtil;
import com.huigou.data.domain.query.QueryAbstractRequest;
import com.huigou.data.domain.query.QueryPageRequest;
import com.huigou.data.query.model.QueryDescriptor;
import com.huigou.data.query.model.QueryModel;
import com.huigou.util.ClassHelper;
import com.huigou.util.Constants;
import com.topsunit.query.annotations.Dictionary;
import com.topsunit.query.annotations.Param;
import com.topsunit.query.annotations.ResultType;
import net.sf.cglib.beans.BeanGenerator;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 用于执行<sql-query>标签中的语句。
 *
 * @author yonghuan
 * @since 1.1.3
 */
final class SqlQueryInvokeStrategy implements InvokeStrategy {

    private final QueryRequestFactory queryRequestFactory;

    public SqlQueryInvokeStrategy(QueryRequestFactory queryRequestFactory) {
        this.queryRequestFactory = queryRequestFactory;
    }

    @Override
    public boolean supports(InvokeContext invokeContext, Method method, Object[] args) {
        return true;
    }

    @Override
    public Object invoke(InvokeContext invokeContext, Method method, Object[] args) {
        List<QueryAbstractRequest> requests = ArrayUtils.isEmpty(args)
                ? new ArrayList<>(1)
                : Arrays.stream(args).filter(arg -> arg instanceof QueryAbstractRequest)
                .map(arg -> (QueryAbstractRequest) arg)
                .collect(Collectors.toList());
        if (requests.size() > 1) {
            throw new IllegalArgumentException("只能设置一个自定QueryRequest");
        }
        if (requests.isEmpty()) {
            requests.add(queryRequestFactory.getQueryRequest(method, args));
        }

        QueryAbstractRequest query = requests.get(0);
        String queryName = method.getName();
        QueryDescriptor queryDescriptor = invokeContext.getSqlExecutor().getQuery(invokeContext.getXml(), queryName);
        QueryModel model = invokeContext.getSqlExecutor().getQueryModel(queryDescriptor, query);
        // 解析字典配置
        for (Dictionary dictionary : method.getAnnotationsByType(Dictionary.class)) {
            if (StringUtils.isNotEmpty(dictionary.value()) && StringUtils.isNotEmpty(dictionary.field())) {
                model.putDictionary(dictionary.field(), DictUtil.getDictionary(dictionary.value()));
            }
        }
        final QueryPageRequest pageModel = query.getPageModel();
        final boolean needPage = pageModel != null
                && pageModel.getPageIndex() != null
                && pageModel.getPageSize() != null;
        Map<String, Object> result = needPage ? invokeContext.getSqlExecutor().executeSlicedQuery(model)
                : invokeContext.getSqlExecutor().executeQuery(model);
        Class<?> retType = method.getReturnType();
        if (Map.class.isAssignableFrom(retType)) {
            return result;
        }
        if (Collection.class.isAssignableFrom(retType)) {
            List<Map<String, Object>> rows = (List<Map<String, Object>>) result.get(Constants.ROWS);
            ResultType resultType = method.getAnnotation(ResultType.class);
            return resultType == null
                    ? rows
                    : rows.stream().map(row -> ClassHelper.fromMap(resultType.value(), row)).collect(Collectors.toList());
        }
        if (Page.class.isAssignableFrom(retType)) {
            List<?> rows = (List<?>) result.get(Constants.ROWS);
            long total = ((Number) result.getOrDefault(Constants.RECORD, rows.size())).longValue();
            return needPage
                    ? new PageImpl(rows, new PageRequest(pageModel.getPageIndex() - 1, pageModel.getPageSize()), total)
                    : new PageImpl(rows);
        }
        throw new IllegalArgumentException(String.format("无法转换查询结果集为：%s", retType.getName()));
    }

}
