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

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
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.product.ErpProductUnitDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;
import cn.iocoder.yudao.module.erp.framework.bean.ProductUnitEnum;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService;
import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOrderService;
import cn.iocoder.yudao.module.erp.service.stock.ErpStockService;
import cn.iocoder.yudao.module.mes.dal.dataobject.bom.BomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.bom.BomDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailDO;
import cn.iocoder.yudao.module.mes.service.bom.BomService;
import cn.iocoder.yudao.module.mes.service.itemrequisition.entity.ItemRequisitionAndStock;
import cn.iocoder.yudao.module.mes.service.stockworkshop.StockWorkshopService;
import cn.iocoder.yudao.module.mes.service.task.TaskService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;

/**
 * 生产领料 Service 实现类
 *
 * @author 内蒙必硕
 */
@Service
@Validated
public class ItemAnalysisServiceImpl implements ItemAnalysisService {
    private static final Logger log = LoggerFactory.getLogger(ItemAnalysisServiceImpl.class);
    private static final String UNIT_ERROR = "物料bom单位不一致！";
    @Resource
    private ErpStockService erpStockService;
    @Resource
    private StockWorkshopService stockWorkshopService;
    @Resource
    private TaskService taskService;
    @Resource
    private BomService bomService;
    @Resource
    private ErpProductService productService;
    @Resource
    private ErpProductUnitService productUnitService;
    @Resource
    private ErpSaleOrderService saleOrderService;


    //分析任务单的物料需求
    @Override
    public List<ItemRequisitionAndStock> getItemAnalysis(TaskDO taskDO) {

        List<TaskDetailDO> detailDOList = taskService.getTaskDetailListByTaskId(taskDO.getId());
        Map<Long, ItemRequisitionAndStock> itemMap = new HashMap<>();
        //算物料需求
        for (TaskDetailDO detail : detailDOList) {
            //这里要求销售的单位和物料单位一致等于内置单位
            BomDO bomDO = bomService.selectByProductId(detail.getProductId());
            if (!bomDO.getUnitId().equals(ProductUnitEnum.Each.getUnitId())
                    || !detail.getUnitId().equals(ProductUnitEnum.Each.getUnitId())) {
                log.error(UNIT_ERROR);
                throw exception(new ErrorCode(500, UNIT_ERROR));
            }

            //每个taskDetail计算一次物料需求，汇总到map里面
            List<BomDetailDO> bomDetailDOList =
                    bomService.getBomDetailListByProductId(detail.getProductId(), detail.getNumber());
            buildItemMap(itemMap, bomDetailDOList);
        }
        //算库存
        for (Long key : itemMap.keySet()) {
            BigDecimal stockCount = erpStockService.getStockCount(key);
            itemMap.get(key).setStockNumber(stockCount);
            BigDecimal stockWorkshopCount = stockWorkshopService.getStockCount(key);
            itemMap.get(key).setStockWorkshopNumber(stockWorkshopCount);
        }
        List<ItemRequisitionAndStock> list = new ArrayList<>(itemMap.values());
        list = buildDetailVOList(list);
        return list;
    }

    //分析计划的物料需求
    @Override
    public List<ItemRequisitionAndStock> getItemAnalysis(PlanDO plan) {
        List<ItemRequisitionAndStock> list = getItemAnalysis(plan.getProductId(), plan.getPlanNumber());
        return list;
    }

    //分析计划的物料需求
    @Override
    public List<ItemRequisitionAndStock> getItemAnalysis(Long productId, Long number) {
        List<ItemRequisitionAndStock> list = new ArrayList<>();
        if (productId == null || number == null) return list;
        List<BomDetailDO> bomDetailDOList =
                bomService.getBomDetailListByProductId(productId, number);

        for (BomDetailDO bomDetail : bomDetailDOList) {
            Long itemId = bomDetail.getProductId();
            ItemRequisitionAndStock temp = new ItemRequisitionAndStock()
                    .setItemId(itemId)
                    .setUnitId(bomDetail.getUnitId())
                    .setNumber(bomDetail.getUsageNumber());

            BigDecimal stockCount = erpStockService.getStockCount(itemId);
            temp.setStockNumber(stockCount);
            BigDecimal stockWorkshopCount = stockWorkshopService.getStockCount(itemId);
            temp.setStockWorkshopNumber(stockWorkshopCount);
            list.add(temp);
        }
        list = buildDetailVOList(list);
        return list;
    }

    @Override
    public List<ItemRequisitionAndStock> getItemAnalysis(Long orderId) {
        List<ErpSaleOrderItemDO> detailDOList = saleOrderService.getSaleOrderItemListByOrderId(orderId);
        if (orderId == null) return new ArrayList<>();
        Map<Long, ItemRequisitionAndStock> itemMap = new HashMap<>();
        //算物料需求
        for (ErpSaleOrderItemDO detail : detailDOList) {
            //这里要求销售的单位和物料单位一致等于内置单位
            BomDO bomDO = bomService.selectByProductId(detail.getProductId());
            if (!bomDO.getUnitId().equals(ProductUnitEnum.Each.getUnitId())
                    || !detail.getProductUnitId().equals(ProductUnitEnum.Each.getUnitId())) {
                log.error(UNIT_ERROR);
                throw exception(new ErrorCode(500, UNIT_ERROR));
            }
            //每个detail计算一次物料需求，汇总到map里面
            List<BomDetailDO> bomDetailDOList =
                    bomService.getBomDetailListByProductId(detail.getProductId(), detail.getCount().longValue());
            buildItemMap(itemMap, bomDetailDOList);
        }
        //算库存
        for (Long key : itemMap.keySet()) {
            BigDecimal stockCount = erpStockService.getStockCount(key);
            itemMap.get(key).setStockNumber(stockCount);
            BigDecimal stockWorkshopCount = stockWorkshopService.getStockCount(key);
            itemMap.get(key).setStockWorkshopNumber(stockWorkshopCount);
        }
        List<ItemRequisitionAndStock> list = new ArrayList<>(itemMap.values());
        list = buildDetailVOList(list);
        return list;
    }

    //添加原料名称,添加单位名称
    private List<ItemRequisitionAndStock> buildDetailVOList(List<ItemRequisitionAndStock> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        Map<Long, ErpProductDO> map = productService.getProductMap(
                convertSet(list, ItemRequisitionAndStock::getItemId));
        Map<Long, ErpProductUnitDO> unitMap = productUnitService.getProductUnitMap(
                convertSet(list, ItemRequisitionAndStock::getUnitId));

        return BeanUtils.toBean(list, ItemRequisitionAndStock.class, item -> {
            MapUtils.findAndThen(map, item.getItemId(),
                    product -> item.setItemName(product.getName()));
            MapUtils.findAndThen(unitMap, item.getUnitId(),
                    unit -> item.setUnitName(unit.getName()));
        });
    }

    private void buildItemMap(Map<Long, ItemRequisitionAndStock> itemMap, List<BomDetailDO> bomDetailDOList) {
        for (BomDetailDO bomDetail : bomDetailDOList) {
            if (itemMap.containsKey(bomDetail.getProductId())) {
                ItemRequisitionAndStock temp = itemMap.get(bomDetail.getProductId());
                //比较单位是否一致
                if (!temp.getUnitId().equals(bomDetail.getUnitId())) {
                    log.error(UNIT_ERROR);
                    throw exception(new ErrorCode(500, UNIT_ERROR));
                }
                temp.setNumber(temp.getNumber().add(bomDetail.getUsageNumber()));
            } else {
                ItemRequisitionAndStock temp = new ItemRequisitionAndStock()
                        .setItemId(bomDetail.getProductId())
                        .setNumber(bomDetail.getUsageNumber())
                        .setUnitId(bomDetail.getUnitId());
                itemMap.put(bomDetail.getProductId(), temp);
            }
        }
    }
}