package com.tofly.es.config;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tofly.common.core.util.BeanUtils;
import com.tofly.common.core.util.Date8Util;
import lombok.SneakyThrows;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.repository.NoRepositoryBean;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author tuonbed
 * @date 2020/5/8  9:45
 */
@NoRepositoryBean
public interface ToflyEsRepository<T, ID extends Serializable> extends ElasticsearchRepository<T, ID> {
    default Page getPage(T bean, Page page,QueryBuilder... queryBuilder){
        BoolQueryBuilder qb =getBoolQueryBuilder(bean);
        for(QueryBuilder queryBuilder1:queryBuilder){
            qb.filter(queryBuilder1);
        }

        Pageable pageRequest=getPageable(page);
        org.springframework.data.domain.Page<T> search = this.search(qb,pageRequest);
        page.setRecords(search.getContent());
        page.setTotal(search.getContent().size());
        return page;
    };

    @SneakyThrows
    default boolean update(T bean){
        Class<T> tClass=this.getEntityClass();
        ID id=null;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(tClass);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

            Field[] declaredFields = tClass.getDeclaredFields();
            for(Field field:declaredFields){
                Id annotation = field.getAnnotation(Id.class);
                if(annotation!=null || field.getName().equalsIgnoreCase("id")){
                    field.setAccessible(true);
                    id=(ID)field.get(bean);
                    break;
                }
            }

            T oldBean=this.findById(id).get();
            if(oldBean==null){
                this.save(bean);
            }else{
                for (PropertyDescriptor property : propertyDescriptors) {
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(bean);
                    if(value!=null &&(!property.getName().equals("class"))){
                        Field f = tClass.getDeclaredField(property.getName());
                        f.setAccessible(true);
                        f.set(oldBean, value);
                    }
                }
                this.save(oldBean);
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e.getMessage());
        }
        return true;
    }

    default BoolQueryBuilder getBoolQueryBuilder(T bean){
        Map<String,Object> map= BeanUtils.beanToMap(bean,false);
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        if(map!=null){
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery(entry.getKey(),entry.getValue());
                queryBuilder.minimumShouldMatch("100%");
                qb.filter(queryBuilder);
            }
        }
        return qb;
    }

    default Pageable getPageable(Page page){
        List<OrderItem> orders = page.getOrders();
        Pageable pageRequest;
        if(orders.size()>0){
            List<String> asc=new ArrayList();
            List<String> desc=new ArrayList();
            orders.forEach(item->{
                if(item.isAsc()){
                    asc.add(item.getColumn());
                }else{
                    desc.add(item.getColumn());
                }
            });
            Sort sort;
            if(asc.size()>0 && desc.size()>0){
                sort=new Sort(Sort.Direction.ASC,asc);
                Sort sort1=new Sort(Sort.Direction.DESC,desc);
                sort.and(sort1);
            }else if(asc.size()>0){
                sort=new Sort(Sort.Direction.ASC,asc);
            }else{
                sort=new Sort(Sort.Direction.DESC,desc);
            }
            pageRequest = PageRequest.of(Long.valueOf(page.getCurrent()).intValue()-1,
                    Long.valueOf(page.getSize()).intValue(),sort);
        }else{
            pageRequest = PageRequest.of(Long.valueOf(page.getCurrent()).intValue()-1,
                    Long.valueOf(page.getSize()).intValue());
        }
        return pageRequest;
    }


    default Page getAllPage(T bean, Page page,int DyMonth,ElasticsearchTemplate elasticsearchTemplate, QueryBuilder... queryBuilder){


        NativeSearchQueryBuilder nativeSearchQueryBuilder =setNativeSearchQueryBuilder(bean,page, DyMonth,elasticsearchTemplate,queryBuilder);
        List<T> list = elasticsearchTemplate.queryForList(nativeSearchQueryBuilder.build(), this.getEntityClass());
//        org.springframework.data.domain.Page<T> search = this.search(qb,pageRequest);
        page.setRecords(list);
        page.setTotal(list.size());
        return page;
    };

    default NativeSearchQueryBuilder setNativeSearchQueryBuilder(T bean,Page page,int DyMonth,ElasticsearchTemplate elasticsearchTemplate,QueryBuilder... queryBuilder){
        BoolQueryBuilder qb =getBoolQueryBuilder(bean);
        Pageable pageRequest=getPageable(page);
        String index=getIndex();
        String[] indexs=new String[DyMonth];
        for(int i=0;i<DyMonth;i++){
            String theindex=index+"_"+ Date8Util.minusMonthstoString(i);
            if(elasticsearchTemplate.indexExists(theindex)){
                indexs[i]=index+"_"+ Date8Util.minusMonthstoString(i);
            }
        }
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        for(QueryBuilder queryBuilder1:queryBuilder){
            qb.filter(queryBuilder1);
        }
        nativeSearchQueryBuilder.withIndices(indexs).withQuery(qb)
                .withPageable(pageRequest);
        return nativeSearchQueryBuilder;
    }

    default List<T> getAll(T bean,Page page,int DyMonth,ElasticsearchTemplate elasticsearchTemplate, QueryBuilder... queryBuilder){
        NativeSearchQueryBuilder nativeSearchQueryBuilder =setNativeSearchQueryBuilder(bean,page, DyMonth,elasticsearchTemplate,queryBuilder);
        List<T> list = elasticsearchTemplate.queryForList(nativeSearchQueryBuilder.build(), this.getEntityClass());
        return list;
    }

    default String getIndex(){
        Class<T> tClass=this.getEntityClass();
        Document annotation = tClass.getAnnotation(Document.class);
        String[] indexName = annotation.indexName().split("_");
        return indexName[0];
    }





}
