package com.gci.boot.annotation.Query.handle;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gci.boot.annotation.Query.OrderQuery;
import com.gci.boot.annotation.Query.SelectQuery;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * @author 24642
 */
@Slf4j
public class QueryGenerator {
    private static final String COMMA = ",";
    /**
     * 生成查询语句
     * @param entity 数据库实体类
     * @param query 查询参数实体类或者数据库实体类
     * @return 生成的查询语句
     */
    public static <T,Q> LambdaQueryWrapper<T> initQueryWrapper(Class<T> entity, Q query) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        if (query == null) {
            return queryWrapper.lambda();
        }
        common(entity, query, queryWrapper,false);
        return queryWrapper.lambda();
    }

    public static <T,Q> LambdaQueryWrapper<T> initQueryWrapper(Class<T> entity, Q query,Boolean ignoreOrder) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        if (query == null) {
            return queryWrapper.lambda();
        }
        common(entity, query, queryWrapper, ignoreOrder);
        return queryWrapper.lambda();
    }

    private static <T, Q> void common(Class<T> entity, Q query, QueryWrapper<T> queryWrapper, boolean ignoreOrder) {
        Field[] fields = ReflectUtil.getFields(query.getClass());
        ArrayList<Field> orderList = new ArrayList<>();
        for (Field field : fields) {
            boolean accessible = field.isAccessible();
            //设置后操作速度更快
            field.setAccessible(true);
            if (!ignoreOrder) {
                if (field.isAnnotationPresent(OrderQuery.class)) {
                    orderList.add(field);
                }
            }
            Object fieldValue = ReflectUtil.getFieldValue(query, field);
            if (ObjectUtil.isEmpty(fieldValue)) {
                continue;
            }
//            String simpleName = field.getType().getSimpleName();
            SelectQuery selectQuery = field.getAnnotation(SelectQuery.class);


            if (selectQuery == null) {
                continue;
            }
            String fieldName = StrUtil.toUnderlineCase(!"".equals(selectQuery.PropName()) ? selectQuery.PropName() : field.getName());
            Class<?> type = field.getType();
//            SFunction function = LambdaFunctionUtil.getFunctionField(entity, fieldName);
            switch (selectQuery.Type()) {
                case EQ:
                    queryWrapper.eq(fieldName,fieldValue);
                    break;
                case NE:
                    queryWrapper.ne(fieldName,fieldValue);
                    break;
                case GT:
                    queryWrapper.gt(fieldName,fieldValue);
                    break;
                case GE:
                    queryWrapper.ge(fieldName,fieldValue);
                    break;
                case LT:
                    queryWrapper.lt(fieldName,fieldValue);
                    break;
                case LE:
                    queryWrapper.le(fieldName,fieldValue);
                    break;
                case IN:
                    if (List.class.equals(type)) {
                        queryWrapper.in(fieldName, (List)fieldValue);
                    } else if (String.class.equals(type)) {
                        Field o = ReflectUtil.getField(entity, StrUtil.toCamelCase(fieldName));
                        String replace = StrUtil.join(",",fieldValue).replace(",,", COMMA);
                        List<String> split = StrUtil.split(replace, COMMA);
                        if (!o.getType().equals(String.class)) {
                            queryWrapper.in(fieldName,Convert.toNumberArray(split));
                        }
                        queryWrapper.in(fieldName,split);
                    }
                    break;
                case NOT_IN:
                    if (List.class.equals(type)) {
                        queryWrapper.notIn(fieldName, (List)fieldValue);
                    } else if (String.class.equals(type)) {
                        Field o = ReflectUtil.getField(entity, StrUtil.toCamelCase(fieldName));
                        String replace = StrUtil.join(",",fieldValue).replace(",,", COMMA);
                        List<String> split = StrUtil.split(replace, COMMA);
                        if (!o.getType().equals(String.class)) {
                            queryWrapper.notIn(fieldName,Convert.toNumberArray(split));
                        }
                        queryWrapper.notIn(fieldName,split);
                    }
                    break;
                case BETWEEN:
                    if (List.class.equals(type)) {
                        if (((List)fieldValue).size()<2) {
                            break;
                        }
                        queryWrapper.between(fieldName, ((List)fieldValue).get(0),((List)fieldValue).get(1));
                    } else if (String.class.equals(type)) {
                        Field o = ReflectUtil.getField(entity, StrUtil.toCamelCase(fieldName));
                        String replace = StrUtil.join(",",fieldValue).replace(",,", COMMA);
                        List<String> split = StrUtil.split(replace, COMMA);
                        if (split.size()<2) {
                            break;
                        }
                        if (!o.getType().equals(String.class)) {
                            Number[] numbers = Convert.toNumberArray(split);
                            queryWrapper.between(fieldName,numbers[0],numbers[1]);
                        }
                        queryWrapper.between(fieldName,split.get(0),split.get(1));
                    }
                    break;
                case NOT_BETWEEN:
                    if (List.class.equals(type)) {
                        if (((List)fieldValue).size()<2) {
                            break;
                        }
                        queryWrapper.notBetween(fieldName, ((List)fieldValue).get(0),((List)fieldValue).get(1));
                    } else if (String.class.equals(type)) {
                        Field o = ReflectUtil.getField(entity, StrUtil.toCamelCase(fieldName));
                        String replace = StrUtil.join(",",fieldValue).replace(",,", COMMA);
                        List<String> split = StrUtil.split(replace, COMMA);
                        if (split.size()<2) {
                            break;
                        }
                        if (!o.getType().equals(String.class)) {
                            Number[] numbers = Convert.toNumberArray(split);
                            queryWrapper.notBetween(fieldName,numbers[0],numbers[1]);
                        }
                        queryWrapper.notBetween(fieldName,split.get(0),split.get(1));
                    }
                    break;
                case LIKE:
                    queryWrapper.like(fieldName,fieldValue);
                    break;
                case LIKE_LEFT:
                    queryWrapper.likeLeft(fieldName,fieldValue);
                    break;
                case LIKE_RIGHT:
                    queryWrapper.likeRight(fieldName,fieldValue);
                    break;
                default:
                    break;
            }
            //恢复原样
            field.setAccessible(accessible);
        }
        if (!ignoreOrder) {
            if (ObjectUtil.isNotEmpty(orderList)) {
                orderList.sort(Comparator.comparingInt(f->f.getAnnotation(OrderQuery.class).sort()));
                for (Field field : orderList) {
                    OrderQuery orderQuery = field.getAnnotation(OrderQuery.class);
                    String fieldName = StrUtil.toUnderlineCase(!"".equals(orderQuery.PropName()) ? orderQuery.PropName() : field.getName());
//                SFunction function = LambdaFunctionUtil.getFunctionField(entity, fieldName);
                    queryWrapper.orderBy(true,orderQuery.IsAsc(),fieldName);
                }
            }
        }
    }
}
