package com.ecep.contract.util;

import java.util.function.Function;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;

import com.fasterxml.jackson.databind.JsonNode;

public class SpecificationUtils {
    public static <T> Specification<T> and(Specification<T> one, Specification<T> two) {
        if (one == null) {
            return two;
        }
        if (two == null) {
            return one;
        }
        return one.and(two);
    }

    /**
     * 逻辑或
     */
    public static <T> Specification<T> or(Specification<T> one, Specification<T> two) {
        if (one == null) {
            return two;
        }
        if (two == null) {
            return one;
        }
        return one.or(two);
    }

    /**
     * 创建 搜索条件，搜索字符串中按 “ ” 分割多个搜索条件，每个搜索条件使用 and 连接
     *
     * @param searchText               搜索的字符串
     * @param buildSearchSpecification 创建搜索条件的方法
     * @param <T>                      泛型
     * @return 搜索条件为空时返回null，否则返回一个Specification对象
     */
    public static <T> Specification<T> andWith(String searchText,
            Function<String, Specification<T>> buildSearchSpecification) {
        Specification<T> spec = null;
        String[] split = null;
        do {
            split = StringUtils.split(searchText, " ");
            String finalSearchText = split == null ? searchText : split[0];
            spec = SpecificationUtils.and(spec, buildSearchSpecification.apply(finalSearchText));
            if (split != null) {
                searchText = split[1];
            }
        } while (split != null);
        return spec;
    }

    public static <T> Specification<T> andParam(Specification<T> spec, JsonNode paramsNode, String... fields) {
        for (String field : fields) {
            if (!paramsNode.has(field)) {
                continue;
            }
            JsonNode param = paramsNode.get(field);
            Integer value = null;
            if (param.isInt()) {
                value = param.asInt();
            } else if (param.isObject()) {
                value = param.get("id").asInt();
            }
            if (value == null) {
                continue;
            }
            final int id = value;
            spec = SpecificationUtils.and(spec, (root, query, builder) -> {
                return builder.equal(root.get(field).get("id"), id);
            });
        }
        return spec;
    }

    public static <T> Specification<T> andFieldEqualParam(Specification<T> spec, JsonNode paramsNode, String... fields) {
        for (String field : fields) {
            if (!paramsNode.has(field)) {
                continue;
            }
            JsonNode param = paramsNode.get(field);
            if (param.isInt()) {
                spec = SpecificationUtils.and(spec, (root, query, builder) -> {
                    return builder.equal(root.get(field), param.asInt());
                });
            } else if (param.isTextual()) {
                spec = SpecificationUtils.and(spec, (root, query, builder) -> {
                    return builder.equal(root.get(field), param.asText());
                });
            } else if (param.isFloat() || param.isDouble()) {
                spec = SpecificationUtils.and(spec, (root, query, builder) -> {
                    return builder.equal(root.get(field), param.asDouble());
                });
            } else if (param.isBoolean()) {
                spec = SpecificationUtils.and(spec, (root, query, builder) -> {
                    return builder.equal(root.get(field), param.asBoolean());
                });
            }
        }
        return spec;
    }
}
