package com.sonic.daosupport.mysql;

import java.util.List;
import java.util.Locale;
import java.util.Optional;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.interfaces.Compare;
import com.baomidou.mybatisplus.core.conditions.interfaces.Func;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sonic.common.exception.BizException;
import com.sonic.common.rpc.GlobalResultCode;
import com.sonic.daosupport.wrapper.CriteriaWrapper;
import com.sonic.daosupport.wrapper.Operator;
import com.sonic.daosupport.wrapper.OperatorProcessor;
import com.sonic.daosupport.wrapper.TriConsumer;
import com.google.common.collect.ImmutableListMultimap;

/**
 * @author code
 * @param <T>
 */
public class MybatisPlusOperatorProcessor<T> implements OperatorProcessor {

    @Override
    public QueryWrapper<T> assembleAllQueryWrapper(ImmutableListMultimap<String, CriteriaWrapper.QueryField> queryColumnMap, boolean andOperator) {
        QueryWrapper<T> queryWrapper = Wrappers.query();
        assemble(queryColumnMap, andOperator, queryWrapper);
        return queryWrapper;
    }

    @Override
    public LambdaQueryWrapper assembleAllLambdaQueryWrapper(ImmutableListMultimap<String, CriteriaWrapper.QueryField> queryColumnMap, boolean andOperator) {
        LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery();
        assemble(queryColumnMap, andOperator, queryWrapper);
        return queryWrapper;
    }

    private void assemble(ImmutableListMultimap<String, CriteriaWrapper.QueryField> queryColumnMap, boolean andOperator, AbstractWrapper queryWrapper) {
        queryColumnMap.asMap().forEach((column, queryFields) -> {
            // 获取processor，拼装wrapper
            if (andOperator) {
                queryFields.forEach(queryField -> get(queryField.getOperator())
                        .accept(queryWrapper, column, queryField.getValue()));
            } else {
                queryFields.forEach(q -> {
                    get(q.getOperator())
                            .accept(queryWrapper, column, q.getValue());
                    queryWrapper.or();
                });
            }
        });
    }

    public TriConsumer<? extends AbstractWrapper, Object, Object> get(Operator operator) {
        switch (operator) {
            case LIKE:
                return Compare::like;
            case EQ:
                return Compare::eq;
            case LT:
                return Compare::lt;
            case LE:
                return Compare::le;
            case GT:
                return Compare::gt;
            case GE:
                return Compare::ge;
            case NE:
                return Compare::ne;
            case IN:
                return (TriConsumer<AbstractWrapper, Object, Object>) Func::in;
            case IS_NULL:
                return (TriConsumer<AbstractWrapper, Object, Object>) (abstractWrapper, o, o2) -> abstractWrapper.isNull(o);
            case IS_NOT_NULL:
                return (TriConsumer<AbstractWrapper, Object, Object>) (abstractWrapper, o, o2) -> abstractWrapper.isNotNull(o);
            case BETWEEN:
                return (TriConsumer<AbstractWrapper, Object, Object>) (k, v, s) -> {
                    List valueList = (List)s;
                    k.between(v, valueList.get(0), valueList.get(1));
                };
            case ORDER:
                return (TriConsumer<AbstractWrapper, Object, Object>) (k, v, s) -> {
                    // 如果value不能转成Direction，会抛异常
                    if (Direction.fromString(String.valueOf(s)).isDescending()) {
                        k.orderByDesc(v);
                        return;
                    }

                    k.orderByAsc(v);
                };
            default:
                throw new BizException(GlobalResultCode.INVALID_PARAMS);
        }
    }

    public enum Direction {
        /** sort type */
        ASC,
        DESC;

        public boolean isAscending() {
            return this.equals(ASC);
        }

        public boolean isDescending() {
            return this.equals(DESC);
        }

        public static Direction fromString(String value) {
            try {
                return valueOf(value.toUpperCase(Locale.US));
            } catch (Exception var2) {
                throw new IllegalArgumentException(String.format("Invalid value '%s' for orders given! Has to be either 'desc' or 'asc' (case insensitive).", value), var2);
            }
        }

        public static Optional<Direction> fromOptionalString(String value) {
            try {
                return Optional.of(fromString(value));
            } catch (IllegalArgumentException var2) {
                return Optional.empty();
            }
        }
    }
}
