//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.baomidou.mybatisplus.extension.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.changfa.constants.ApiResult;
import com.changfa.utils.ColaBeanUtils;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.SqlSessionHolder;
import org.mybatis.spring.SqlSessionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
    protected Log log = LogFactory.getLog(this.getClass());
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    protected M baseMapper;
    protected Class<?> entityClass = this.currentModelClass();

    private Class<?> clazz;

    private Field[] fields;

    protected List<String> excludeColumnList;

    public ServiceImpl() {
        excludeColumnList = new ArrayList<>();
        clazz = (Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
        fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            ExcelProperty exportColumn = fields[i].getDeclaredAnnotation(ExcelProperty.class);
            if(exportColumn == null){
                excludeColumnList.add(fields[i].getName());
            }
        }
        fields =  clazz.getSuperclass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            ExcelProperty exportColumn = fields[i].getDeclaredAnnotation(ExcelProperty.class);
            if(exportColumn == null){
                excludeColumnList.add(fields[i].getName());
            }
        }

    }

    /**
     * 导出excel
     * @param response
     * @return
     */
    @Override
    public ApiResult downloadExcel(Wrapper wrapper,HttpServletResponse response,String fileName)throws Exception {
        List<T> list = baseMapper.selectList(wrapper);
        for(T t:list){
            this.genKeyNameData(t);
        }
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())//自适应表格格式
                .sheet("模板").excludeColumnFiledNames(excludeColumnList).head(clazz)
                .doWrite(list);

        return ApiResult.success("下载成功");
    }

    public T genKeyNameData(T e) {
        //子类根据需要重写此方法
        return e;
    }


    public ApiResult downloadExcel(List<?> list ,HttpServletResponse response,String fileName)throws Exception {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), list.get(0).getClass())
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())//自适应表格格式
                .sheet("模板")
                .doWrite(list);
        return ApiResult.success("下载成功");
    }
    public M getBaseMapper() {
        return this.baseMapper;
    }

    /** @deprecated */
    @Deprecated
    protected boolean retBool(Integer result) {
        return SqlHelper.retBool(result);
    }

    protected Class<?> currentModelClass() {
        return ReflectionKit.getSuperClassGenericType(this.getClass(), 1);
    }

    /** @deprecated */
    @Deprecated
    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(this.entityClass);
    }

    /** @deprecated */
    @Deprecated
    protected void closeSqlSession(SqlSession sqlSession) {
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(this.entityClass));
    }

    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(this.entityClass).getSqlStatement(sqlMethod.getMethod());
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.sqlStatement(SqlMethod.INSERT_ONE);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            sqlSession.insert(sqlStatement, entity);
        });
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean saveOrUpdate(T entity) {
        if (null == entity) {
            return false;
        } else {
            Class<?> cls = entity.getClass();
            TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
            Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!", new Object[0]);
            String keyProperty = tableInfo.getKeyProperty();
            Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!", new Object[0]);
            Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
            return !StringUtils.checkValNull(idVal) && !Objects.isNull(this.getById((Serializable)idVal)) ? this.updateById(entity) : this.save(entity);
        }
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
        Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!", new Object[0]);
        String keyProperty = tableInfo.getKeyProperty();
        Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!", new Object[0]);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            Object idVal = ReflectionKit.getMethodValue(this.entityClass, entity, keyProperty);
            if (!StringUtils.checkValNull(idVal) && !Objects.isNull(this.getById((Serializable)idVal))) {
                ParamMap<T> param = new ParamMap();
                param.put("et", entity);
                sqlSession.update(tableInfo.getSqlStatement(SqlMethod.UPDATE_BY_ID.getMethod()), param);
            } else {
                sqlSession.insert(tableInfo.getSqlStatement(SqlMethod.INSERT_ONE.getMethod()), entity);
            }

        });
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.sqlStatement(SqlMethod.UPDATE_BY_ID);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            ParamMap<T> param = new ParamMap();
            param.put("et", entity);
            sqlSession.update(sqlStatement, param);
        });
    }

    public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
        return throwEx ? this.baseMapper.selectOne(queryWrapper) : SqlHelper.getObject(this.log, this.baseMapper.selectList(queryWrapper));
    }

    public Map<String, Object> getMap(Wrapper<T> queryWrapper) {
        return (Map)SqlHelper.getObject(this.log, this.baseMapper.selectMaps(queryWrapper));
    }

    public <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return SqlHelper.getObject(this.log, this.listObjs(queryWrapper, mapper));
    }

    /** @deprecated */
    @Deprecated
    protected boolean executeBatch(Consumer<SqlSession> consumer) {
        SqlSessionFactory sqlSessionFactory = SqlHelper.sqlSessionFactory(this.entityClass);
        SqlSessionHolder sqlSessionHolder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sqlSessionFactory);
        boolean transaction = TransactionSynchronizationManager.isSynchronizationActive();
        SqlSession sqlSession;
        if (sqlSessionHolder != null) {
            sqlSession = sqlSessionHolder.getSqlSession();
            sqlSession.commit(!transaction);
        }

        sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        if (!transaction) {
            this.log.warn("SqlSession [" + sqlSession + "] was not registered for synchronization because DataSource is not transactional");
        }

        boolean var6;
        try {
            consumer.accept(sqlSession);
            sqlSession.commit(!transaction);
            var6 = true;
        } catch (Throwable var12) {
            sqlSession.rollback();
            Throwable unwrapped = ExceptionUtil.unwrapThrowable(var12);
            if (unwrapped instanceof RuntimeException) {
                MyBatisExceptionTranslator myBatisExceptionTranslator = new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);
                throw (DataAccessException)Objects.requireNonNull(myBatisExceptionTranslator.translateExceptionIfPossible((RuntimeException)unwrapped));
            }

            throw ExceptionUtils.mpe(unwrapped);
        } finally {
            sqlSession.close();
        }

        return var6;
    }

    protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        Assert.isFalse(batchSize < 1, "batchSize must not be less than one", new Object[0]);
        return !CollectionUtils.isEmpty(list) && this.executeBatch((sqlSession) -> {
            int size = list.size();
            int i = 1;

            for(Iterator<E> var6 = list.iterator(); var6.hasNext(); ++i) {
                E element = var6.next();
                consumer.accept(sqlSession, element);
                if (i % batchSize == 0 || i == size) {
                    sqlSession.flushStatements();
                }
            }

        });
    }

    protected <E> boolean executeBatch(Collection<E> list, BiConsumer<SqlSession, E> consumer) {
        return this.executeBatch(list, 1000, consumer);
    }

   /* protected <E> boolean physicsDeleteById(Serializable id){
        return this.baseMapper.deleteBatchIds()
    }*/


    public ApiResult importExcel(MultipartFile multipartFile,boolean isBackMap)throws Exception {
        if(isBackMap){
            List<Map<String,Object>> data = new LinkedList<>();
            EasyExcel.read(multipartFile.getInputStream()).sheet().registerReadListener(new AnalysisEventListener<Map<String,Object>>() {
                @Override
                public void invoke(Map<String,Object> o, AnalysisContext analysisContext) {
                    data.add(o);
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext analysisContext) {

                }
            }).doRead();
            saveBatchByImport(data,isBackMap);
        }else{
            List<T> data = new LinkedList<>();
            EasyExcel.read(multipartFile.getInputStream()).head(clazz).sheet().registerReadListener(new AnalysisEventListener<T>() {
           // EasyExcel.read(multipartFile.getInputStream()).sheet().registerReadListener(new AnalysisEventListener<T>() {
                @Override
                public void invoke(T o, AnalysisContext analysisContext) {
                    data.add(o);
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext analysisContext) {

                }
            }).doRead();
            saveBatchByImport(data,isBackMap);
        }
        return ApiResult.success();
    }



    public void saveBatchByImport(List<?> data,boolean isBackMap){
        if(!org.springframework.util.ObjectUtils.isEmpty(data)){
            if(!isBackMap){
                List<T> entityList= JSON.parseObject(JSON.toJSONString(data), new TypeReference<List<T>>() {});
                this.saveBatch(entityList);
            }

        }
    }
}
