package cn.ibizlab.core.data.repository;

import cn.ibizlab.core.data.dto.BaseData;
import cn.ibizlab.core.data.dto.FilterData;
import cn.ibizlab.core.data.model.POSchema;
import cn.ibizlab.core.data.elasticsearch.DynamicEsContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.IdsQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.*;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class EsDataRepository {

	@Autowired
	@Lazy
	private ReactiveElasticsearchTemplate esTemplate;

	final private String TYPE="_doc";



	/**
	 * 反射获取泛型类型
	 *
	 * @return
	 */
	protected Class<BaseData> getEntityClass()
	{
		return getEntityClass();
	}


	/**
	 * 将查询条件对象转换为query
	 *
	 * @param object
	 * @return
	 * @author Jason
	 */
	private Query getQueryByObject(BaseData object) {
		Criteria query = new Criteria();
		Map<String, Object> dataMap = (Map)object;
		Criteria criteria = new Criteria();
		for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
			criteria.and(entry.getKey()).is(entry.getValue());
		}
		return new CriteriaQuery(query);
	}


	public Query getKeyQuery(BaseData data)
	{
		Assert.notNull(data.getKey(),"未找到主键");
		return new CriteriaQuery(new Criteria().and("_id").is(data.getKey()));
	}

	public Query getKeyQuery(List list)
	{
		List<String> ids=new ArrayList<>();
		if(list.get(0) instanceof BaseData)
		{
			list.forEach(item -> {
				BaseData data = (BaseData) item;
				Serializable key=data.getKey();
				Assert.notNull(key,"未找到主键");
				ids.add(key.toString());
			});
		}
		else
		{
			ids.addAll(list);
		}
		return new CriteriaQuery(new Criteria().and("_id").in(ids));
	}

	public IdsQueryBuilder getKeyQueryBuilder(List list)
	{
		IdsQueryBuilder ids = QueryBuilders.idsQuery();
		if(list.get(0) instanceof BaseData)
		{
			list.forEach(item -> {
				BaseData data = (BaseData) item;
				Serializable key=data.getKey();
				Assert.notNull(key,"未找到主键");
				ids.addIds(key.toString());
			});
		}
		else
		{
			list.forEach(key->ids.addIds(key.toString()));
		}
		return ids;
	}

	public void insertData(String ds,POSchema schema, BaseData data){

		try
		{
			DynamicEsContextHolder.push(ds);
			data.setAll(this.esTemplate.save(data,schema.getName(),TYPE).block());
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public void insertBathData(String ds,POSchema schema, List<BaseData> list){

		try
		{
			DynamicEsContextHolder.push(ds);
			this.esTemplate.save(Flux.fromIterable(list),schema.getName(),TYPE).block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public void updateData(String ds,POSchema schema, BaseData data){
		try
		{
			DynamicEsContextHolder.push(ds);
			data.setAll(this.esTemplate.save(data,schema.getName(),TYPE).block());
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public void updateBathData(String ds,POSchema schema, List<BaseData> list){
		try
		{
			DynamicEsContextHolder.push(ds);
			this.esTemplate.save(Flux.fromIterable(list),schema.getName(),TYPE).block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public void removeData(String ds,POSchema schema, BaseData data){
		try
		{
			Assert.notNull(data.getKey(),"未找到主键");
			DynamicEsContextHolder.push(ds);
			this.esTemplate.deleteById(data.getKey().toString(),getEntityClass(),schema.getName(),TYPE).block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public void removeBathData(String ds,POSchema schema, List list){
		try
		{
			DynamicEsContextHolder.push(ds);
			esTemplate.deleteBy(getKeyQuery(list),getEntityClass(),schema.getName(),TYPE).block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public void saveData(String ds,POSchema schema, BaseData data){
		try
		{
			DynamicEsContextHolder.push(ds);
			data.setAll(this.esTemplate.save(data, schema.getName(),TYPE).block());;
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public void saveBatchData(String ds,POSchema schema, List<BaseData> list){
		try
		{
			DynamicEsContextHolder.push(ds);
			this.esTemplate.save(Flux.fromIterable(list),schema.getName(),TYPE).block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public long countData(String ds,POSchema schema, BaseData data){
		try
		{
			DynamicEsContextHolder.push(ds);
			return esTemplate.count(getQueryByObject(data),getEntityClass(),schema.getName(),TYPE).block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public List<BaseData> getData(String ds,POSchema schema, BaseData data){
		try
		{
			Assert.notNull(data.getKey(),"未找到主键");
			DynamicEsContextHolder.push(ds);
			return esTemplate.find(getKeyQuery(data),getEntityClass(),schema.getName(),TYPE).collectList().block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public List<BaseData> getBatchData(String ds,POSchema schema, List<BaseData> list){
		try
		{
			DynamicEsContextHolder.push(ds);
			return esTemplate.find(getKeyQuery(list),getEntityClass(),schema.getName(),TYPE).collectList().block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public List<BaseData> getBatchKey(String ds,POSchema schema, List<BaseData> list){
		try
		{
			NativeSearchQuery query = new NativeSearchQueryBuilder()
					.withQuery(getKeyQueryBuilder(list)).withSourceFilter(new SourceFilter() {
						@Override
						public String[] getIncludes() {
							return new String[]{"_id"};
						}

						@Override
						public String[] getExcludes() {
							return new String[]{""};
						}
					}).build();
			DynamicEsContextHolder.push(ds);
			return esTemplate.find(query,getEntityClass(),schema.getName(),TYPE).collectList().block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}



	public List<BaseData> selectData(String ds,POSchema schema, BaseData data){
		try
		{
			DynamicEsContextHolder.push(ds);
			return esTemplate.find(getQueryByObject(data),getEntityClass(),schema.getName(),TYPE).collectList().block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public Page<BaseData> selectData(String ds,POSchema schema, BaseData data, Pageable page){
		try
		{
			if(page==null)
				page= PageRequest.of(0,20, Sort.unsorted());
			Query query = getQueryByObject(data);

			DynamicEsContextHolder.push(ds);
			long total = esTemplate.count(query, getEntityClass(),schema.getName()).block();
			List<BaseData> list=(total==0)?new ArrayList<>():esTemplate.find(query.setPageable(page),getEntityClass(),schema.getName()).collectList().block();
			return new PageImpl<BaseData>(list,page,total);
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public List<BaseData> queryData(String ds,POSchema schema, String sql, FilterData context){
		try
		{
			DynamicEsContextHolder.push(ds);
			return esTemplate.find(context.getEsQuery(),getEntityClass(),schema.getName(),TYPE).collectList().block();
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

	public Page<BaseData> queryData(String ds,POSchema schema, String sql, FilterData context,  Pageable page){
		try
		{
			if(page==null)
				page=context.getPageable();
			else
				context.setPageable(page);
			DynamicEsContextHolder.push(ds);
			Query query=context.getEsQuery();
			long total = esTemplate.count(query,getEntityClass(),schema.getName(),TYPE).block();
			List<BaseData> list=(total==0)?new ArrayList<>():esTemplate.find(query.setPageable(page),getEntityClass(),schema.getName(),TYPE).collectList().block();
			return new PageImpl<BaseData>(list,page,total);
		}
		finally {
			DynamicEsContextHolder.poll();
		}
	}

}

