package cn.ibizlab.core.data.dto;

import cn.ibizlab.core.data.filter.QueryBuildContext;
import cn.ibizlab.core.data.model.POSchema;
import cn.ibizlab.core.data.filter.QueryFilter;
import cn.ibizlab.core.data.filter.QueryWrapperContext;
import cn.ibizlab.util.filter.EsQueryContext;
import cn.ibizlab.util.helper.DataObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.mongodb.BasicDBObject;
import com.mongodb.QueryBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.regex.Pattern;

public class FilterData<T> extends BaseData
{
	public FilterData set(String key, Object value)
	{
		this.put(key,value);
		return this;
	}

	public FilterData setAll(Map map)
	{
		if(map!=null)
			this.putAll(map);
		else if(this.size()==0)
			return null;
		return this;
	}

	public boolean needPage()
	{
		return this.keySet().contains("page")||this.keySet().contains("size");
	}

	public String getDataSource()
	{
		return this.getStringValue("datasource");
	}

	public String getQuery()
	{
		return this.getStringValue("query");
	}

	public FilterData setQuery(String query)
	{
		return this.set("query",query);
	}


	public int getPage()
	{
		return this.getIntegerValue("page",0);
	}

	public FilterData setPage(int page)
	{
		return this.set("page",page);
	}

	public int getSize()
	{
		return getIntegerValue("size",20);
	}

	public FilterData setSize(int size)
	{
		return this.set("size",size);
	}

	public String getSort()
	{
		return this.getStringValue("sort");
	}

	public FilterData setSort(String sort) {

		pageable=PageRequest.of(getPage(),getSize(),getPageSort());
		return this.set("sort",sort);
	}

	@JsonIgnore
	@JSONField(serialize = false)
	public Sort pageSort;
	@JsonIgnore
	@JSONField(serialize = false)
	public Sort getPageSort()
	{
		if(pageSort==null)
		{
			if(!StringUtils.isEmpty(getSort())){
				String sortArr[]=getSort().split(",");
				String sortField=sortArr[0];
				String sortDirection=sortArr.length>1?sortArr[1]:"asc";
				if(sortDirection.equalsIgnoreCase("desc"))
					this.pageSort=Sort.by(Sort.Direction.DESC,sortField);
				else
					this.pageSort=Sort.by(Sort.Direction.ASC,sortField);
			}
			else
				this.pageSort=Sort.unsorted();
		}
		return pageSort;
	}

	@JsonIgnore
	@JSONField(serialize = false)
	public Pageable pageable;
	@JsonIgnore
	@JSONField(serialize = false)
	public Pageable getPageable() {
		if(pageable==null)
			pageable=PageRequest.of(getPage(),getSize(),getPageSort());
		return pageable;
	}

	public FilterData setPageable(Pageable pageable)
	{
		this.pageable=pageable;
		return this;
	}

	public FilterData addParent(String entity,String key)
	{
		super.addParent(entity,key);
		return this;
	}

	public String getUserTaskId() {
		return this.getStringValue("userTaskId",this.getParams().getStringValue("userTaskId"));
	}

	public FilterData setUserTaskId(String userTaskId)
	{
		return this.set("userTaskId",userTaskId);
	}

	public String getProcessDefinitionKey() {
		return this.getStringValue("processDefinitionKey",this.getParams().getStringValue("processDefinitionKey"));
	}

	public FilterData setProcessDefinitionKey(String processDefinitionKey)
	{
		return this.set("processDefinitionKey",processDefinitionKey);
	}

	private QueryFilter filter;

	public QueryFilter getFilter()
	{
		return filter;
	}

	public FilterData setFilter(QueryFilter filter)
	{
		this.filter=filter;
		return this;
	}




	@JsonIgnore
	@JSONField(serialize = false)
	public Page getPages()
	{
		return getPages(getPOSchema(),this.getPageable());
	}

	public static Page getPages(POSchema poSchema,Pageable pageable){
		Page page;

		int currentPage=pageable.getPageNumber();
		int pageSize=pageable.getPageSize();

		//构造mybatis-plus分页
		if(StringUtils.isEmpty(currentPage) || StringUtils.isEmpty(pageSize)) {
			page=new Page(1,Short.MAX_VALUE);
		}
		else {
			page=new Page(currentPage+1,pageSize);
		}

		//构造mybatis-plus排序
		Sort sort = pageable.getSort();
		Iterator<Sort.Order> it_sort = sort.iterator();

		if(ObjectUtils.isEmpty(it_sort)) {
			return page;
		}

		while (it_sort.hasNext()) {
			Sort.Order sort_order = it_sort.next();
			String colName=sort_order.getProperty();
			if(poSchema!=null&&poSchema.getColumn(colName)!=null)
				colName=poSchema.getColumn(colName).getName();
			page.addOrder(new OrderItem().setColumn(colName).setAsc(sort_order.getDirection()!= Sort.Direction.DESC));
		}

		return page;
	}

	@JsonIgnore
	@JSONField(serialize = false)
	private QueryWrapper<BaseData> searchCond=null;
	public QueryWrapper<BaseData> getSearchCond()
	{
		if(searchCond==null)
		{
			if(this.getFilter()!=null)
			{
				QueryWrapperContext<BaseData> context=new QueryWrapperContext<>();
				context.setFilter(this.getFilter());
				searchCond = context.getSelectCond();
			}
			else
			{
				searchCond=new QueryWrapper<>();
			}

			if(this.getPOSchema()!=null)
			{
				if(!StringUtils.isEmpty(this.getQuery()))
				{
					if(!ObjectUtils.isEmpty(this.getPOSchema().getQuickSearch()))
					{
						searchCond.and(qw-> {
							int i=0;
							for(String column:this.getPOSchema().getQuickSearch().keySet())
							{
								if(i>0)
									qw.or();
								qw.like(column,this.getQuery());
								i++;
							}
						});
					}
				}

				if(!ObjectUtils.isEmpty(this.getPOSchema().getSearchMap()))
				{
					for(Map.Entry<String, POSchema.Column> search:this.getPOSchema().getSearchMap().entrySet())
					{
						String key=search.getKey().toLowerCase();
						String name=search.getValue().getName().toLowerCase();
						if(this.keySet().contains(key))
						{
							POSchema.Column column=search.getValue();
							Object obj=column.isDateTime()?this.getTimestampValue(key,null):this.get(key);
							if(!ObjectUtils.isEmpty(obj)) {
								if(key.endsWith("_like"))
									searchCond.like(name,obj);
								else if(key.endsWith("_leftlike"))
									searchCond.likeRight(name,obj);
								else if(key.endsWith("_rightlike"))
									searchCond.likeLeft(name,obj);
								else if(key.endsWith("_eq"))
									searchCond.eq(name,obj);
								else if(key.endsWith("_noteq"))
									searchCond.ne(name,obj);
								else if(key.endsWith("_gt"))
									searchCond.gt(name,obj);
								else if(key.endsWith("_gtandeq"))
									searchCond.ge(name,obj);
								else if(key.endsWith("_lt"))
									searchCond.lt(name,obj);
								else if(key.endsWith("_ltandeq"))
									searchCond.le(name,obj);
								else if(key.endsWith("_isnotnull")&& DataObject.getIntegerValue(obj,1)==1)
									searchCond.isNotNull(name);
								else if(key.endsWith("_isnull")&& DataObject.getIntegerValue(obj,1)==1)
									searchCond.isNull(name);
								else if(key.endsWith("_in"))
									searchCond.in(name,DataObject.getStringValue(obj,"").split(";|,"));
								else if(key.endsWith("_notin"))
									searchCond.notIn(name,DataObject.getStringValue(obj,"").split(";|,"));
							}
						}
					}

				}

			}
		}
		return searchCond;
	}

	@JsonIgnore
	@JSONField(serialize = false)
	private QueryBuilder queryBuilder=null;
	@JsonIgnore
	@JSONField(serialize = false)
	public QueryBuilder getQueryBuilder() {
		if(queryBuilder==null)
		{
			if(!ObjectUtils.isEmpty(filter)){
				QueryBuildContext context=new QueryBuildContext();
				context.setFilter(this.getFilter());
				queryBuilder=context.getSelectCond();
			}
			else
				queryBuilder=new QueryBuilder();
		}

		if(this.getPOSchema()!=null)
		{
			if(!StringUtils.isEmpty(this.getQuery()))
			{
				if(!ObjectUtils.isEmpty(this.getPOSchema().getQuickSearch()))
				{
					Pattern pattern = Pattern.compile("^.*" + this.getQuery() + ".*$", Pattern.CASE_INSENSITIVE);
					QueryBuilder qw=new QueryBuilder();
					for(String column:this.getPOSchema().getQuickSearch().keySet())
						qw.or(new BasicDBObject(column,pattern));
					queryBuilder.and(qw.get());
				}
			}

			if(!ObjectUtils.isEmpty(this.getPOSchema().getSearchMap()))
			{
				for(Map.Entry<String, POSchema.Column> search:this.getPOSchema().getSearchMap().entrySet())
				{
					String key=search.getKey().toLowerCase();
					String name=search.getValue().getName().toLowerCase();
					if(this.keySet().contains(key))
					{
						POSchema.Column column=search.getValue();
						Object obj=column.isDateTime()?this.getTimestampValue(key,null):this.get(key);
						if(!ObjectUtils.isEmpty(obj)) {
							if(key.endsWith("_like")) {
								Pattern pattern = Pattern.compile("^.*" + obj.toString() + ".*$", Pattern.CASE_INSENSITIVE);
								queryBuilder.and(name).regex(pattern);
							}
							else if(key.endsWith("_leftlike")){
								Pattern pattern = Pattern.compile(obj.toString() + ".*$", Pattern.CASE_INSENSITIVE);
								queryBuilder.and(name).regex(pattern);
							}
							else if(key.endsWith("_rightlike")){
								Pattern pattern = Pattern.compile("^.*" + obj.toString() , Pattern.CASE_INSENSITIVE);
								queryBuilder.and(name).regex(pattern);
							}
							else if(key.endsWith("_eq"))
								queryBuilder.and(name).is(obj);
							else if(key.endsWith("_noteq"))
								queryBuilder.and(name).notEquals(obj);
							else if(key.endsWith("_gt"))
								queryBuilder.and(name).greaterThan(obj);
							else if(key.endsWith("_gtandeq"))
								queryBuilder.and(name).greaterThanEquals(obj);
							else if(key.endsWith("_lt"))
								queryBuilder.and(name).lessThan(obj);
							else if(key.endsWith("_ltandeq"))
								queryBuilder.and(name).lessThanEquals(obj);
							else if(key.endsWith("_isnotnull")&& DataObject.getIntegerValue(obj,1)==1)
								queryBuilder.and(name).exists(true).and(name).notEquals("").and(name).notEquals(null);
							else if(key.endsWith("_isnull")&& DataObject.getIntegerValue(obj,1)==1)
								queryBuilder.and(name).exists(null);
							else if(key.endsWith("_in"))
								queryBuilder.and(name).in(DataObject.getStringValue(obj,"").split(";|,"));
							else if(key.endsWith("_notin"))
								queryBuilder.and(name).notIn(DataObject.getStringValue(obj,"").split(";|,"));
						}
					}
				}

			}

		}

		return queryBuilder;
	}



	@JsonIgnore
	@JSONField(serialize = false)
	private CriteriaQuery esCriteriaQuery=null;
	@JsonIgnore
	@JSONField(serialize = false)
	public CriteriaQuery getEsQuery() {
		if(esCriteriaQuery==null)
		{
			esCriteriaQuery=new EsQueryContext().getSelectCond();
		}

		if(this.getPOSchema()!=null)
		{
			if(!StringUtils.isEmpty(this.getQuery()))
			{
				if(!ObjectUtils.isEmpty(this.getPOSchema().getQuickSearch()))
				{
					Criteria critera=new Criteria();
					for(String column:this.getPOSchema().getQuickSearch().keySet())
						critera.or(column).contains(this.getQuery());
					esCriteriaQuery.getCriteria().and(critera);
				}
			}

			if(!ObjectUtils.isEmpty(this.getPOSchema().getSearchMap()))
			{
				for(Map.Entry<String, POSchema.Column> search:this.getPOSchema().getSearchMap().entrySet())
				{
					String key=search.getKey().toLowerCase();
					String name=search.getValue().getName().toLowerCase();
					if(this.keySet().contains(key))
					{
						POSchema.Column column=search.getValue();
						Object obj=column.isDateTime()?this.getTimestampValue(key,null):this.get(key);
						if(!ObjectUtils.isEmpty(obj)) {
							if(key.endsWith("_like")) {
								esCriteriaQuery.getCriteria().and(name).contains(obj.toString());
							}
							else if(key.endsWith("_leftlike")){
								esCriteriaQuery.getCriteria().and(name).startsWith(obj.toString());
							}
							else if(key.endsWith("_rightlike")){
								esCriteriaQuery.getCriteria().and(name).endsWith(obj.toString());
							}
							else if(key.endsWith("_eq"))
								esCriteriaQuery.getCriteria().and(name).is(obj);
							else if(key.endsWith("_noteq"))
								esCriteriaQuery.getCriteria().not().and(name).is(obj);
							else if(key.endsWith("_gt"))
								esCriteriaQuery.getCriteria().and(name).greaterThan(obj);
							else if(key.endsWith("_gtandeq"))
								esCriteriaQuery.getCriteria().and(name).greaterThanEqual(obj);
							else if(key.endsWith("_lt"))
								esCriteriaQuery.getCriteria().and(name).lessThan(obj);
							else if(key.endsWith("_ltandeq"))
								esCriteriaQuery.getCriteria().and(name).lessThanEqual(obj);
							else if(key.endsWith("_isnotnull")&& DataObject.getIntegerValue(obj,1)==1)
								esCriteriaQuery.getCriteria().not().and(name).is(null);
							else if(key.endsWith("_isnull")&& DataObject.getIntegerValue(obj,1)==1)
								esCriteriaQuery.getCriteria().and(name).is(null);
							else if(key.endsWith("_in"))
								esCriteriaQuery.getCriteria().and(name).in(DataObject.getStringValue(obj,"").split(";|,"));
							else if(key.endsWith("_notin"))
								esCriteriaQuery.getCriteria().and(name).notIn(DataObject.getStringValue(obj,"").split(";|,"));
						}
					}
				}

			}

		}

		return esCriteriaQuery;
	}

	public String getSql(String codename)
	{
		Assert.notNull(getPOSchema(),"未找到查询片段");
		POSchema.Segment segment=getPOSchema().getSegment(codename,"");
		Assert.notNull(segment,"未找到查询片段"+codename);
		String sql=segment.getBody();

		QueryWrapper qw=this.getSearchCond();
		if(qw!=null&&qw.getSqlSegment()!=null)
		{
			if(!qw.isEmptyOfWhere())
				sql=sql.concat(" where ");
			sql = sql.concat(qw.getSqlSegment());
		}

		if(!StringUtils.isEmpty(segment.getFormat()))
			return String.format(segment.getFormat(),sql);
		return sql;
	}


	public static FilterData fromContext(Map map)
	{
		if(map==null)
			return null;
		map.remove("datasource");
		if(map.size()==0)
			return null;

		String srfparentdename=DataObject.getStringValue(map.get("srfparentdename"),DataObject.getStringValue(map.get("data[srfparentdename]"),""));
		String srfparentkey=DataObject.getStringValue(map.get("srfparentkey"),DataObject.getStringValue(map.get("data[srfparentkey]"),""));
		FilterData filterData=null;
		if((!StringUtils.isEmpty(srfparentdename))&&(!StringUtils.isEmpty(srfparentkey)))
		{
			map.remove("srfparentdename");
			map.remove("data[srfparentdename]");
			map.remove("srfparentkey");
			map.remove("data[srfparentkey]");
			filterData=new FilterData().setAll(map);
			filterData.addParent(srfparentdename,srfparentkey);
		}
		else
			filterData=new FilterData().setAll(map);
		return filterData;
	}

}
