package com.wms.utils.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.wms.entity.app.dto.extend.KanbanEntity;
import com.wms.entity.table.Goods;
import com.wms.entity.table.Kanban;
import com.wms.service.GoodsService;
import com.wms.service.KanbanService;
import com.wms.utils.StringUtils;
import com.wms.utils.excel.vo.KanbanGoodsExcelVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.wms.utils.WmsUtils.generateId;

/**
 * 上传看板监听
 */
public class UploadKanbanListener implements ReadListener<KanbanGoodsExcelVo> {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 每隔5条存储数据库，实际使用中可以1000条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 1000;
    /**
     * 保存数据总数
     */
    private int SAVE_COUNT = 0;
    private List<KanbanGoodsExcelVo> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    private final GoodsService goodsService;// Dbs服务
    private final KanbanService kanbanService;// 看板服务
    private final String uploadUser;// 用户
    private final Map<String, Goods> goodsMap;// 物料清单
    private final Map<String, Kanban> oldKanbanMap;// 看板列表
    public UploadKanbanListener(GoodsService goodsService, Map<String, Goods> goodsMap, KanbanService kanbanService, Map<String, Kanban> oldKanbanMap, String uploadUser) {
        this.goodsService = goodsService;
        this.goodsMap = goodsMap;
        this.kanbanService = kanbanService;
        this.oldKanbanMap = oldKanbanMap;
        this.uploadUser = uploadUser;
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        int rowCount = context.readRowHolder().getRowIndex() + 1;
        logger.error("处理物料数据发生异常，第{}行发生异常。", rowCount);
        throw new Exception("第" + rowCount + "行数据异常。");
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param goodsExcelVo    one row value. It is same as {@link AnalysisContext#readRowHolder()}
     * @param analysisContext context
     */
    @Override
    public void invoke(KanbanGoodsExcelVo goodsExcelVo, AnalysisContext analysisContext) {
        if (goodsMap.containsKey(goodsExcelVo.getGoodsId())
                && StringUtils.isNotEmpty(goodsExcelVo.getGoodsId())) {
            // 符合条件的数据
            cachedDataList.add(goodsExcelVo);
        }
        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            logger.info("导入{}条数据，开始存储数据库！", cachedDataList.size());
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param analysisContext context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        logger.info("导入{}条数据，开始存储数据库！", cachedDataList.size());
        saveData();
        logger.info("此次共保存{}条数据。", SAVE_COUNT);
    }

    /**
     * 存储数据
     */
    private void saveData() {
        // 开始存储数据
        List<Goods> goodsList = new ArrayList<>();
        List<Kanban> newKanbanList = new ArrayList<>();
        for (KanbanGoodsExcelVo goodsExcelVo : cachedDataList) {
            Goods currentGoods = goodsMap.get(goodsExcelVo.getGoodsId());
            // 设置信息
            currentGoods.setHeat(goodsExcelVo.getHeat());// 热度
            currentGoods.setReleasePoint(goodsExcelVo.getReleasePoint());// 卸货点
            currentGoods.setFeedingValue(goodsExcelVo.getFeedingPoint());// 补货点
            currentGoods.setQuantityPerKanban(goodsExcelVo.getKanbanQty());// 每个看板物料数量
            currentGoods.setKanbanNum(goodsExcelVo.getNumberOfKanban());// 看板数量
            // 设置看板
            List<KanbanEntity> kanbanList = goodsExcelVo.convertToKanbanList();
            currentGoods.setKanbanList(kanbanList);
            currentGoods.setLastUpdateTime(LocalDateTime.now());
            currentGoods.setLastUpdateUser(uploadUser);
            goodsList.add(currentGoods);
            // 查询当前料所有的看板
            for (KanbanEntity kanbanEntity : kanbanList) {
                if (!oldKanbanMap.containsKey(currentGoods.getGoodsId() + kanbanEntity.getKanbanId())) {
                    // 添加新看板
                    Kanban newKanban = new Kanban();
                    newKanban.setRecordId(generateId("KANBAN_"));
                    newKanban.setGoodsId(currentGoods.getGoodsId());
                    newKanban.setKanbanId(kanbanEntity.getKanbanId());
                    newKanban.setKanbanStatus(1);// 默认置满
                    newKanban.setLastPullTime(LocalDateTime.now());
                    newKanban.setLastPullUser(uploadUser);
                    newKanbanList.add(newKanban);
                }
            }
        }
        // 保存物料看板信息
        goodsService.saveOrUpdateBatch(goodsList);
        // 保存看板需求信息
        kanbanService.saveOrUpdateBatch(newKanbanList);
        // 打印保存数量
        SAVE_COUNT += goodsList.size();
    }
}
