package com.zcxk.core.mongo.base;

import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.zcxk.core.common.pojo.PageResult;
import com.zcxk.core.mongo.annotation.QueryField;
import com.zcxk.core.mongo.pageing.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
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 java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author Andy
 * @date 2021-07-22 11:03
 */
public class BaseDao<T, PK extends Serializable> {

    @Autowired
    public MongoTemplate mongoTemplate;
    protected Class<T> entityClass;
    private static final String ID_FIELD = "_id";

    public BaseDao() {
        Class<?> c = this.getClass();
        Type type = c.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
            this.entityClass = (Class) parameterizedType[0];
        }
    }

    public Long count(Query query) {
        return this.mongoTemplate.count(query, this.entityClass);
    }

    public T findById(PK id) {
        return this.mongoTemplate.findById(id, this.entityClass);
    }

    public T findOne(Query query) {
        return this.mongoTemplate.findOne(query, this.entityClass);
    }

    public void insert(List<T> list) {
        this.mongoTemplate.insert(list, this.entityClass);
    }

    public UpdateResult update(Query q, Update update) {
        return this.mongoTemplate.upsert(q, update, this.entityClass);
    }

    public UpdateResult upsert(Query query, Update update) {
        return mongoTemplate.upsert(query, update, entityClass);
    }

    public UpdateResult updateById(PK id, Update update) {
        Query query = Query.query(Criteria.where(ID_FIELD).is(id));
        return update(query, update, true);
    }

    public UpdateResult update(Query query, Update update, boolean isFirst) {
        UpdateResult wr;
        if (isFirst) {
            wr = mongoTemplate.updateFirst(query, update, entityClass);
        } else {
            wr = mongoTemplate.updateMulti(query, update, entityClass);
        }
        return wr;
    }

    public UpdateResult updateFirst(Query query, Update update) {
        return mongoTemplate.updateFirst(query, update, entityClass);
    }

    public UpdateResult updateMulti(Query query, Update update) {
        return mongoTemplate.updateMulti(query, update, entityClass);
    }

    public void save(T entity) {
        this.mongoTemplate.save(entity);
    }

    public List<T> findList(Query query, Sort... sort) {
        return this.findList(query, 0, 0, sort);
    }

    public List<T> findList(Query query) {
        return this.findList(query, 0, 0, (Sort[]) null);
    }

    public List<T> findList(Query query, int limit) {
        Sort sort = null;
        return this.findList(query, 0, limit, (Sort) sort);
    }

    public List<T> findList(Query query, int start, int limit, Sort... sort) {
        if (query == null) {
            query = new Query();
        }

        if (sort !=null){
            for (Sort s : sort) {
                if (s != null){
                    query.with(s);
                }
            }
        }

        if (limit > 0) {
            query.limit(limit);
        }

        if (start > 0) {
            query.skip((long) start);
        }

        return this.mongoTemplate.find(query, this.entityClass);
    }

    public List<T> findLists(Query query) {
        return this.mongoTemplate.find(query, this.entityClass);
    }

    public List<T> findListByIds(Collection<PK> ids, Sort... sort) {
        Query query = Query.query(Criteria.where(ID_FIELD).in(ids));
        if (sort != null) {
            for (Sort s : sort) {
                if (s != null){
                    query.with(s);
                }
            }
        }
        return findList(query);
    }

    /**
     * 分页查询
     */
    public Page findPage(Query query, Pageable pageable, Sort... sort) {
        if (query == null) {
            query = new Query();
        }
        Long total = count(query);
        List<T> content = new ArrayList<>();
        if (total > 0) {
            for (Sort s : sort) {
                if (s != null){
                    query.with(s);
                }
            }
            if(pageable != null){
                query.with(pageable);
            }
            content = findList(query);
        }
        return new Page(total.longValue(), content);

    }

    /**
     * 分页查询
     */
    public PageResult findPageResult(Query query, Pageable pageable, Sort... sort) {
        if (query == null) {
            query = new Query();
        }
        Long total = count(query);
        Integer totalSize = total == null ? 0 : total.intValue();
        List<T> content = new ArrayList<>();
        if (total > 0) {
            for (Sort s : sort) {
                if (s != null){
                    query.with(s);
                }
            }

            if(pageable != null){
                query.with(pageable);
            }
            content = findList(query);
        }
        Integer currentPage = pageable != null ? pageable.getPageNumber() : 0;
        Integer pageSize = pageable != null ? pageable.getPageSize() : totalSize;
        return PageResult.builder(content, currentPage + 1, pageSize, totalSize);
    }

    public DeleteResult remove(T entity) {
        return this.mongoTemplate.remove(entity);
    }

    public DeleteResult remove(Query query) {
        return this.mongoTemplate.remove(query, entityClass);
    }

    public T saveEntity(T entity) {
        mongoTemplate.save(entity);
        return entity;
    }

    public List<T> findAll() {
        return mongoTemplate.findAll(entityClass);
    }

    /**
    * @author Andy
    * @description 聚合查询
    * @date 18:44 2021/7/27
    * @param mainCriteria : 主文档查询
    * @param unwind：需要拆开的子文档
    * @param unwindCriteria ：子文档查询,
    * @param groupOperation : 分组查询
    * @return java.util.List<org.springframework.data.mongodb.core.aggregation.AggregationOperation>
    **/
    public List<AggregationOperation> aggrQueryCondition(Criteria mainCriteria, String unwind, Criteria unwindCriteria, GroupOperation groupOperation) {
        List<AggregationOperation> commonOperations = new ArrayList<>();
        // 1. 添加主查询条件
        commonOperations.add(Aggregation.match(mainCriteria));
        // 2.拆开
        commonOperations.add(Aggregation.unwind(unwind));
        // 3.子文档查询
        commonOperations.add(Aggregation.match(unwindCriteria));
        // 4.分组
        if (groupOperation != null) {
            commonOperations.add(groupOperation);
        }
        return commonOperations;
    }

    /**
     * 根据vo构建查询条件Query
     * @param t
     * @return
     */
    public Query buildBaseQuery(T t) {
        Query query = new Query();

        List<Field> fields = getAllFields(t);
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    QueryField queryField = field.getAnnotation(QueryField.class);
                    if (queryField != null) {
                        query.addCriteria(queryField.type().buildCriteria(queryField, field, value));
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return query;
    }

    /**
     * 根据vo构建查询条件Query
     * @param list
     * @return
     */
    public Query buildQuery(List<Criteria> list){
        Query query = new Query();
        for(Criteria c : list){
            query.addCriteria(c);
        }
        return query ;
    }
    public List<Criteria> buildCriteriaList(T t){
        List<Criteria> criteriaList = new ArrayList<>();
        List<Field> fields = getAllFields(t);
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    QueryField queryField = field.getAnnotation(QueryField.class);
                    if (queryField != null) {
                        criteriaList.add(queryField.type().buildCriteria(queryField, field, value));
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return criteriaList ;
    }

    public List<Criteria> combineCriteria(List<Criteria>... criteriaAvgs ){
        List<Criteria> result = new ArrayList<>();
        Map<String,List<Criteria>> map = new HashMap<>() ;
        for(List<Criteria> cList : criteriaAvgs){
            for(Criteria c : cList){
                String key = c.getKey();
                List<Criteria> existList = map.get(key);
                if(existList == null){
                    existList = new ArrayList<>();
                    existList.add(c);
                    map.put(key,existList) ;
                }
                else{
                    existList.add(c);
                }
            }
        }
        for(Map.Entry<String,List<Criteria>> entry : map.entrySet() ){
            List<Criteria> value = entry.getValue();
            if(value.size() == 1){
                result.addAll(value) ;
            }
            else{
                Criteria tmp = new Criteria();
                tmp.andOperator(value.toArray(new Criteria[value.size()]));
                result.add(tmp);
            }
        }
        return result ;
    }

    /**
     * 根据vo构建更新条件Query
     * @param t
     * @return
     */
    public Update buildBaseUpdate(T t) {
        Update update = new Update();

        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    if(StringUtils.equals("serialVersionUID",field.getName())){
                        continue;
                    }
                    update.set(field.getName(), value);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return update;
    }

    private List<Field> getAllFields(T t) {
        List<Field> fieldList = new ArrayList<>() ;
        Class tempClass = t.getClass();
        // 当父类为null的时候说明到达了最上层的父类(Object类).
        while (tempClass != null) {
            fieldList.addAll(Arrays.asList(tempClass .getDeclaredFields()));
            //得到父类,然后赋给自己
            tempClass = tempClass.getSuperclass();
        }
        return fieldList;
    }

}
