package cd.casic.framework.mongo.utils;


import cd.casic.framework.mongo.core.constant.ECompare;
import cd.casic.framework.mongo.core.constant.EConditionType;
import cd.casic.framework.mongo.core.constant.ESortType;
import cd.casic.framework.mongo.core.entity.Condition;
import cd.casic.framework.mongo.core.entity.SortCondition;
import cd.casic.framework.mongo.core.wrapper.ConditionWrapper;
import cd.casic.framework.mongo.core.wrapper.LambdaQueryWrapper;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Query;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

public class QueryBuildUtils {

    private static final Map<ECompare, Function<Condition, Criteria>> HANDLERS = new ConcurrentHashMap<>();

    static {
        HANDLERS.put(ECompare.EQ,QueryBuildUtils::eqHandle);
        HANDLERS.put(ECompare.NE,QueryBuildUtils::neHandle);
        HANDLERS.put(ECompare.LE,QueryBuildUtils::leHandle);
        HANDLERS.put(ECompare.LT,QueryBuildUtils::ltHandle);
        HANDLERS.put(ECompare.GE,QueryBuildUtils::geHandle);
        HANDLERS.put(ECompare.GT,QueryBuildUtils::gtHandle);
        HANDLERS.put(ECompare.BW,QueryBuildUtils::bwHandle);
        HANDLERS.put(ECompare.IN,QueryBuildUtils::inHandle);
        HANDLERS.put(ECompare.NIN,QueryBuildUtils::ninHandle);
        HANDLERS.put(ECompare.ISNULL,QueryBuildUtils::isNullHandle);
    }

    public static Query buildQuery(LambdaQueryWrapper<?> queryWrapper){
        return buildQuery(queryWrapper.getCondition());
    }

    public static Query buildQuery(ConditionWrapper wrapper){
        Criteria criteria = new Criteria();

        //构建查询参数
        Map<String,Object> map = buildCondition(wrapper);
        Query query = new Query((CriteriaDefinition) map.get("criteria"));

        //构建排序参数
        List<SortCondition> sortConditions = wrapper.getSortConditions();
        if (Objects.nonNull(sortConditions)){
            query.with(Sort.by(buildSort(sortConditions)));
        }

        //构建分页参数
        if (Objects.nonNull(wrapper.getSkip())){
            query.skip(wrapper.getSkip());
        }
        if (Objects.nonNull(wrapper.getLimit())){
            query.limit(wrapper.getLimit());
        }

        //构建查询列
        if(Objects.nonNull(wrapper.getFields()) && wrapper.getFields().size() > 0){
            Field fields = query.fields();
            wrapper.getFields().forEach(item ->fields.include(item.getColumn()));
        }

        return query;
    }

    public static Map<String,Object> buildCondition(ConditionWrapper wrapper){

        Map<String, Object> map = new HashMap<>();
        Criteria criteria = new Criteria();
        if (Objects.isNull(wrapper) || Objects.isNull(wrapper.getConditions()) || wrapper.getConditions().size() ==0){
            map.put("criteria",criteria);
            map.put("critters",new Criteria[]{criteria});
            return map;
        }
        List<Condition> conditions = wrapper.getConditions();

        boolean isOr = false;
        Criteria[] critters = new Criteria[conditions.size()];
        for (int i = 0; i < conditions.size(); i++) {
            Condition condition = conditions.get(i);
            if (Objects.nonNull(condition.getConditionWrapper()) && Objects.isNull(condition.getColumn()) && condition.getConditionWrapper().getConditions().size() > 0){
                Criteria curCriteria = new Criteria();
                Condition first = condition.getConditionWrapper().getConditions().get(0);
                Map<String,Object> map1 = buildCondition(condition.getConditionWrapper());
                if (first.getConditionType() == EConditionType.OR){
                    curCriteria.orOperator((Criteria[]) map1.get("critters"));
                }else {
                    curCriteria.andOperator((Criteria[]) map1.get("critters"));
                }
                critters[i] = curCriteria;
                continue;
            }
            ECompare type = condition.getType();
            if (condition.getConditionType() == EConditionType.OR){
                isOr = true;
            }
            Function<Condition, Criteria> handler = HANDLERS.get(type);
            if (Objects.isNull(handler)){
                throw new RuntimeException("buildQuery error not have queryType" + type);
            }
            Criteria curCriteria = handler.apply(condition);
            critters[i] = curCriteria;
        }
        if (isOr){
            criteria.orOperator(critters);
        }else {
            criteria.andOperator(critters);
        }
        map.put("criteria",criteria);
        map.put("critters",critters);
        return map;
    }

    private static List<Sort.Order> buildSort(List<SortCondition> sortConditions){
        return sortConditions.stream().map(sortCondition -> {
            if (sortCondition.getSortType() == ESortType.ASC){
                return Sort.Order.asc(sortCondition.getColumn());
            }else {
                return Sort.Order.desc(sortCondition.getColumn());
            }
        }).collect(Collectors.toList());
    }

    public static Criteria eqHandle(Condition condition){
        return Criteria.where(condition.getColumn()).is(condition.getArgs().get("eq"));
    }

    public static Criteria neHandle(Condition condition){
        return Criteria.where(condition.getColumn()).ne(condition.getArgs().get("ne"));
    }

    public static Criteria leHandle(Condition condition){
        return Criteria.where(condition.getColumn()).lte(condition.getArgs().get("le"));
    }

    public static Criteria ltHandle(Condition condition){
        return Criteria.where(condition.getColumn()).lt(condition.getArgs().get("lt"));
    }

    public static Criteria geHandle(Condition condition){
        return Criteria.where(condition.getColumn()).gte(condition.getArgs().get("ge"));
    }

    public static Criteria gtHandle(Condition condition){
        return Criteria.where(condition.getColumn()).gt(condition.getArgs().get("gt"));
    }

    public static Criteria bwHandle(Condition condition){
        return Criteria.where(condition.getColumn()).gte(condition.getArgs().get("bw1")).lte(condition.getArgs().get("bw2"));
    }

    public static Criteria inHandle(Condition condition){
        Map<String,Object> map = condition.getArgs();
        Collection<Object> in = (Collection<Object>) map.get("in");
        return Criteria.where(condition.getColumn()).in(in);
    }

    public static Criteria ninHandle(Condition condition){
        Map<String,Object> map = condition.getArgs();
        List<Object> notIn = (List<Object>) map.get("notIn");
        return Criteria.where(condition.getColumn()).nin(notIn);
    }

    public static Criteria isNullHandle(Condition condition){
        return Criteria.where(condition.getColumn()).isNull();
    }
}
