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.mongodb.DynamicMongoContextHolder;
import cn.ibizlab.util.errors.BadRequestAlertException;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.*;

@Slf4j
@Component
public class MongoDataRepository {

	@Autowired
	@Lazy
	private MongoTemplate mongoTemplate;




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


	/**
	 * 将查询条件对象转换为query
	 *
	 * @param object
	 * @return
	 * @author Jason
	 */
	private Query getQueryByObject(BaseData object) {
		Query query = new Query();
		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());
		}
		query.addCriteria(criteria);
		return query;
	}

	/**
	 * 将查询条件对象转换为update
	 *
	 * @param object
	 * @return
	 * @author Jason
	 */
	private Update getUpdateByObject(BaseData object) {
		Update update = new Update();
		Map<String, Object> dataMap = (Map)object;
		for (Map.Entry<String,Object> entry : dataMap.entrySet()) {
			update.set(entry.getKey(), entry.getValue());
		}
		return update;
	}


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

	public Query getKeyQuery(List list)
	{
		Query query = new Query();
		if(ObjectUtils.isEmpty(list))
			return query;
		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 query.addCriteria(new Criteria().and("_id").in(ids));
	}


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

		try
		{
			DynamicMongoContextHolder.push(ds);
			this.mongoTemplate.save(data, schema.getName());
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

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

		try
		{
			DynamicMongoContextHolder.push(ds);
			this.mongoTemplate.insert(list, schema.getName());
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public long updateData(String ds,POSchema schema, BaseData data){
		try
		{
			Query query = getKeyQuery(data);
			Update update = getUpdateByObject(data);

			DynamicMongoContextHolder.push(ds);
			return this.mongoTemplate.updateFirst(query, update, schema.getName()).getModifiedCount();
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public void updateBathData(String ds,POSchema schema, List<BaseData> list){
		try
		{
			DynamicMongoContextHolder.push(ds);
			BulkOperations ops = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, schema.getName());
			list.forEach(data -> {
				Query query = getKeyQuery(data);
				Update update = getUpdateByObject(data);
				ops.updateOne(query, update);
			});
			ops.execute();
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public long removeData(String ds,POSchema schema, BaseData data){
		try
		{
			DynamicMongoContextHolder.push(ds);
			return this.mongoTemplate.remove(getKeyQuery(data),schema.getName()).getDeletedCount();
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public long removeBathData(String ds,POSchema schema, List list){
		try
		{
			DynamicMongoContextHolder.push(ds);
			return mongoTemplate.remove(getKeyQuery(list),schema.getName()).getDeletedCount();
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public long saveData(String ds,POSchema schema, BaseData data){
		try
		{
			Query query = getKeyQuery(data);
			Update update = getUpdateByObject(data);

			DynamicMongoContextHolder.push(ds);
			return this.mongoTemplate.upsert(query, update, schema.getName()).getModifiedCount();
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public void saveBatchData(String ds,POSchema schema, List<BaseData> list){
		try
		{
			DynamicMongoContextHolder.push(ds);
			BulkOperations ops = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, schema.getName());
			list.forEach(data -> {
				Query query = getKeyQuery(data);
				Update update = getUpdateByObject(data);
				ops.upsert(query, update);
			});
			ops.execute();
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public long countData(String ds,POSchema schema, BaseData data){
		try
		{
			DynamicMongoContextHolder.push(ds);
			return mongoTemplate.count(getQueryByObject(data),schema.getName());
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public List<BaseData> getData(String ds,POSchema schema, BaseData data){
		try
		{
			DynamicMongoContextHolder.push(ds);
			return mongoTemplate.find(getKeyQuery(data),getEntityClass(),schema.getName());
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public List<BaseData> getBatchData(String ds,POSchema schema, List<BaseData> list){
		try
		{
			DynamicMongoContextHolder.push(ds);
			return mongoTemplate.find(getKeyQuery(list),getEntityClass(),schema.getName());
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public List<BaseData> getBatchKey(String ds,POSchema schema, List<BaseData> list){
		try
		{
			BasicDBObject query=new BasicDBObject();
			BasicDBList values = new BasicDBList();
			list.forEach(item->{
				Serializable key=item.getKey();
				Assert.notNull(key,"未找到主键");
				values.add(key);
			});
			BasicDBObject in = new BasicDBObject("$in", values);
			query.put("_id",in);
			BasicDBObject fieldsObject = new BasicDBObject();
			fieldsObject.put("_id", true);
			Query queryIds = new BasicQuery(query.toJson(), fieldsObject.toJson());

			DynamicMongoContextHolder.push(ds);
			return mongoTemplate.find(queryIds,getEntityClass(),schema.getName());
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}



	public List<BaseData> selectData(String ds,POSchema schema, BaseData data){
		try
		{
			DynamicMongoContextHolder.push(ds);
			return mongoTemplate.find(getQueryByObject(data),getEntityClass(),schema.getName());
		}
		finally {
			DynamicMongoContextHolder.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);

			DynamicMongoContextHolder.push(ds);
			long total = mongoTemplate.count(query, BaseData.class,schema.getName());
			List<BaseData> list=(total==0)?new ArrayList<>():mongoTemplate.find(query.with(page),BaseData.class,schema.getName());
			return new PageImpl<BaseData>(list,page,total);
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

	public List<BaseData> queryData(String ds,POSchema schema, String sql, FilterData context){
		try
		{
			DynamicMongoContextHolder.push(ds);
			Query query = new BasicQuery(context.getQueryBuilder().get().toString());
			return mongoTemplate.find(query,BaseData.class,schema.getName());
		}
		finally {
			DynamicMongoContextHolder.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);
			Query query = new BasicQuery(context.getQueryBuilder().get().toString());

			DynamicMongoContextHolder.push(ds);
			long total = mongoTemplate.count(query, BaseData.class,schema.getName());
			List<BaseData> list=(total==0)?new ArrayList<>():mongoTemplate.find(query.with(page),BaseData.class,schema.getName());
			return new PageImpl<BaseData>(list,page,total);
		}
		finally {
			DynamicMongoContextHolder.poll();
		}
	}

}

