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

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
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.ErpStockInService;
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.controller.admin.stockindetail.vo.StockInDetailPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.stockindetail.vo.StockInDetailSaveReqVO;
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 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.math.MathContext;
import java.math.RoundingMode;
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.*;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.STOCK_IN_DETAIL_NOT_EXISTS;

/**
 * 生产入库分配明细 Service 实现类
 *
 * @author 内蒙必硕
 */
@Service
@Validated
public class StockInDetailServiceImpl implements StockInDetailService {

    @Resource
    private StockInDetailMapper stockInDetailMapper;

    @Override
    public Long createStockInDetail(StockInDetailSaveReqVO createReqVO) {
        // 插入
        StockInDetailDO stockInDetail = BeanUtils.toBean(createReqVO, StockInDetailDO.class);
        stockInDetailMapper.insert(stockInDetail);
        // 返回
        return stockInDetail.getId();
    }

    @Override
    public void updateStockInDetail(StockInDetailSaveReqVO updateReqVO) {
        // 校验存在
        validateStockInDetailExists(updateReqVO.getId());
        // 更新
        StockInDetailDO updateObj = BeanUtils.toBean(updateReqVO, StockInDetailDO.class);
        stockInDetailMapper.updateById(updateObj);
    }

    @Override
    public void deleteStockInDetail(Long id) {
        // 校验存在
        validateStockInDetailExists(id);
        // 删除
        stockInDetailMapper.deleteById(id);
    }

    private void validateStockInDetailExists(Long id) {
        if (stockInDetailMapper.selectById(id) == null) {
            throw exception(STOCK_IN_DETAIL_NOT_EXISTS);
        }
    }

    @Override
    public StockInDetailDO getStockInDetail(Long id) {
        return stockInDetailMapper.selectById(id);
    }

    @Override
    public PageResult<StockInDetailDO> getStockInDetailPage(StockInDetailPageReqVO pageReqVO) {
        return stockInDetailMapper.selectPage(pageReqVO);
    }
    @Override
    public BigDecimal selectSumBy(Long planId){
        return stockInDetailMapper.selectSumBy(planId);
    }
    @Override
    public BigDecimal selectSumByTaskDetailId(Long taskDetailId){
        return stockInDetailMapper.selectSumByTaskDetailId(taskDetailId);
    }
    @Resource
    private ErpStockInMapper stockInMapper;
    @Resource
    private ErpStockInItemMapper stockInItemMapper;

    @Resource
    private ErpProductService productService;
    @Resource
    private ErpStockRecordService stockRecordService;
    @Resource
    private ErpStockInService stockInService;
    @Resource
    private PlanService planService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    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);
        }

        // 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);
        }

        // 3. 变更库存
        List<ErpStockInItemDO> stockInItems = stockInItemMapper.selectListByInId(id);
        Integer bizType = approve ? ErpStockRecordBizTypeEnum.MES_PRODUCT_IN.getType()
                : ErpStockRecordBizTypeEnum.MES_PRODUCT_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()));
        });
        List<Integer> statusList = new ArrayList<>();
        statusList.add(PlanStatusEnum.量产.getValue());
        statusList.add(PlanStatusEnum.完工.getValue());
        List<StockInDetailDO> resultList = new ArrayList<>();
        //入库产品分配给计划
        for (ErpStockInItemDO inItem : stockInItems) {
            //查找开工、完工的计划
            List<PlanDO> planList = planService.getPlanByStatusAndProduct(statusList,inItem.getProductId());
            if(planList!=null&& planList.size()>0){
                //该产品只有一个计划在开工
                if(planList.size()==1){
                    StockInDetailDO detailDO = new StockInDetailDO()
                            .setStockInId(stockIn.getId()).setStockInNo(stockIn.getNo())
                            .setProductId(inItem.getProductId()).setStockInItemId(inItem.getId())
                            .setNumber(inItem.getCount()).setPlanId(planList.get(0).getId())
                            .setTaskDetailId(planList.get(0).getTaskDetailId());
                    resultList.add(detailDO);
                }
                else
                   resultList.addAll(stockInToPlan(stockIn,inItem,planList));
            }
            else{
                StockInDetailDO detailDO = new StockInDetailDO()
                        .setStockInId(stockIn.getId()).setStockInNo(stockIn.getNo())
                        .setProductId(inItem.getProductId()).setStockInItemId(inItem.getId())
                        .setNumber(inItem.getCount()).setPlanId(null).setTaskDetailId(null);
                resultList.add(detailDO);
            }
        }
       if(resultList.size()>0)
           stockInDetailMapper.insertBatch(resultList);
    }
    private ErpStockInDO validateStockInExists(Long id) {
        ErpStockInDO stockIn = stockInMapper.selectById(id);
        if (stockIn == null) {
            throw exception(STOCK_IN_NOT_EXISTS);
        }
        return stockIn;
    }
    //该产品有多个计划在开工
    private List<StockInDetailDO> stockInToPlan(ErpStockInDO stockIn, ErpStockInItemDO inItem,List<PlanDO> planList){
        List<StockInDetailDO> detailDOS = new ArrayList<>();
        MathContext mc = new MathContext(0, RoundingMode.HALF_UP);
        MathContext mc2 = new MathContext(6, RoundingMode.HALF_UP);
        long totalNeed = 0;
        Map<Long, Long> planNeedMap = new HashMap<>();
        Map<Long, PlanDO> planDoMap = new HashMap<>();
        for (PlanDO plan : planList) {
            BigDecimal finishNumber = stockInDetailMapper.selectSumBy(plan.getId());
            long planNeed = plan.getPlanNumber() > finishNumber.longValue() ? plan.getPlanNumber() - finishNumber.longValue(): 0;
            totalNeed += planNeed;
            planNeedMap.put(plan.getId(), planNeed);
            planDoMap.put(plan.getId(),plan);
        }
        //根据计划各自的需求比例分配给计划
        BigDecimal unplan = new BigDecimal(inItem.getCount().toString());
        for (Long planId : planNeedMap.keySet()) {
            Long planNeed = planNeedMap.get(planId);
            if(planNeed>0){
                BigDecimal planNumber = inItem.getCount().multiply(BigDecimal.valueOf(planNeed),mc).divide(BigDecimal.valueOf(totalNeed),mc2)
                        .setScale(0,RoundingMode.CEILING);
                StockInDetailDO detailDO = new StockInDetailDO()
                        .setStockInId(stockIn.getId()).setStockInNo(stockIn.getNo())
                        .setProductId(inItem.getProductId()).setStockInItemId(inItem.getId())
                        .setPlanId(planId).setTaskDetailId(planDoMap.get(planId).getTaskDetailId());
                if(unplan.compareTo(planNumber)>=0){
                    detailDO.setNumber(planNumber);
                    unplan = unplan.subtract(planNumber,mc);
                }
                else{
                    detailDO.setNumber(unplan);
                    unplan = BigDecimal.ZERO;
                }
                if(detailDO.getNumber().compareTo(BigDecimal.ZERO)>0)
                    detailDOS.add(detailDO);
            }
        }
        if(unplan.compareTo(BigDecimal.ZERO) > 0 && detailDOS.size()>0){
            detailDOS.get(0).setNumber(detailDOS.get(0).getNumber().add(unplan));
        }
        return detailDOS;
    }
}