package com.elab.mongodb;

import cn.hutool.core.bean.BeanUtil;
import com.mongodb.WriteResult;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.data.mongodb.core.MongoTemplate;
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.util.Assert;

import java.util.List;
import java.util.Map;

/**
 * 公共mongo操作
 *
 * @author Liukx
 * @create 2018-05-03 11:02
 * @email liukx@elab-plus.com
 **/
public abstract class BaseMongodb<T> {

    /**
     * 由客户端指定对应的MongoTemplate数据源定义
     *
     * @return
     */
    public abstract MongoTemplate getMongoTemplate();

    /**
     * 添加一个对象
     *
     * @param entity
     */
    public void save(T entity) {
        if (getCollectionName() == null) {
            getMongoTemplate().save(entity);
        } else {
            getMongoTemplate().save(entity, getCollectionName());
        }
    }

    /**
     * 添加一个集合对象
     *
     * @param entity
     */
    public void batchSave(List<T> entity) {
        if (getCollectionName() == null) {
            getMongoTemplate().insertAll(entity);
        } else {
            getMongoTemplate().insert(entity, getCollectionName());
        }
    }

    /**
     * 相当于mongo中的Collection,一般用对象表示
     *
     * @return
     */
    public abstract Class<T> getEntity();

    /**
     * db的名称 , 如果是用对象存储的则不用重写
     *
     * @return
     */
    protected String getCollectionName() {
        return null;
    }

    /**
     * 根据条件查看该数据是否存在
     *
     * @param entity
     * @return
     * @throws Exception
     */
    public boolean exists(T entity) throws Exception {
        Query query = beanForQuery(entity);
        boolean exists = false;
        if (getCollectionName() == null) {
            exists = getMongoTemplate().exists(query, getEntity());
        } else {
            exists = getMongoTemplate().exists(query, getEntity(), getCollectionName());
        }
        return exists;
    }

    /**
     * 根据条件查询总数
     *
     * @param entity
     * @return
     * @throws Exception
     */
    public Long count(T entity) throws Exception {

        return count(entity, getCollectionName());
    }

    /**
     * 根据条件查询总数
     *
     * @param entity         查询对象
     * @param collectionName 查询collection名称
     * @return
     * @throws Exception
     */
    public Long count(T entity, String collectionName) throws Exception {
        Query query = beanForQuery(entity);
        long count = 0;
        if (collectionName == null) {
            count = getMongoTemplate().count(query, getEntity(), collectionName);
        } else {
            count = getMongoTemplate().count(query, getEntity(), collectionName);
        }
        return count;
    }

    /**
     * 根据主键修改编号
     *
     * @param id
     * @param entity
     * @return
     * @throws Exception
     */
    public boolean updateById(String id, T entity) throws Exception {

        Assert.notNull(id, "id is not null!");
        Assert.notNull(entity, "entity is not null!");

        Query query = new Query(Criteria.where("id").is(id));
        Update update = new Update();
        //Map<String, String> describe = BeanUtils.describe(entity);
        Map<String, Object> describe = BeanUtil.beanToMap(entity);
        // 遍历map集合
        for (Map.Entry<String, Object> entry : describe.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            if ("class".equals(key) || value == null) {
                continue;
            }

            update.set(key, value);
        }
        //更新查询返回结果集的第一条
        WriteResult writeResult = null;
        if (getCollectionName() == null) {
            writeResult = getMongoTemplate().updateFirst(query, update, getEntity());
        } else {
            writeResult = getMongoTemplate().updateFirst(query, update, getEntity(), getCollectionName());
        }

        if (writeResult != null && writeResult.getN() > 0) {
            return true;
        }
        return false;
    }

    /**
     * 根据主键删除对象
     *
     * @param id
     */
    public void deleteById(String id) {
        Query query = new Query(Criteria.where("id").is(id));
        if (getCollectionName() == null) {
            getMongoTemplate().remove(query, getEntity());
        } else {
            getMongoTemplate().remove(query, getEntity(), getCollectionName());
        }
    }


    /**
     * 根据指定列删除
     *
     * @param column 指定列
     * @param value  值
     */
    public void deleteById(String column, String value) {
        Query query = new Query(Criteria.where(column).is(value));
        if (getCollectionName() == null) {
            getMongoTemplate().remove(query, getEntity());
        } else {
            getMongoTemplate().remove(query, getEntity(), getCollectionName());
        }
    }

    /**
     * 根据对象查询一个集合
     * <p>
     * 等号查询
     *
     * @param entity
     * @return
     * @throws Exception
     */
    public List<T> findByList(T entity) throws Exception {
        Assert.notNull(entity, "entity is not null!");
        Query query = beanForQuery(entity);

        List<T> result = null;
        if (getCollectionName() == null) {
            result = getMongoTemplate().find(query, getEntity());
        } else {
            result = getMongoTemplate().find(query, getEntity(), getCollectionName());
        }
        return result;
    }

    /**
     * 将bean转化成Query
     *
     * @param entity
     * @return
     * @throws Exception
     */
    private Query beanForQuery(T entity) throws Exception {
        Query query = new Query();
        Map<String, String> describe = BeanUtils.describe(entity);

        // 遍历map集合
        for (Map.Entry<String, String> entry : describe.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();

            if ("class".equals(key) || value == null) {
                continue;
            }
            Criteria criteria = Criteria.where(key).is(value);
            query.addCriteria(criteria);
        }
        return query;
    }

}
