package cd.casic.framework.mongo.core.wrapper;

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.SelectField;
import cd.casic.framework.mongo.core.entity.SortCondition;
import cd.casic.framework.mongo.core.sdk.Compare;
import cd.casic.framework.mongo.core.sdk.Function;
import cd.casic.framework.mongo.core.sdk.Nested;
import cd.casic.framework.mongo.core.sdk.SFunction;
import cd.casic.framework.mongo.utils.ConvertUtils;

import java.util.*;
import java.util.stream.Collectors;

public class LambdaQueryWrapper<T>
        implements
        Compare<LambdaQueryWrapper<T>, SFunction<T, ?>>,
        Function<T, LambdaQueryWrapper<T>, SFunction<T, ?>>,
        Nested<LambdaQueryWrapper<T>, LambdaQueryWrapper<T>> {

    private final List<SelectField> fields = new ArrayList<>(5);

    private final List<Condition> conditions = new ArrayList<>(5);

    private final List<SortCondition> sortConditions = new ArrayList<>(5);

    private Long skip;

    private Integer limit;

    public ConditionWrapper getCondition(){
        return new ConditionWrapper(fields,conditions,sortConditions,skip,limit);
    }

    private String getFieldMeta(SFunction<T, ?> column){
        return ConvertUtils.convertToFieldName(column);
    }


    @Override
    public LambdaQueryWrapper<T> eq(Boolean condition, SFunction<T, ?> column, Object value) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("eq",value);
            conditions.add(new Condition(ECompare.EQ, getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> ne(Boolean condition, SFunction<T, ?> column, Object value) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("ne",value);
            conditions.add(new Condition(ECompare.NE,getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> le(Boolean condition, SFunction<T, ?> column, Object value) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("le",value);
            conditions.add(new Condition(ECompare.LE,getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> lt(Boolean condition, SFunction<T, ?> column, Object value) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("lt",value);
            conditions.add(new Condition(ECompare.LT,getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> ge(Boolean condition, SFunction<T, ?> column, Object value) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("ge",value);
            conditions.add(new Condition(ECompare.GE,getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> gt(Boolean condition, SFunction<T, ?> column, Object value) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("gt",value);
            conditions.add(new Condition(ECompare.GT,getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> between(Boolean condition, SFunction<T, ?> column, Object leftValue, Object rightValue) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("bw1",leftValue);
            map.put("bw2",rightValue);
            conditions.add(new Condition(ECompare.BW,getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> in(Boolean condition, SFunction<T, ?> column, Collection<?> values) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("in",values);
            conditions.add(new Condition(ECompare.IN,getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> notIn(Boolean condition, SFunction<T, ?> column, Collection<?> values) {
        if(condition){
            Map<String, Object> map = new HashMap<>();
            map.put("notIn", values);
            conditions.add(new Condition(ECompare.NIN,getFieldMeta(column), map));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> isNull(Boolean condition, SFunction<T, ?> column) {
        if (condition){
            conditions.add(new Condition(ECompare.ISNULL,getFieldMeta(column)));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> or() {
        if (conditions.size() == 0){
            throw new RuntimeException("not first use or");
        }
        Condition lastCondition = conditions.get(conditions.size() - 1);
        ConditionWrapper conditionWrapper = lastCondition.getConditionWrapper();
        if (Objects.isNull(conditionWrapper)){
            conditionWrapper = new ConditionWrapper(fields,conditions,sortConditions,skip,limit);
        }
        List<Condition> sub = conditionWrapper.getConditions();
        Condition condition = sub.get(sub.size() - 1);
        condition.setConditionType(EConditionType.OR);
        return this;
   }

    @Override
    public LambdaQueryWrapper<T> and(boolean condition, java.util.function.Function<LambdaQueryWrapper<T>, LambdaQueryWrapper<T>> function) {
        if (condition){
            LambdaQueryWrapper<T> apply = function.apply(new LambdaQueryWrapper<>());
            if (this.conditions.size() == 0){
                throw new RuntimeException("not first use and");
            }
            ConditionWrapper conditionWrapper = apply.getCondition();
            Condition c = new Condition();
            c.setConditionWrapper(conditionWrapper);
            this.conditions.add(c);
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> orderByAsc(boolean condition, SFunction<T, ?> column) {
        if (condition){
            sortConditions.add(new SortCondition(ESortType.ASC,getFieldMeta(column)));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> orderByDesc(boolean condition, SFunction<T, ?> column) {
        if (condition){
            sortConditions.add(new SortCondition(ESortType.DESC,getFieldMeta(column)));
        }
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> skip(Long skip) {
        this.skip = skip;
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> limit(Integer limit) {
        this.limit = limit;
        return this;
    }

    @SafeVarargs
    @Override
    public final LambdaQueryWrapper<T> select(SFunction<T, ?>... columns) {
        if (Objects.nonNull(columns) && columns.length > 0){
            List<SelectField> fields = Arrays.stream(columns).map(column -> new SelectField(getFieldMeta(column))).collect(Collectors.toList());
            this.fields.addAll(fields);
        }
        return this;
    }
}
