package com.zt.plat.module.qms.resource.material.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.bpm.api.task.BpmProcessInstanceApi;
import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceRespDTO;
import com.zt.plat.module.bpm.api.task.dto.UserSimpleDTO;
import com.zt.plat.module.qms.core.code.SequenceUtil;
import com.zt.plat.module.qms.core.constant.DataTypeConstant;
import com.zt.plat.module.qms.enums.AdjustType;
import com.zt.plat.module.qms.enums.LockType;
import com.zt.plat.module.qms.office.supplier.dal.dataobject.SupplierDO;
import com.zt.plat.module.qms.office.supplier.service.SupplierService;
import com.zt.plat.module.qms.resource.material.constant.MaterialConstants;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialBatchPageReqVO;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialBatchRespVO;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialBatchSaveReqVO;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialProductRespVO;
import com.zt.plat.module.qms.resource.material.controller.vo.importer.MaterialBatchImportExcelVO;
import com.zt.plat.module.qms.resource.material.controller.vo.query.MaterialProductQueryVO;
import com.zt.plat.module.qms.resource.material.controller.vo.resp.MaterialBatchImportRespVO;
import com.zt.plat.module.qms.resource.material.dal.dataobject.MaterialBatchDO;
import com.zt.plat.module.qms.resource.material.dal.dataobject.MaterialLifecycleDO;
import com.zt.plat.module.qms.resource.material.dal.dataobject.MaterialLifecycleDetailDO;
import com.zt.plat.module.qms.resource.material.dal.dataobject.MaterialProductDO;
import com.zt.plat.module.qms.resource.material.dal.mapper.MaterialBatchMapper;
import com.zt.plat.module.qms.resource.material.enums.MaterialAcceptStatus;
import com.zt.plat.module.qms.resource.material.enums.MaterialBatchGongType;
import com.zt.plat.module.qms.resource.material.enums.MaterialFlowType;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.qms.enums.ErrorCodeConstants.*;

/**
 * 物料批次 Service 实现类
 *
 * @author 后台管理
 */
@Slf4j
@Service
@Validated
public class MaterialBatchServiceImpl implements MaterialBatchService {

    @Resource
    private MaterialBatchMapper materialBatchMapper;
    @Autowired
    private SequenceUtil sequenceUtil;

    @Autowired
    private MaterialProductService materialProductService;
    @Autowired
    private SupplierService supplierService;

    @Override
    public MaterialBatchRespVO createMaterialBatch(MaterialBatchSaveReqVO createReqVO) {
        // 插入
        MaterialBatchDO mtrlBat = BeanUtils.toBean(createReqVO, MaterialBatchDO.class);
        mtrlBat.setAcceptanceStatus(MaterialAcceptStatus.not_accepted.name());
        // 批次编号
        mtrlBat.setBatchNo(sequenceUtil.genCode(MaterialConstants.SEQUENCE_BATCH_KEY));

        mtrlBat.setParentId(0L);
        mtrlBat.setRemaineQuantity(mtrlBat.getInboundQuantity());
        materialBatchMapper.insert(mtrlBat);

        // 返回
        return BeanUtils.toBean(mtrlBat, MaterialBatchRespVO.class);
    }

    @Override
    public void updateMaterialBatch(MaterialBatchSaveReqVO updateReqVO) {
        // 校验存在
        Long reqId = updateReqVO.getId();
        MaterialBatchDO batchDO = materialBatchMapper.selectById(reqId);
        if (batchDO == null) throw exception(MATERIAL_BATCH_NOT_EXISTS);
        // 已经提交的不可修改
        if (batchDO.getSubmitStatus() == 1) throw new ServiceException(1_032_160_000, "批次已经提交，不可修改");

        // 更新
        MaterialBatchDO updateObj = BeanUtils.toBean(updateReqVO, MaterialBatchDO.class);
        updateObj.setRemaineQuantity(updateObj.getInboundQuantity());
        materialBatchMapper.updateById(updateObj);
    }

    @Override
    public void deleteMaterialBatch(Long id) {

        MaterialBatchDO batchDO = materialBatchMapper.selectById(id);
        if (batchDO == null) throw exception(MATERIAL_BATCH_NOT_EXISTS);
        if (batchDO.getSubmitStatus() == 1)
            throw new ServiceException(1_032_160_000, "批次已经提交，不可删除");
        // 删除拆分数据
        deleteAssignByBatIds(Collections.singletonList(id));
        // 删除
        materialBatchMapper.deleteById(id);
    }

    private void deleteAssignByBatIds(List<Long> ids) {
        materialBatchMapper.delete(Wrappers.lambdaQuery(MaterialBatchDO.class)
                .in(MaterialBatchDO::getParentId, ids));
    }

    @Transactional
    @Override
        public void deleteMaterialBatchListByIds(List<Long> ids) {
        // 校验存在
        List<MaterialBatchDO> list = materialBatchMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_BATCH_NOT_EXISTS);
        }
        // 存在已经提交的批次时，不可删除
        for (MaterialBatchDO batch : list) {
            if (batch.getSubmitStatus() == 1)
                throw new ServiceException(1_032_160_000, "存在已经提交的批次，不可删除");
        }
        deleteAssignByBatIds(ids);
        // 删除
        materialBatchMapper.deleteByIds(ids);
        }

    private void validateMaterialBatchExists(List<Long> ids) {
        List<MaterialBatchDO> list = materialBatchMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_BATCH_NOT_EXISTS);
        }
    }

    private void validateMaterialBatchExists(Long id) {
        if (materialBatchMapper.selectById(id) == null) {
            throw exception(MATERIAL_BATCH_NOT_EXISTS);
        }
    }

    @Override
    public MaterialBatchDO getMaterialBatch(Long id) {
        return materialBatchMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialBatchDO> getMaterialBatchPage(MaterialBatchPageReqVO pageReqVO) {
        return materialBatchMapper.selectPage(pageReqVO);
    }

    /**
     * 批次工段拆分
     * 
     */
    @Transactional
    @Override
    public List<MaterialBatchRespVO> assignMaterialBatchGongduan(List<MaterialBatchSaveReqVO> createReqVOs) {
        // 是否已经拆分过
        Long batId = createReqVOs.get(0).getParentId();

        // 1. 所属的批次需要是同一个
        Set<Long> pIds = createReqVOs.stream().map(MaterialBatchSaveReqVO::getParentId).collect(Collectors.toSet());
        if (pIds.size() > 1) throw exception(GONGDUAN_BELONG_MATERIAL_BATCH_NOT_EQUAL);

        // 2. 拆分后的数量要相等
        MaterialBatchDO mtrlBat = materialBatchMapper.selectById(batId);
        if (mtrlBat == null) throw exception(MATERIAL_BATCH_NOT_EXISTS);
        if (mtrlBat.getSubmitStatus() == 1) throw new ServiceException(1_032_160_000, "批次已经提交，不可修改");
        BigDecimal total = BigDecimal.valueOf(0);
        for (MaterialBatchSaveReqVO batAsn : createReqVOs) {
            total = total.add(batAsn.getInboundQuantity());
        }
        if (total.compareTo(mtrlBat.getRemaineQuantity()) != 0)
            throw exception(GONGDUAN_QUANTITY_MATERIAL_BATCH_AVAILABLE_QUANTITY_NOT_EQUAL);
        // 修改工段
        boolean exists = materialBatchMapper.exists(Wrappers.lambdaQuery(MaterialBatchDO.class)
                .eq(MaterialBatchDO::getParentId, batId));
        if (exists) {
            // 删除之前的拆分数据
            deleteAssignByBatIds(Collections.singletonList(batId));
        }

        // 3. 保存工段
        List<MaterialBatchDO> gongEts = createReqVOs.stream().map(batAsn -> {
            MaterialBatchDO bean = BeanUtils.toBean(batAsn, MaterialBatchDO.class);
            bean.setProductId(mtrlBat.getProductId()).setBatchNo(mtrlBat.getBatchNo())
                    .setRemaineQuantity(bean.getInboundQuantity())
                    .setAcceptanceStatus(MaterialAcceptStatus.not_accepted.name());
            return bean;
        }).toList();
        materialBatchMapper.insertBatch(gongEts);

        return gongEts.stream().map(
                gong -> BeanUtils.toBean(gong, MaterialBatchRespVO.class)
        ).toList();
    }


    @Override
    public List<MaterialBatchDO> getGongduanListByBatIds(List<Long> batIds) {

        return materialBatchMapper.selectList(Wrappers.lambdaQuery(MaterialBatchDO.class)
                .in(MaterialBatchDO::getParentId, batIds)
                .ne(MaterialBatchDO::getParentId, 0));
    }

    @Transactional
    @Override
    public Boolean submitMaterialBatch(Long id) {
        MaterialBatchDO batchDO = materialBatchMapper.selectById(id);
        if (batchDO == null) throw exception(MATERIAL_BATCH_NOT_EXISTS);
        if (batchDO.getSubmitStatus() == 1) throw new ServiceException(1_032_160_000, "批次已经提交过");
        // 检查是否属于检定批次
        Boolean isVerify = materialProductService.checkIsVerifyCategoryByPdtId(batchDO.getProductId());
        if (!isVerify) {

            List<MaterialBatchDO> gongduans = this.getGongduanListByBatIds(Collections.singletonList(id));
            if (CollUtil.isEmpty(gongduans)) throw new ServiceException(1_032_160_000, "批次还未拆分，不可提交");
            // 批次存在锁定数量时不可提交
            if (batchDO.getLockQuantity().compareTo(BigDecimal.ZERO) > 0)
                throw new ServiceException(1_032_160_000, String.format("批次存在锁定数量【%s】，不可提交", batchDO.getLockQuantity()));
            // 批次可用数量可能存在变更，需要再次校验数量一致性
            BigDecimal gongTotalInbQuantity = BigDecimal.ZERO;
            for (MaterialBatchDO gongduan : gongduans) {
                gongTotalInbQuantity = gongTotalInbQuantity.add(gongduan.getInboundQuantity());
            }
            if (batchDO.getRemaineQuantity().compareTo(gongTotalInbQuantity) != 0)
                throw exception(GONGDUAN_QUANTITY_MATERIAL_BATCH_AVAILABLE_QUANTITY_NOT_EQUAL);

        }

        batchDO.setSubmitStatus(1).setSubmitDate(LocalDateTime.now());
        materialBatchMapper.updateById(batchDO);
        // 提交工段
        MaterialBatchDO updateBatch = new MaterialBatchDO().setSubmitStatus(1).setSubmitDate(LocalDateTime.now());
        materialBatchMapper.update(updateBatch, Wrappers.lambdaQuery(MaterialBatchDO.class)
                .eq(MaterialBatchDO::getParentId, id));
        return true;
    }

    @Override
    public List<MaterialBatchDO> getGongduanListByGongIds(List<Long> gongIds) {

        return materialBatchMapper.selectList(Wrappers.lambdaQuery(MaterialBatchDO.class)
                .in(MaterialBatchDO::getId, gongIds)
                .ne(MaterialBatchDO::getParentId, 0));
    }

    @Override
    public void updateById(MaterialBatchDO gongDO) {
        materialBatchMapper.updateById(gongDO);
    }

    @Override
    public List<MaterialBatchDO> getBatchListByBatchIds(List<Long> batchIds) {

        return materialBatchMapper.selectList(Wrappers.lambdaQuery(MaterialBatchDO.class)
                .in(MaterialBatchDO::getId, batchIds)
                .eq(MaterialBatchDO::getParentId, 0));
    }

    @Override
    public void updateBatchReturnExchangeCount(List<MaterialLifecycleDetailDO> batches, AdjustType adjustType) {
        List<Long> batIds = batches.stream().map(MaterialLifecycleDetailDO::getBatchId).toList();
        List<MaterialBatchDO> batchDOS = getBatchListByBatchIds(batIds);
        Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByBatchId = batches.stream().collect(Collectors.toMap(MaterialLifecycleDetailDO::getBatchId, Function.identity()));
        adjustBatchOrGongReturnExchangeCount(adjustType, batchDOS, lifecycleDetailDOMapByBatchId);
        materialBatchMapper.updateBatch(batchDOS);
    }

    @Override
    public void updateGongduanReturnExchangeCount(List<MaterialLifecycleDetailDO> gongs, AdjustType adjustType) {
        List<Long> gongIds = gongs.stream().map(MaterialLifecycleDetailDO::getBatchGongduanId).toList();
        List<MaterialBatchDO> gongDOS = getGongduanListByGongIds(gongIds);
        Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByGongId = gongs.stream().collect(Collectors.toMap(MaterialLifecycleDetailDO::getBatchGongduanId, Function.identity()));
        // 调整工段退换货数量
        adjustBatchOrGongReturnExchangeCount(adjustType, gongDOS, lifecycleDetailDOMapByGongId);
        materialBatchMapper.updateBatch(gongDOS);
        // 调整批次的退换货数量
        List<Long> batIds = gongDOS.stream().map(MaterialBatchDO::getParentId).toList();
        List<MaterialBatchDO> batDOS = getBatchListByBatchIds(batIds);
        if (CollUtil.isEmpty(batDOS)) return;
        Map<Long, Long> batIdMapByGongId = gongDOS.stream().collect(Collectors.toMap(MaterialBatchDO::getId, MaterialBatchDO::getParentId));
        for (MaterialLifecycleDetailDO gong : gongs) {
            gong.setBatchId(batIdMapByGongId.get(gong.getBatchGongduanId()));
        }
        Map<Long, List<MaterialLifecycleDetailDO>> detailGroupByBatId = gongs.stream().collect(Collectors.groupingBy(MaterialLifecycleDetailDO::getBatchId));
        List<MaterialLifecycleDetailDO> batches = batIds.stream().map(batId -> {
            List<MaterialLifecycleDetailDO> detailDOS = detailGroupByBatId.get(batId);
            BigDecimal influenceCount = detailDOS.stream().map(MaterialLifecycleDetailDO::getInfluenceCount).reduce(BigDecimal.ZERO, BigDecimal::add);
            return new MaterialLifecycleDetailDO().setBatchId(batId).setInfluenceCount(influenceCount);
        }).toList();
        Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByBatId = batches.stream().collect(Collectors.toMap(MaterialLifecycleDetailDO::getBatchId, Function.identity()));
        adjustBatchOrGongReturnExchangeCount(adjustType, batDOS, lifecycleDetailDOMapByBatId);
        materialBatchMapper.updateBatch(batDOS);

    }

    @Override
    public List<MaterialBatchDO> getListByIds(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) return List.of();
        return materialBatchMapper.selectByIds(ids);
    }

    @Override
    public void lockBatchReturnExchangeCount(List<MaterialLifecycleDetailDO> batches, LockType lockType) {
        List<Long> batIds = batches.stream().map(MaterialLifecycleDetailDO::getBatchId).toList();
        List<MaterialBatchDO> batchDOS = getBatchListByBatchIds(batIds);
        Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByBatchId = batches.stream().collect(Collectors.toMap(MaterialLifecycleDetailDO::getBatchId, Function.identity()));
        lockBatchOrGongReturnExchangeCount(lockType, batchDOS, lifecycleDetailDOMapByBatchId);
        materialBatchMapper.updateBatch(batchDOS);
    }

    @Override
    public void lockGongduanReturnExchangeCount(List<MaterialLifecycleDetailDO> gongs, LockType lockType) {
        List<Long> gongIds = gongs.stream().map(MaterialLifecycleDetailDO::getBatchGongduanId).toList();
        List<MaterialBatchDO> gongDOS = getGongduanListByGongIds(gongIds);
        Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByGongId = gongs.stream().collect(Collectors.toMap(MaterialLifecycleDetailDO::getBatchGongduanId, Function.identity()));
        // 调整工段锁定数量
        lockBatchOrGongReturnExchangeCount(lockType, gongDOS, lifecycleDetailDOMapByGongId);
        materialBatchMapper.updateBatch(gongDOS);
        // 调整批次锁定数量
        List<Long> batIds = gongDOS.stream().map(MaterialBatchDO::getParentId).toList();
        List<MaterialBatchDO> batDOS = getBatchListByBatchIds(batIds);
        if (CollUtil.isEmpty(batDOS)) return;
        Map<Long, Long> batIdMapByGongId = gongDOS.stream().collect(Collectors.toMap(MaterialBatchDO::getId, MaterialBatchDO::getParentId));
        for (MaterialLifecycleDetailDO gong : gongs) {
            gong.setBatchId(batIdMapByGongId.get(gong.getBatchGongduanId()));
        }
        Map<Long, List<MaterialLifecycleDetailDO>> detailGroupByBatId = gongs.stream().collect(Collectors.groupingBy(MaterialLifecycleDetailDO::getBatchId));
        List<MaterialLifecycleDetailDO> batches = batIds.stream().map(batId -> {
            List<MaterialLifecycleDetailDO> detailDOS = detailGroupByBatId.get(batId);
            BigDecimal influenceCount = detailDOS.stream().map(MaterialLifecycleDetailDO::getInfluenceCount).reduce(BigDecimal.ZERO, BigDecimal::add);
            return new MaterialLifecycleDetailDO().setBatchId(batId).setInfluenceCount(influenceCount);
        }).toList();
        Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByBatId = batches.stream().collect(Collectors.toMap(MaterialLifecycleDetailDO::getBatchId, Function.identity()));
        lockBatchOrGongReturnExchangeCount(lockType, batDOS, lifecycleDetailDOMapByBatId);
        materialBatchMapper.updateBatch(batDOS);
    }

    @Override
    public PageResult<MaterialBatchRespVO> getBatchGongPageWithPdtInfo(MaterialBatchPageReqVO pageReqVO) {
        // 分为 批次分页、验收、入库、退换货
        Long pdtId = pageReqVO.getProductId();
        PageResult<MaterialBatchRespVO> pageResult = new PageResult<>();

        String categoryCustomConfig = pageReqVO.getCategoryCustomConfig();
        if (pdtId != null){
            List<MaterialProductDO> mtrlDos = materialProductService.getMaterialProductsByLikeIdPath(pdtId);
            if (CollUtil.isEmpty(mtrlDos)) {
                pageResult = materialBatchMapper.selectPageWithPdtInfo(pageReqVO, List.of());
            } else {
                List<Long> pdtIds = mtrlDos.stream().map(MaterialProductDO::getId).toList();
                pageResult = materialBatchMapper.selectPageWithPdtInfo(pageReqVO, pdtIds);
            }
        } else if (StrUtil.isNotEmpty(categoryCustomConfig)) {
            List<MaterialProductRespVO> categoryAndData = materialProductService.getCategoryAndData(new MaterialProductQueryVO().setCategoryCustomConfig(categoryCustomConfig));
            if (CollUtil.isNotEmpty(categoryAndData)) {
                List<Long> pdtIds = categoryAndData.stream()
                        .filter(p -> DataTypeConstant.DATA_TYPE_DATA.equals(p.getNodeType()))
                        .map(MaterialProductRespVO::getId).toList();
                pageResult = materialBatchMapper.selectPageWithPdtInfo(pageReqVO, pdtIds);
            }
        } else {
            pageResult = materialBatchMapper.selectPageWithPdtInfo(pageReqVO, List.of());
        }
        // 给批次设置顶级分类的json配置
        if (StrUtil.isEmpty(pageReqVO.getDataType()) || MaterialBatchGongType.batch.name().equals(pageReqVO.getDataType())) {
            List<MaterialBatchRespVO> batches = pageResult.getList();
            if (CollUtil.isNotEmpty(batches)) {
                List<Long> pdtIds = batches.stream().map(MaterialBatchRespVO::getProductId).toList();
                List<MaterialProductDO> pdts = materialProductService.getListByIds(pdtIds);
                Map<Long, MaterialProductDO> productDOMapById = pdts.stream().collect(Collectors.toMap(MaterialProductDO::getId, Function.identity()));
                List<MaterialProductDO> topCategories = materialProductService.getTopCategoriesByPdtIds(pdtIds);
                batches.forEach(batch -> {
                    Long productId = batch.getProductId();
                    MaterialProductDO productDO = productDOMapById.get(productId);
                    for (MaterialProductDO topCategory : topCategories) {
                        if (productDO.getIdPath().contains("/" + topCategory.getId() + "/")) {
                            batch.setCategoryCustomConfig(JSONUtil.parseObj(topCategory.getCustomConfig()));
                        }
                    }

                });
                pageResult.setList(batches);
            }
        }

        // 查全部
        if (StrUtil.isEmpty(pageReqVO.getDataType())) {
            List<MaterialBatchRespVO> respVOS = pageResult.getList();
            if (CollUtil.isNotEmpty(respVOS)) {
                List<Long> batIds = respVOS.stream().map(MaterialBatchRespVO::getId).toList();
                List<MaterialBatchDO> gongs = materialBatchMapper.selectList(Wrappers.lambdaQuery(MaterialBatchDO.class)
                        .in(MaterialBatchDO::getParentId, batIds));
                if (CollUtil.isNotEmpty(gongs)) {
                    List<MaterialBatchRespVO> gongRespVOs = gongs.stream().map(gong -> BeanUtils.toBean(gong, MaterialBatchRespVO.class)).toList();
                    respVOS.addAll(gongRespVOs);
                    pageResult.setList(respVOS);
                }
            }
        }

        // 需要组装children
        if (!MaterialBatchGongType.gong.name().equals(pageReqVO.getDataType()) && pageReqVO.getChildren()) {
            List<MaterialBatchRespVO> voList = pageResult.getList();
            if (CollUtil.isNotEmpty(voList)) {
                List<MaterialBatchRespVO> treeVos = this.listTransTree(voList, 0L);
                pageResult.setList(treeVos);
            }
        }

        return pageResult;
    }

    @Override
    public void lockBatchVerifyCalibrateCount(List<MaterialLifecycleDetailDO> batches, LockType lockType) {
        List<Long> batIds = batches.stream().map(MaterialLifecycleDetailDO::getBatchId).toList();
        List<MaterialBatchDO> batchDOS = getBatchListByBatchIds(batIds);
        Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByBatchId = batches.stream().collect(Collectors.toMap(MaterialLifecycleDetailDO::getBatchId, Function.identity()));
        for (MaterialBatchDO batchDO : batchDOS) {
            MaterialLifecycleDetailDO detailDO = lifecycleDetailDOMapByBatchId.get(batchDO.getId());
            BigDecimal influenceCount = detailDO.getInfluenceCount();
            switch (lockType) {
                case lock -> {
                    BigDecimal lockQuantity = batchDO.getLockQuantity().add(influenceCount);
                    if (lockQuantity.compareTo(batchDO.getRemaineQuantity()) > 0)
                        throw new ServiceException(1_032_160_000, "检定数量大于可用数量");
                    batchDO.setLockQuantity(lockQuantity);
                }
                case unlock -> batchDO.setLockQuantity(batchDO.getLockQuantity().subtract(influenceCount));
            }
        }
        materialBatchMapper.updateBatch(batchDOS);
    }

    @Override
    public void updateBatchVerifyCalibrateCount(List<MaterialLifecycleDetailDO> batches, AdjustType adjustType) {
        List<Long> batIds = batches.stream().map(MaterialLifecycleDetailDO::getBatchId).toList();
        List<MaterialBatchDO> batchDOS = getBatchListByBatchIds(batIds);
        Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByBatchId = batches.stream().collect(Collectors.toMap(MaterialLifecycleDetailDO::getBatchId, Function.identity()));
        for (MaterialBatchDO batchDO : batchDOS) {
            MaterialLifecycleDetailDO detailDO = lifecycleDetailDOMapByBatchId.get(batchDO.getId());
            BigDecimal influenceCount = detailDO.getInfluenceCount();
            BigDecimal oriVerificationQuantity = batchDO.getVerificationQuantity();
            BigDecimal oriRemaineQuantity = batchDO.getRemaineQuantity();
            switch (adjustType) {
                case add -> {
                    batchDO.setVerificationQuantity(oriVerificationQuantity.add(influenceCount));
                    batchDO.setRemaineQuantity(oriRemaineQuantity.subtract(influenceCount));
                }
                case subtract -> {
                    batchDO.setVerificationQuantity(oriVerificationQuantity.subtract(influenceCount));
                    batchDO.setRemaineQuantity(oriRemaineQuantity.add(influenceCount));
                }
            }
        }
        materialBatchMapper.updateBatch(batchDOS);
    }

    @Override
    public MaterialBatchImportRespVO importMaterialBatchList(List<MaterialBatchImportExcelVO> importList) {
        if (CollUtil.isEmpty(importList)) throw new ServiceException(1_032_160_000, "导入数据不能为空");
        log.info("导入数据：{}", importList);
        List<String> successBatchNos = new ArrayList<>();
        // 检验
        for (MaterialBatchImportExcelVO batch : importList) {
            if (StrUtil.isEmpty(batch.getProductCode())) throw new ServiceException(1_032_160_000, "物料编码不能为空");
            if (StrUtil.isEmpty(batch.getProductSpecification())) throw new ServiceException(1_032_160_000, "物料规格不能为空");
            if (batch.getInboundQuantity() ==  null || batch.getInboundQuantity().compareTo(BigDecimal.ZERO) <= 0)
                throw new ServiceException(1_032_160_000, "批次数量需要大于0");
        }

        // 获取物料编码和规格，使用编码 + 规格匹配物料
        Set<String> pdtCodes = importList.stream().map(MaterialBatchImportExcelVO::getProductCode).collect(Collectors.toSet());
        Set<String> specifications = importList.stream().map(MaterialBatchImportExcelVO::getProductSpecification).collect(Collectors.toSet());
        List<MaterialProductDO> products = materialProductService.getProductListByCodeAndSpec(new ArrayList<>(pdtCodes), new ArrayList<>(specifications));
        if (CollUtil.isEmpty(products)) throw new ServiceException(1_032_160_000, "未找到对应的物料");

        // 构建编码 + 规格的组合键用于校验
        Map<String, MaterialProductDO> productDOMapByKey = products.stream().collect(Collectors.toMap(
                p -> p.getCode() + "_" + p.getSpecification(),
                Function.identity(),
                (v1, v2) -> v1
        ));

        // 校验是否所有导入的物料都能找到
        List<String> notFoundMaterials = new ArrayList<>();
        for (MaterialBatchImportExcelVO importItem : importList) {
            String key = importItem.getProductCode() + "_" + importItem.getProductSpecification();
            if (!productDOMapByKey.containsKey(key)) {
                notFoundMaterials.add(String.format("物料【%s】+【%s】", importItem.getProductCode(), importItem.getProductSpecification()));
            }
        }
        if (CollUtil.isNotEmpty(notFoundMaterials)) {
            throw new ServiceException(1_032_160_000, "未找到以下物料：" + String.join(", ", notFoundMaterials));
        }
        // 获取供应商
        Set<String> supplierNames = importList.stream().map(MaterialBatchImportExcelVO::getSupplier).filter(Objects::nonNull).collect(Collectors.toSet());
        List<SupplierDO> suppliers;
        Map<String, SupplierDO> supplierDOMapByName = Map.of();
        if (CollUtil.isNotEmpty(supplierNames)) {
            suppliers = supplierService.getSuppliersByNames(new ArrayList<>(supplierNames));
            if (CollUtil.isEmpty(suppliers)) throw new ServiceException(1_032_160_000, "未找到对应的供应商");
            supplierDOMapByName = suppliers.stream().collect(Collectors.toMap(SupplierDO::getName, Function.identity()));
            if (supplierNames.size() != suppliers.size()) {
                List<String> notFoundNames = new ArrayList<>();
                for (String supplierName : supplierNames) {
                    if (!supplierDOMapByName.containsKey(supplierName)) {
                        notFoundNames.add(supplierName);
                    }
                }
                if (CollUtil.isNotEmpty(notFoundNames)) {
                    throw new ServiceException(1_032_160_000, "未找到以下供应商：" + String.join(", ", notFoundNames));
                }
            }
        }
        // 保存批次
        Map<String, SupplierDO> finalSupplierDOMapByName = supplierDOMapByName;
        List<MaterialBatchDO> batchDOS = importList.stream().map(importBat -> {
            String key = importBat.getProductCode() + "_" + importBat.getProductSpecification();
            MaterialProductDO productDO = productDOMapByKey.get(key);
            // MaterialProductDO productDO = productDOMapByCode.get(importBat.getProductCode());
            LocalDate manufacturerDate = importBat.getManufacturerDate();
            MaterialBatchDO batchDO = new MaterialBatchDO()
                    .setParentId(0L)
                    .setProductId(productDO.getId())
                    .setBatchNo(sequenceUtil.genCode(MaterialConstants.SEQUENCE_BATCH_KEY))
                    .setInboundQuantity(importBat.getInboundQuantity())
                    .setRemaineQuantity(importBat.getInboundQuantity())
                    .setAcceptanceStatus(MaterialAcceptStatus.not_accepted.name())
                    .setSubmitStatus(0)
                    .setManufacturerDate(manufacturerDate)
                    .setLocation(importBat.getLocation())
                    .setRemark(importBat.getRemark());
            SupplierDO supplierDO = finalSupplierDOMapByName.get(importBat.getSupplier());
            batchDO.setSupplierId(supplierDO == null ? null : supplierDO.getId());
            // 处理到期日期
            LocalDate importDueDate = importBat.getDueDate();
            Integer due = productDO.getDue();
            if (importDueDate != null) {
                batchDO.setDueDate(importDueDate);
            } else if (manufacturerDate != null && due != null){
                batchDO.setDueDate(manufacturerDate.plusDays(due));
            }
            successBatchNos.add(batchDO.getBatchNo());
            return batchDO;
        }).toList();
        materialBatchMapper.insertBatch(batchDOS);
        return new MaterialBatchImportRespVO()
                .setSuccessBatchNos(successBatchNos)
                .setSuccessCount(successBatchNos.size());
    }

    @Override
    public void updateByIds(List<Long> ids, MaterialBatchDO updateEntity) {
        if (CollUtil.isEmpty(ids)) return;
        materialBatchMapper.update(updateEntity, Wrappers.lambdaQuery(MaterialBatchDO.class)
                .in(MaterialBatchDO::getId, ids));
    }

    @Override
    public void saveBatch(List<MaterialBatchDO> dos) {
        if (CollUtil.isEmpty(dos)) return;
        materialBatchMapper.insertBatch(dos);
    }

    @Override
    public void updateBatch(List<MaterialBatchDO> gongs) {
        materialBatchMapper.updateBatch(gongs);
    }


    private void lockBatchOrGongReturnExchangeCount(LockType lockType, List<MaterialBatchDO> batchOrGongDOS, Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByBatOrGongId) {
        for (MaterialBatchDO batOrGong : batchOrGongDOS) {
            MaterialLifecycleDetailDO detailDO = lifecycleDetailDOMapByBatOrGongId.get(batOrGong.getId());
            BigDecimal influenceCount = detailDO.getInfluenceCount();
            switch (lockType) {
                case lock -> {
                    BigDecimal lockQuantity = batOrGong.getLockQuantity().add(influenceCount);
                    if (lockQuantity.compareTo(batOrGong.getRemaineQuantity()) > 0)
                        throw new ServiceException(1_032_160_000, "退换货数量大于可用数量");
                    batOrGong.setLockQuantity(lockQuantity);
                }
                case unlock -> {
                    batOrGong.setLockQuantity(batOrGong.getLockQuantity().subtract(influenceCount));
                    if (batOrGong.getLockQuantity().compareTo(BigDecimal.ZERO) < 0)
                        throw new ServiceException(1_032_160_000, "锁定数量小于0，数据异常");
                }
            }
        }
    }

    private void adjustBatchOrGongReturnExchangeCount(AdjustType adjustType, List<MaterialBatchDO> batchOrGongDOS, Map<Long, MaterialLifecycleDetailDO> lifecycleDetailDOMapByBatOrGongId) {
        for (MaterialBatchDO batOrGong : batchOrGongDOS) {
            MaterialLifecycleDetailDO detailDO = lifecycleDetailDOMapByBatOrGongId.get(batOrGong.getId());
            String businessType = detailDO.getBusinessType();
            BigDecimal influenceCount = detailDO.getInfluenceCount();
            if (MaterialFlowType.return_material.getName().equals(businessType)) {
                BigDecimal returnQuantity = adjustType == AdjustType.add ? batOrGong.getReturnQuantity().add(influenceCount)
                        : batOrGong.getReturnQuantity().subtract(influenceCount);
                batOrGong.setReturnQuantity(returnQuantity);
            } else if (MaterialFlowType.replace_material.getName().equals(businessType)) {
                BigDecimal replaceQuantity = adjustType == AdjustType.add ? batOrGong.getReplaceQuantity().add(influenceCount)
                        : batOrGong.getReplaceQuantity().subtract(influenceCount);
                batOrGong.setReplaceQuantity(replaceQuantity);
            }
            BigDecimal remaineQuantity = adjustType == AdjustType.add ? batOrGong.getRemaineQuantity().subtract(influenceCount)
                    : batOrGong.getRemaineQuantity().add(influenceCount);
            if (remaineQuantity.compareTo(BigDecimal.ZERO) < 0)
                throw new ServiceException(1_032_160_000, " 退换货数量大于可用数量");
            if (remaineQuantity.compareTo(batOrGong.getInboundQuantity()) > 0)
                throw new ServiceException(1_032_160_000, "批次数量不能大于初始总数量");

            batOrGong.setRemaineQuantity(remaineQuantity);
        }
    }

    /**
     * 组装物料批次树
     *
     */
    private List<MaterialBatchRespVO> listTransTree(List<MaterialBatchRespVO> voList, Long parentId) {

        // 获取父级节点
        List<MaterialBatchRespVO> parentVOs = voList.stream().filter(vo ->
                vo.getParentId().equals(parentId)).collect(Collectors.toList());
        // 设置响应的子节点
        parentVOs.forEach(vo -> vo.setChildren(listTransTree(voList, vo.getId())));

        return parentVOs;
    }

}