package com.ag.dynamicmodule.service;

import com.ag.util.CommonUtil;
import com.ag.util.UUIDUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.wb.util.SysUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * @description: 批处理封装 executeBatch,基本维护 save
 * @author: WangNan
 * @create: 2019-04-10 16:20
 */
@Component
public class IBaseServiceImpl implements IBaseService {

    private static final Logger LOGGER = LoggerFactory.getLogger(IBaseServiceImpl.class);
    /**
     * 一般表主键名称
     */
    private final static String PRIMARY_KEY = "id";
    /**
     * mybatis 新增标签
     */
    private final static String MYBATINS_INSERT_TAG = "insertSelective";
    /**
     * mybatis 修改标签
     */
    private final static String MYBATINS_UPDATE_TAG = "updateByPrimaryKeySelective";
    /**
     * mybatis 删除标签
     */
    private final static String MYBATINS_DELETE_TAG = "deleteByPrimaryKey";
    /**
     * 存放属性
     */
    private Map<String, Object> invokeMap;

    @Override
    @Transactional
    public String executeBatch(IBaseRepository baseRepository) {
        SqlSession sqlsession = baseRepository.getSqlSessionTemplate().getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
        Object obj;
        String dataJson = null;
        if (!CollectionUtils.isEmpty(baseRepository.beanList())) {
            try {
                for (int i = 0; i < baseRepository.beanList().size(); i++) {
                    obj = baseRepository.beanList().get(i);
                    //这三个if一点都不冗余，你给我找出第四种类型来？
                    if (IBaseRepository.INSERT.equals(baseRepository.sqlType())) {
                        sqlsession.insert(baseRepository.myBatisNameSpace(), baseRepository.mbtParams(obj, i));
                    }
                    if (IBaseRepository.UPDATE.equals(baseRepository.sqlType())) {
                        sqlsession.update(baseRepository.myBatisNameSpace(), baseRepository.mbtParams(obj, i));
                    }
                    if (IBaseRepository.DELETE.equals(baseRepository.sqlType())) {
                        sqlsession.delete(baseRepository.myBatisNameSpace(), baseRepository.mbtParams(obj, i));
                    }
                    dataJson += JSON.toJSONString(obj) + ",";
                    if (i % 1000 == 0 || i == baseRepository.beanList().size() - 1) {
                        //会回滚
                        sqlsession.commit();
                        sqlsession.clearCache();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                sqlsession.rollback();
                LOGGER.error(e.getMessage());
                throw new RuntimeException(e.getCause());
            } finally {
                sqlsession.close();
            }
        }
        if (dataJson != null) {
            dataJson = dataJson.substring(0, dataJson.length() - 1);
            dataJson = dataJson.substring(4);
        }
        return dataJson;
    }

    @Override
    public Object buildObj(PojoBuilder pojoBuilder) {
        Object obj = null;
        try {
            pojoBuilder.setBaseObj(Class.forName((pojoBuilder.getBaseClass() == null ? pojoBuilder.getBaseObj().getClass() :
                    pojoBuilder.getBaseClass()).getName()).newInstance());
            obj = this.invokeObj(pojoBuilder, Boolean.TRUE);
        } catch (Exception e) {
            LOGGER.error(e.getLocalizedMessage());
        }
        return obj;
    }

    @Override
    public Object initObj(PojoBuilder pojoBuilder) {
        checkNotNull(pojoBuilder.getBaseObj(), "pojoBuilder's baseObj can not be null when initObj....");
        return this.invokeObj(pojoBuilder, Boolean.TRUE);
    }

    @Override
    public Object invokeObj(PojoBuilder pojoBuilder, Boolean isInit) {
        checkNotNull(pojoBuilder.getBaseObj(), "pojoBuilder's baseObj can not be null when invoke....");
        invokeMap = Maps.newConcurrentMap();
        if (isInit) {
            invokeMap.put("id", SysUtil.getId());
        }
        try {
            for (Map.Entry<String, Object> entry : invokeMap.entrySet()) {
                PropertyUtils.setProperty(pojoBuilder.getBaseObj(), entry.getKey(), entry.getValue());
            }
        } catch (Exception e) {
            LOGGER.error(e.getLocalizedMessage());
            throw new RuntimeException("Fail to invokeObj ........ in" + this.getClass().getName());
        }
        return pojoBuilder.getBaseObj();
    }


    @Override
    public void save(BaseMapperRepository baseRepository) {
        this.saveMethod(baseRepository, null);
    }

    @Override
    public void save(BaseMapperRepository baseRepository, String primaryName) {
        this.saveMethod(baseRepository, primaryName);
    }

    @Override
    public Object savewithid(BaseMapperRepository baseRepository) {
        return this.saveMethodwithid(baseRepository, null);
    }

    @Transactional
    Object saveMethodwithid(BaseMapperRepository baseRepository, String primaryName) {
        Object bean = null;
        try {
            if (StringUtils.isEmpty(PropertyUtils.getProperty(baseRepository.getBean(), StringUtils.isEmpty(primaryName) ? PRIMARY_KEY : primaryName))) {
                bean = this.insertByInvokereturnId(baseRepository);
            } else {
                this.updateByInvoke(baseRepository);
                //  bean=null;
            }
            return bean;
        } catch (Exception e) {
            throw new RuntimeException(e.getCause());
        }
    }


    private Object insertByInvokereturnId(BaseMapperRepository baseRepository) {
        this.invokeObj(new PojoBuilder(baseRepository.getBean()), Boolean.TRUE);
        if (ObjectUtils.isEmpty(baseRepository.getMapper())) {
            baseRepository.getSqlSessionTemplate().insert(baseRepository.getMyBatisNameSpace() + "." + MYBATINS_INSERT_TAG,
                    baseRepository.getBean());
        } else {
            this.invokeMethod(baseRepository, MYBATINS_INSERT_TAG);
        }
        return baseRepository.getBean();
    }

    @Override
    public void insert(BaseMapperRepository baseRepository) {
        this.insertByInvoke(baseRepository);
    }

    @Override
    public void insertWithId(BaseMapperRepository baseRepository) {
        this.insertByInvokeWithId(baseRepository);
    }

    @Override
    public void update(BaseMapperRepository baseRepository) {
        this.updateByInvoke(baseRepository);
    }

    @Override
    public void delete(BaseMapperRepository baseRepository) {
        this.deleteByInvoke(baseRepository);
    }

    @Override
    public void deleteBySomeone(BaseMapperRepository baseRepository, String someone) {
        this.deleteByInvokeSomeone(baseRepository, someone);
    }

    @Override
    public Object setParameterValue(Object bean, HttpServletRequest request) {
        try {
            if (StringUtils.isEmpty(BeanUtils.getProperty(bean, "id"))) {
                BeanUtils.setProperty(bean, "createdBy", request.getSession().getAttribute("sys.username"));
                BeanUtils.setProperty(bean, "createdOn", CommonUtil.getNowDate());
                BeanUtils.setProperty(bean, "id", UUIDUtil.getUUID());
            }
            BeanUtils.setProperty(bean, "updatedBy", request.getSession().getAttribute("sys.username"));
            BeanUtils.setProperty(bean, "updatedOn", CommonUtil.getNowDate());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return bean;
    }

    @Transactional
    void saveMethod(BaseMapperRepository baseRepository, String primaryName) {
        try {
            if (StringUtils.isEmpty(PropertyUtils.getProperty(baseRepository.getBean(), StringUtils.isEmpty(primaryName) ? PRIMARY_KEY : primaryName))) {
                this.insertByInvoke(baseRepository);
            } else {
                this.updateByInvoke(baseRepository);
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getCause());
        }
    }

    private void insertByInvoke(BaseMapperRepository baseRepository) {
        this.invokeObj(new PojoBuilder(baseRepository.getBean()), Boolean.TRUE);
        if (ObjectUtils.isEmpty(baseRepository.getMapper())) {
            baseRepository.getSqlSessionTemplate().insert(baseRepository.getMyBatisNameSpace() + "." + MYBATINS_INSERT_TAG,
                    baseRepository.getBean());
        } else {
            this.invokeMethod(baseRepository, MYBATINS_INSERT_TAG);
        }
    }

    private void insertByInvokeWithId(BaseMapperRepository baseRepository) {
        this.invokeObj(new PojoBuilder(baseRepository.getBean()), Boolean.FALSE);
        if (ObjectUtils.isEmpty(baseRepository.getMapper())) {
            baseRepository.getSqlSessionTemplate().insert(baseRepository.getMyBatisNameSpace() + "." + MYBATINS_INSERT_TAG,
                    baseRepository.getBean());
        } else {
            this.invokeMethod(baseRepository, MYBATINS_INSERT_TAG);
        }
    }

    private void updateByInvoke(BaseMapperRepository baseRepository) {
        this.invokeObj(new PojoBuilder(baseRepository.getBean()), Boolean.FALSE);
        if (ObjectUtils.isEmpty(baseRepository.getMapper())) {
            baseRepository.getSqlSessionTemplate().update(baseRepository.getMyBatisNameSpace() + "." + MYBATINS_UPDATE_TAG,
                    baseRepository.getBean());
        } else {
            this.invokeMethod(baseRepository, MYBATINS_UPDATE_TAG);
        }
    }

    private void deleteByInvoke(BaseMapperRepository baseRepository) {
        if (ObjectUtils.isEmpty(baseRepository.getMapper())) {
            baseRepository.getSqlSessionTemplate().delete(baseRepository.getMyBatisNameSpace() + "." + MYBATINS_DELETE_TAG,
                    baseRepository.getBean());
        }
    }

    private void deleteByInvokeSomeone(BaseMapperRepository baseRepository, String someone) {
        if (ObjectUtils.isEmpty(baseRepository.getMapper())) {
            baseRepository.getSqlSessionTemplate().delete(baseRepository.getMyBatisNameSpace() + "." + someone,
                    baseRepository.getBean());
        }
    }


    private void invokeMethod(BaseMapperRepository baseRepository, String methodName) {
        Method m = ReflectionUtils.findMethod(baseRepository.getMapper().getClass(),
                methodName, baseRepository.getBean().getClass());
        ReflectionUtils.invokeMethod(m, baseRepository.getMapper(), baseRepository.getBean());
    }
}