package cn.iocoder.yudao.module.mes.service.stockworkshop;

import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInMapper;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService;
import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;
import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.PlanStatusEnum;
import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.stockindetail.StockInDetailDO;
import cn.iocoder.yudao.module.mes.dal.mysql.stockindetail.StockInDetailMapper;
import cn.iocoder.yudao.module.mes.service.plan.PlanService;
import cn.iocoder.yudao.module.mes.service.stockindetail.StockInDetailService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;

// TODO 芋艿：记录操作日志

/**
 * 生产入库单 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class MesStockInServiceImpl implements MesStockInService {

    @Resource
    private ErpStockInMapper stockInMapper;
    @Resource
    private ErpStockInItemMapper stockInItemMapper;

    @Resource
    private ErpProductService productService;
    @Resource
    private StockInDetailMapper stockInDetailMapper;
    @Resource
    private ErpStockRecordService stockRecordService;


    @Resource
    private StockInDetailService stockInDetailService;
    @Resource
    private PlanService planService;

    @Override
    public void updateStockInStatus(Long id, Integer status) {
        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
        // 1.1 校验存在
        ErpStockInDO stockIn = validateStockInExists(id);
        // 1.2 校验状态
        if (stockIn.getStatus().equals(status)) {
            throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL);
        }

        // 3. 变更库存
        List<ErpStockInItemDO> stockInItems = stockInItemMapper.selectListByInId(id);

        //生产入库分配给计划
        //完工的计划列表
        List<Integer> statusList = new ArrayList<>();
        statusList.add(PlanStatusEnum.完工.getValue());
        List<PlanDO> finishedPlans = planService.getPlanByStatus(statusList);
        planService.buildProgress(finishedPlans);

        //开工的计划列表
        statusList = new ArrayList<>();
        statusList.add(PlanStatusEnum.量产.getValue());
        List<PlanDO> startPlans = planService.getPlanByStatus(statusList);
        planService.buildProgress(startPlans);
        //合并同产品
        Map<Long, ErpStockInItemDO> stockInItemDOMap = new HashMap<>();
        stockInItems.forEach(itemDO -> {
            if(stockInItemDOMap.containsKey(itemDO.getProductId())){
                BigDecimal old = stockInItemDOMap.get(itemDO.getProductId()).getCount();
                stockInItemDOMap.get(itemDO.getProductId()).setCount(old.add(itemDO.getCount()));
            }
            else stockInItemDOMap.put(itemDO.getProductId(), itemDO);
        });

        //分配
        Map<Long, StockInDetailDO> stockInDetailDOMap = get(stockIn,finishedPlans,startPlans,stockInItemDOMap);
        updateStatus( id,  status,  approve,  stockIn,stockInItems
                ,finishedPlans,  startPlans, stockInItemDOMap, stockInDetailDOMap);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(Long id, Integer status, boolean approve, ErpStockInDO stockIn,List<ErpStockInItemDO> stockInItems
    ,List<PlanDO> finishedPlans, List<PlanDO> startPlans, Map<Long, ErpStockInItemDO> stockInItemDOMap,
                             Map<Long, StockInDetailDO> stockInDetailDOMap){
        // 2. 更新状态
        int updateCount = stockInMapper.updateByIdAndStatus(id, stockIn.getStatus(),
                new ErpStockInDO().setStatus(status));
        if (updateCount == 0) {
            throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL);
        }

        Integer bizType = approve ? ErpStockRecordBizTypeEnum.OTHER_IN.getType()
                : ErpStockRecordBizTypeEnum.OTHER_IN_CANCEL.getType();
        stockInItems.forEach(stockInItem -> {
            BigDecimal count = approve ? stockInItem.getCount() : stockInItem.getCount().negate();
            ErpProductDO productDO = productService.getProduct(stockInItem.getProductId());
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    stockInItem.getProductId(),productDO.getCategoryId(), stockInItem.getWarehouseId(), count,
                    bizType, stockInItem.getInId(), stockInItem.getId(), stockIn.getNo(),stockIn.getInTime()));
        });

        //保存分配明细
        if(stockInDetailDOMap.size()>0)
            stockInDetailMapper.insertBatch( stockInDetailDOMap.values());
        //将完成数大于计划数的完工计划状态改为 入库状态
        for (PlanDO plan : finishedPlans) {
            if(plan.getFinishNumber()>= plan.getPlanNumber()){
                planService.updateStatus(plan.getId(), PlanStatusEnum.待入库.getValue());
            }
        }
    }
    private ErpStockInDO validateStockInExists(Long id) {
        ErpStockInDO stockIn = stockInMapper.selectById(id);
        if (stockIn == null) {
            throw exception(STOCK_IN_NOT_EXISTS);
        }
        return stockIn;
    }

    private Map<Long, StockInDetailDO> get( ErpStockInDO stockIn, List<PlanDO> finishedPlans, List<PlanDO> startPlans,
                                           Map<Long, ErpStockInItemDO> stockInItemDOMap){
        //分配入库数量
        Map<Long, StockInDetailDO> stockInDetailDOMap = new HashMap<>();
        //先分配给完工的计划
        for (PlanDO plan : finishedPlans) {
            //计划待分配
            long needNumber = plan.getPlanNumber() - plan.getFinishNumber() ;
            if(needNumber > 0 && stockInItemDOMap.containsKey(plan.getProductId())){
                ErpStockInItemDO itemDO = stockInItemDOMap.get(plan.getProductId());
                //入库待分配
                if(itemDO.getCount().compareTo(BigDecimal.ZERO) > 0){
                    StockInDetailDO detailDO = new StockInDetailDO()
                            .setStockInItemId(itemDO.getId())
                            .setStockInId(stockIn.getId()).setStockInNo(stockIn.getNo())
                            .setPlanId(plan.getId()).setProductId(plan.getProductId())
                            .setTaskDetailId(plan.getTaskDetailId());

                    if(itemDO.getCount().compareTo(BigDecimal.valueOf(needNumber)) > 0){
                        detailDO.setNumber(BigDecimal.valueOf(needNumber));
                    }
                    else detailDO.setNumber(itemDO.getCount());
                    //入库数量减去分配给计划的数量
                    itemDO.setCount(itemDO.getCount().add(detailDO.getNumber().negate()));
                    plan.setFinishNumber(plan.getFinishNumber()+detailDO.getNumber().longValue());
                    stockInDetailDOMap.put(itemDO.getId(), detailDO);
                }
            }
        }
       //分配给开工的计划
        for (PlanDO plan : startPlans) {
            //计划待分配
            long needNumber = plan.getPlanNumber() - plan.getFinishNumber() ;
            if(needNumber > 0 && stockInItemDOMap.containsKey(plan.getProductId())){
                ErpStockInItemDO itemDO = stockInItemDOMap.get(plan.getProductId());
                //入库待分配
                if(itemDO.getCount().compareTo(BigDecimal.ZERO) > 0){
                    StockInDetailDO detailDO = new StockInDetailDO()
                            .setStockInItemId(itemDO.getId())
                            .setStockInId(stockIn.getId()).setStockInNo(stockIn.getNo())
                            .setPlanId(plan.getId()).setProductId(plan.getProductId())
                            .setTaskDetailId(plan.getTaskDetailId());

                    if(itemDO.getCount().compareTo(BigDecimal.valueOf(needNumber)) > 0){
                        detailDO.setNumber(BigDecimal.valueOf(needNumber));
                    }
                    else detailDO.setNumber(itemDO.getCount());
                    //入库数量减去分配给计划的数量
                    itemDO.setCount(itemDO.getCount().add(detailDO.getNumber().negate()));
                    plan.setFinishNumber(plan.getFinishNumber()+detailDO.getNumber().longValue());
                    stockInDetailDOMap.put(itemDO.getId(), detailDO);
                }
            }
        }
        //处理剩下的
        for (Long productId : stockInItemDOMap.keySet()) {
            ErpStockInItemDO itemDO = stockInItemDOMap.get(productId);
            //有剩余未分配
            if(itemDO.getCount().compareTo(BigDecimal.ZERO)>0){
                if(stockInDetailDOMap.containsKey(itemDO.getId())){
                    BigDecimal old = stockInDetailDOMap.get(itemDO.getId()).getNumber();
                    stockInDetailDOMap.get(itemDO.getId()).setNumber(old.add(itemDO.getCount()));
                }else{
                    StockInDetailDO detailDO = new StockInDetailDO()
                            .setStockInItemId(itemDO.getId())
                            .setStockInId(stockIn.getId()).setStockInNo(stockIn.getNo())
                            .setPlanId(null).setProductId(itemDO.getProductId())
                            .setTaskDetailId(null);
                    detailDO.setNumber(itemDO.getCount());
                    stockInDetailDOMap.put(itemDO.getId(), detailDO);
                }
            }
        }

        return stockInDetailDOMap;
    }

}