package cn.novalon.manage.common.dao;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * 查询工具类
 * 
 * @author 张翔
 * @date 2026-03-13
 */
public class QueryUtil {

    private static final Logger log = LoggerFactory.getLogger(QueryUtil.class);

    public static <Q> Query getQuery(Q query) {
        return getQuery(query, true);
    }

    public static <Q> Query getQueryAll(Q query) {
        return getQuery(query, false);
    }

    public static <Q> Query getQuery(Q query, Boolean enabled) {
        Criteria criteria = Criteria.empty();
        if (enabled) {
            criteria = criteria.and("deletedAt").isNull();
        }
        if (query == null) {
            log.info("Query object is null, returning empty criteria");
            return Query.query(criteria);
        }
        System.out.println("=== QueryUtil.getQuery START ===");
        System.out.println("Query object class: " + query.getClass().getName());
        log.info("=== QueryUtil.getQuery START ===");
        log.info("Query object class: {}", query.getClass().getName());
        try {
            List<Field> fields = getAllFields(query.getClass(), new ArrayList<>());
            log.info("Found {} fields to process", fields.size());
            System.out.println("Found " + fields.size() + " fields to process");
            for (Field field : fields) {
                boolean accessible = Modifier.isStatic(field.getModifiers()) ? field.canAccess(null)
                        : field.canAccess(query);
                field.setAccessible(true);
                QueryField q = field.getAnnotation(QueryField.class);
                if (q != null) {
                    String propName = q.propName();
                    String blurry = q.blurry();
                    String attributeName = isBlank(propName) ? field.getName() : propName;
                    Object val = field.get(query);
                    log.info("Processing field: {}, value: {}, blurry: {}", attributeName, val, blurry);
                    System.out.println("Processing field: " + attributeName + ", value: " + val + ", blurry: " + blurry);
                    if (val == null || "".equals(val)) {
                        log.info("Field {} has null or empty value, skipping", attributeName);
                        System.out.println("Field " + attributeName + " has null or empty value, skipping");
                        continue;
                    }
                    if (StringUtils.isNotBlank(blurry)) {
                        log.info("Field {} has blurry search configuration: {}", attributeName, blurry);
                        System.out.println("Field " + attributeName + " has blurry search configuration: " + blurry);
                        String[] blurrys = blurry.split(",");
                        Criteria orCriteria = Criteria.empty();
                        for (String s : blurrys) {
                            orCriteria = orCriteria.or(s).like("%" + val + "%");
                        }
                        criteria = criteria.and(orCriteria);
                        log.info("Added OR criteria for blurry search: {} with value: {}", blurry, val);
                        System.out.println("Added OR criteria for blurry search: " + blurry + " with value: " + val);
                        continue;
                    }
                    switch (q.type()) {
                        case EQUAL:
                            criteria = criteria.and(attributeName).is(val);
                            break;
                        case GREATER_THAN:
                            criteria = criteria.and(attributeName).greaterThanOrEquals(val);
                            break;
                        case LESS_THAN:
                            criteria = criteria.and(attributeName).lessThanOrEquals(val);
                            break;
                        case LESS_THAN_NQ:
                            criteria = criteria.and(attributeName).lessThan(val);
                            break;
                        case INNER_LIKE:
                            criteria = criteria.and(attributeName).like("%" + val + "%");
                            break;
                        case LEFT_LIKE:
                            criteria = criteria.and(attributeName).like("%" + val);
                            break;
                        case NOT_LEFT_LIKE:
                            criteria = criteria.and(attributeName).notLike("%" + val);
                            break;
                        case RIGHT_LIKE:
                            criteria = criteria.and(attributeName).like(val + "%");
                            break;
                        case IN:
                            if (val instanceof Collection && CollectionUtils.isNotEmpty((Collection<?>) val)) {
                                criteria = criteria.and(attributeName).in((Collection<?>) val);
                            }
                            break;
                        case OR:
                            QueryField.Type orValue = q.orPropVal();
                            String[] orPropNames = q.orPropNames();
                            Criteria orPredicate = Criteria.empty();
                            if (QueryField.Type.IS_NULL.equals(orValue)) {
                                for (String prop : orPropNames) {
                                    orPredicate = orPredicate.or(prop).isNull();
                                }
                            }
                            if (QueryField.Type.IS_NOT_NULL.equals(orValue)) {
                                for (String prop : orPropNames) {
                                    orPredicate = orPredicate.or(prop).isNotNull();
                                }
                            }
                            criteria = criteria.and(orPredicate);
                            break;
                        case IS_NULL:
                            criteria = criteria.and(attributeName).isNull();
                            break;
                        case IS_NOT_NULL:
                            criteria = criteria.and(attributeName).isNotNull();
                            break;
                    }
                }
                field.setAccessible(accessible);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return Query.query(criteria);
    }

    public static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(cs.charAt(i))) {
                return false;
            }
        }
        return false;
    }

    private static List<Field> getAllFields(Class<?> clazz, List<Field> fields) {
        if (clazz != null) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            getAllFields(clazz.getSuperclass(), fields);
        }
        return fields;
    }
}
