package cn.ibizlab.util.filter;

import cn.ibizlab.core.data.filter.QueryFilter;
import cn.ibizlab.core.data.filter.SearchContextBase;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

@Slf4j
@Data
public class EsQueryContext extends SearchContextBase {

    @JsonIgnore
    @JSONField(serialize = false)
    private CriteriaQuery selectCond=null;


    public CriteriaQuery getSearchCond(){
        return this.selectCond;
    }

    /**
     * 填充自定义查询条件
     * @return
     */
    public CriteriaQuery getSelectCond() {
        if(selectCond==null&&(!ObjectUtils.isEmpty(filter))){
            Criteria queryBuilder=parseQueryFilter(filter);
            if(!ObjectUtils.isEmpty(queryBuilder)){
                selectCond=new CriteriaQuery(queryBuilder);
            }
        }
        else if(selectCond==null)
        {
            selectCond=new CriteriaQuery(new Criteria());
        }
        return selectCond;
    }

    /**
     * 解析自定义查询条件
     * @param queryFilter
     * @return
     */
    private Criteria parseQueryFilter(QueryFilter queryFilter){

        if(ObjectUtils.isEmpty(queryFilter.get$and()) && ObjectUtils.isEmpty(queryFilter.get$or()) && ObjectUtils.isEmpty(queryFilter.any())) {
            return null;
        }
        Criteria rsBuilder=new Criteria();
        Criteria fieldBuilder=parseFieldMap(queryFilter.any());
        Criteria orBuilder=parseOrQueryFilter(queryFilter.get$or());
        Criteria andBuilder=parseAndQueryFilter(queryFilter.get$and());
        if(!ObjectUtils.isEmpty(fieldBuilder)){
            rsBuilder.and(fieldBuilder);
        }
        if(!ObjectUtils.isEmpty(orBuilder)){
            rsBuilder.and(orBuilder);
        }
        if(!ObjectUtils.isEmpty(andBuilder)){
            rsBuilder.and(andBuilder);
        }
        return rsBuilder;
    }

    /**
     * 解析自定义条件[or]
     * @param queryFilters
     * @return
     */
    private Criteria parseOrQueryFilter(List<QueryFilter> queryFilters) {
        Criteria orBuilder =new Criteria();
        if(queryFilters==null || queryFilters.size()==0){
            return null;
        }
        for(QueryFilter queryFilter: queryFilters){
            Criteria queryBuilder=parseQueryFilter(queryFilter);
            if(!ObjectUtils.isEmpty(queryBuilder)){
                orBuilder.or(queryBuilder);
            }
        }
        return orBuilder;
    }

    /**
     * 解析自定义条件[and]
     * @param queryFilters
     * @return
     */
    private Criteria parseAndQueryFilter(List<QueryFilter> queryFilters) {
        Criteria orBuilder =new Criteria();
        if(queryFilters==null || queryFilters.size()==0){
            return null;
        }
        for(QueryFilter queryFilter: queryFilters){
            Criteria queryBuilder=parseQueryFilter(queryFilter);
            if(!ObjectUtils.isEmpty(queryBuilder)){
                orBuilder.and(queryBuilder);
            }
        }
        return orBuilder;
    }

    /**
     * 解析自定义条件[字段条件]
     * @param fieldMap
     * @return
     */
    private Criteria parseFieldMap(Map<String , QueryFilter.SegmentCond> fieldMap) {
        if(fieldMap.size()==0) {
            return null;
        }
        Criteria fieldBuilders=new Criteria();
        for(Map.Entry<String, QueryFilter.SegmentCond> entry: fieldMap.entrySet()){
            getSegmentCondSql(entry.getKey(),entry.getValue(),fieldBuilders);
        }
        return fieldBuilders;
    }

    /**
     * 解析自定义条件[字段条件]
     * @param fieldName
     * @param segmentCond
     * @param fieldBuilder
     */
    private void getSegmentCondSql(String fieldName, QueryFilter.SegmentCond segmentCond,Criteria fieldBuilder) {

        Map<String , Object> segmentCondMap =  segmentCond.any();
        for(Map.Entry<String , Object> entry: segmentCondMap.entrySet()){
            Object value=entry.getValue();
            switch (entry.getKey()){
                case "$eq":
                    fieldBuilder.and(fieldName).is(value);
                    break;
                case "$ne":
                    fieldBuilder.not().and(fieldName).is(value);
                    break;
                case "$gt":
                    fieldBuilder.and(fieldName).greaterThan(value);
                    break;
                case "$gte":
                    fieldBuilder.and(fieldName).greaterThanEqual(value);
                    break;
                case "$lt":
                    fieldBuilder.and(fieldName).lessThan(value);
                    break;
                case "$lte":
                    fieldBuilder.and(fieldName).lessThanEqual(value);
                    break;
                case "$null":
                    fieldBuilder.and(fieldName).is(null);
                    break;
                case "$notNull":
                    fieldBuilder.not().and(fieldName).is(null);
                    break;
                case "$in":
                    fieldBuilder.and(fieldName).in(value);
                    break;
                case "$notIn":
                    fieldBuilder.and(fieldName).notIn(value);
                    break;
                case "$like":
                    if(!ObjectUtils.isEmpty(value))
                        fieldBuilder.and(fieldName).contains(value.toString());
                    break;
                case "$startsWith":
                    if(!ObjectUtils.isEmpty(value))
                        fieldBuilder.and(fieldName).startsWith(value.toString());
                    break;
                case "$endsWith":
                    if(!ObjectUtils.isEmpty(value))
                        fieldBuilder.and(fieldName).endsWith(value.toString());
                    break;
                case "$exists":
                    break;
                case "$notExists":
                    break;
            }
        }
    }
}
