/* Copyright (c) 2018, yiboshi.com All Rights Reserved. */
package com.yiboshi.science.base;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yiboshi.arch.exception.BusinessException;
import com.yiboshi.arch.exception.SystemException;
import com.yiboshi.science.BizConstant;
import com.yiboshi.science.utils.StringUtil;
import org.apache.commons.beanutils.BeanMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;


/**
 * Service基类实现
 *
 * @author NEGI
 * @version 2018-11
 */
@SuppressWarnings("unchecked")
public abstract class BaseServiceImpl<Dao extends BaseMapper<Entity>, QueryVO extends PaginationVO, DTO extends BaseDTO, Entity extends BaseEntity> extends ServiceImpl<Dao, Entity> implements BaseService<QueryVO, DTO, Entity> {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    private StringUtil stringUtil;

    private enum Operation {
        INSERT,
        UPDATE,
        SAVE
    }

    // 设置查询条件criteria
    protected abstract void setCriteriaForQuery(QueryVO vo, QueryWrapper<QueryVO> criteria);

    // 设置排序条件criteria
    protected void setCriteriaOrder(QueryWrapper<Entity> wrapper) {

    }

    public void notNullField(Entity e, QueryWrapper<Entity> wrapper) {
        for (Field field : e.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                //序列化 字段不需要查询
                if ("serialVersionUID".equals(field.getName())) {
                    continue;
                }
                //属性为空，不用查询
                if (field.get(e) == null) {
                    continue;
                }
                //主键 注解TableId
                TableId tableId = field.getAnnotation(TableId.class);
                if (tableId != null) {
                    //主键
                    wrapper.eq(tableId.value(), field.get(e));
                    continue;
                }
                //数据库中字段名和实体类属性不一致 注解TableField
                TableField tableField = field.getAnnotation(TableField.class);
                if (tableField != null) {
                    if (tableField.exist()) {
                        wrapper.eq(tableField.value(), field.get(e));
                    }// @TableField(exist = false) 不是表中内容 不形成查询条件
                    continue;
                }
                //数据库中字段名和实体类属性一致
                wrapper.eq(stringUtil.toLine(field.getName()), field.get(e));
            } catch (IllegalAccessException ex) {
                ex.printStackTrace();
            }
        }
    }

    @Override
    public String insert(Entity e) {
        e.setId(String.valueOf(UUID.randomUUID()));
        e.setCreated(new Date());
        this.save(e);
        return e.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insertBatch(List<Entity> dataList) {
        List<Entity> entityList = dataList;
        for (int i = 0; i < entityList.size(); i++) {
            this.insert(entityList.get(i));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertOrUpdateBatch(List<Entity> dataList) {
        List<Entity> insertList = new ArrayList<>(dataList.size());
        List<Entity> updateList = new ArrayList<>(dataList.size());
        for (Entity vo : dataList) {
            Entity entity = newInstanceEntity();
            BeanUtils.copyProperties(vo, entity);
            if (Objects.isNull(entity.getId())) {
                insertList.add(entity);
            } else {
                updateList.add(entity);
            }
        }
        if (insertList.size() > 0) {
            this.saveBatch(insertList);
        }
        if (updateList.size() > 0) {
            this.updateBatchById(updateList);
        }
    }

    @Override
    public String update(Entity e) {
        return updateSelective(e);
    }


    @Override
    public String updateSelective(Entity e) {
        if (e.getId() == null) {
            throw new BusinessException("id不能为空");
        }
        e.setCreated(null);
        e.setUpdated(new Date());
        this.updateById(e);
        return e.getId();
    }

    @Override
    public void deleteByIds(List<String> idList) {
        this.removeByIds(idList);
    }

    @Override
    public boolean delete(Entity e) {
        QueryWrapper wrapper = new QueryWrapper();
        notNullField(e, wrapper);
        return this.remove(wrapper);
    }

    @Override
    public void deleteById(String id) {
        if (BizConstant.REAL_DELETE) {
            this.removeById(id);
        } else {
            throw new BusinessException("系统已禁止物理删除！");
        }
    }

    @Override
    public void updateBatch(List<Entity> dataList) {
        List<Entity> entityList = dataList;
//      convertVo2EntityList(dataList);
        this.updateBatchById(entityList);
    }


    @Override
    public Entity entityById(String id) {
        if (id == null) {
            throw new BusinessException("id不能为空");
        }
        Entity entity = this.getById(id);
        return entity == null ? null : entity;
    }

    @Override
    public Entity getEntity(Entity e) {
        QueryWrapper wrapper = new QueryWrapper();
        notNullField(e, wrapper);
        return (Entity) this.getOne(wrapper);
    }

    @Override
    public List<Entity> entityList(Entity e) {
        QueryWrapper criteria = new QueryWrapper();
        notNullField(e, criteria);
        setCriteriaOrder(criteria);
        long total = this.count(criteria);
        checkQueryLimit(total);
        return this.list(criteria);
    }

    @Override
    public List<Entity> entityAll() {
        List<Entity> entityList = this.list();
        return entityList;
    }


    @Override
    public DTO dtoById(String id) {
        Entity entity = this.getById(id);
        return entity == null ? null : convert2DTO(entity);
    }


    @Override
    public DTO getDto(Entity e) {
        e = getEntity(e);
        return e == null ? null : convert2DTO(e);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<DTO> dtoList(Entity e) {
        QueryWrapper criteria = new QueryWrapper();
        notNullField(e, criteria);
        setCriteriaOrder(criteria);
        long total = this.count(criteria);
        checkQueryLimit(total);
        return convertEntity2DtoList(this.list(criteria));
    }

    @Override
    public QueryVO queryById(String id) {
        Entity entity = this.getById(id);
        return entity == null ? null : convert2QueryVO(entity);
    }

    @Override
    public QueryVO getQuery(Entity e) {
        e = getEntity(e);
        return e == null ? null : convert2QueryVO(e);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<QueryVO> queryList(Entity e) {
        QueryWrapper criteria = new QueryWrapper();
        notNullField(e, criteria);
        setCriteriaOrder(criteria);
        long total = this.count(criteria);
        checkQueryLimit(total);
        return convertEntity2QueryVoList(this.list(criteria));
    }

    @Override
    public List<QueryVO> queryAll() {
        long total = this.count();
        checkQueryLimit(total);
        List<Entity> entityList = this.list();
        List<QueryVO> dtoList = new ArrayList<>();
        for (Entity entity : entityList) {
            QueryVO newQueryVO = newInstanceVo();
            BeanUtils.copyProperties(entity, newQueryVO);
            dtoList.add(newQueryVO);
        }
        return dtoList;
    }

    @Override
    public long count(QueryVO vo) {
        QueryWrapper criteria = new QueryWrapper();
        setCriteriaForQuery(vo, criteria);
        return this.count(criteria);
    }

    public Pagination<DTO> getListByPage(QueryVO vo) {
        QueryWrapper criteria = new QueryWrapper();
        setCriteriaForQuery(vo, criteria);
        long total = this.count(criteria);
        IPage<Entity> page = new Page<>(vo.getPageIndex(), vo.getPageSize(), total, false);
        List<DTO> dtoList = dtoList(page, criteria);
        return new Pagination<>(dtoList, page.getTotal(), vo.getPageSize());
    }

    private List<DTO> dtoList(IPage<Entity> page, QueryWrapper<Entity> criteria) {
        List<Entity> entityList = this.page(page, criteria).getRecords();
        return convertEntity2DtoList(entityList);
    }

    private List<QueryVO> queryList(IPage<Entity> page, QueryWrapper<Entity> criteria) {
        List<Entity> entityList = this.page(page, criteria).getRecords();
        return convertEntity2QueryVoList(entityList);
    }

    public boolean isObjectNull(DTO d) {
        boolean state = true;
        for (Field field : d.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                //属性为空，不用查询
                if (field.get(d) != null) {
                    state = false;
                    break;
                }
            } catch (Exception ex) {
                state = true;
            }
        }
        return state;
    }

    /**
     * 根据id查询数据集合
     *
     * @param ids
     * @return
     */
    public List<Entity> entityListByIds(List<Long> ids) {
        List<Entity> list = (List<Entity>) this.listByIds(ids);
        return list;
    }

    /**
     * 根据id查询数据集合
     *
     * @param ids
     * @return
     */
    public List<QueryVO> queryListByIds(List<Long> ids) {
        List<Entity> list = (List<Entity>) this.listByIds(ids);
        return convertEntity2QueryVoList(list);
    }

    // entity集合转QueryVo集合
    protected List<QueryVO> convertEntity2QueryVoList(List<Entity> entityList) {
        List<QueryVO> dtoList = new ArrayList<>(entityList.size());
        for (Entity entity : entityList) {
            QueryVO newVO = newInstanceVo();
            BeanUtils.copyProperties(entity, newVO);
            dtoList.add(newVO);
        }
        return dtoList;
    }

    // QueryVo集合转Entity集合
    protected List<Entity> convertVo2EntityList(List<QueryVO> dataList) {
        List<Entity> entityList = new ArrayList<>(dataList.size());
        for (QueryVO vo : dataList) {
            Entity entity = newInstanceEntity();
            BeanUtils.copyProperties(vo, entity);
            entityList.add(entity);
        }
        return entityList;
    }


    //entity集合转dto集合
    protected List<DTO> convertEntity2DtoList(List<Entity> entityList) {
        List<DTO> dtoList = new ArrayList<>(entityList.size());
        for (Entity entity : entityList) {
            DTO newDto = newInstanceDTO();
            BeanUtils.copyProperties(entity, newDto);
            dtoList.add(newDto);
        }
        return dtoList;
    }


    //dto集合转entity集合
    protected List<Entity> convertDto2EntityList(List<DTO> dtoList) {
        List<Entity> entityList = new ArrayList<>(dtoList.size());
        for (DTO entity : dtoList) {
            Entity newDto = newInstanceEntity();
            BeanUtils.copyProperties(entity, newDto);
            entityList.add(newDto);
        }
        return entityList;
    }


    private QueryVO newInstanceVo() {
        Class<QueryVO> entityClass = (Class<QueryVO>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
        try {
            return entityClass.newInstance();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new SystemException(e);
        }
    }

    private Entity newInstanceEntity() {
        Class<Entity> entityClass = (Class<Entity>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[3];
        try {
            return entityClass.newInstance();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new SystemException(e);
        }
    }

    private DTO newInstanceDTO() {
        Class<DTO> entityClass = (Class<DTO>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[2];
        try {
            return entityClass.newInstance();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new SystemException(e);
        }
    }

    //vo转entity
    protected Entity convert2Entity(QueryVO vo) {
        try {
            Entity entity = newInstanceEntity();
            BeanUtils.copyProperties(vo, entity);
            return entity;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new SystemException(e);
        }
    }


    //entity转QueryVO
    protected QueryVO convert2QueryVO(Entity e) {
        try {
            QueryVO dto = newInstanceVo();
            BeanUtils.copyProperties(e, dto);
            return dto;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            throw new SystemException(ex);
        }
    }

    //entity转dto
    protected DTO convert2DTO(Entity e) {
        try {
            DTO dto = newInstanceDTO();
            BeanUtils.copyProperties(e, dto);
            return dto;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            throw new SystemException(ex);
        }
    }

    //dto转Entity
    protected Entity convert2Entity(DTO dto) {
        try {
            Entity entity = newInstanceEntity();
            BeanUtils.copyProperties(dto, entity);
            return entity;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new SystemException(e);
        }
    }

    /**
     * 查询限制校验
     */
    private void checkQueryLimit(long total) {
        if (total > BizConstant.QUERY_LIMIT) {
            throw new BusinessException("查询数据量" + total + "已超过系统限制" + BizConstant.QUERY_LIMIT);
        }
    }

    /**
     * 对象转Map集合
     *
     * @param obj         查询对象
     * @param allowAddKey 返回的map对象是否允许添加新的key
     */
    protected Map beanToMap(QueryVO obj, boolean allowAddKey) {
        if (obj == null) {
            return null;
        }
        Map beanMap = new BeanMap(obj);
        return allowAddKey ? new HashMap() {{
            putAll(beanMap);
        }} : beanMap;
    }

}