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

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.erp.controller.admin.autocode.util.AutoCodeUtil;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
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.ErpStockOutService;
import cn.iocoder.yudao.module.erp.service.stock.ErpStockService;
import cn.iocoder.yudao.module.mes.controller.admin.itemrequisition.vo.ItemRequisitionSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.itemrequisition.vo.ItemRequisitionStatusEnum;
import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.*;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskStatusEnum;
import cn.iocoder.yudao.module.mes.dal.dataobject.itemrequisition.ItemRequisitionDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.organization.OrganizationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.paigongrecord.PaigongRecordDO;
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.dal.mysql.paigongrecord.PaigongRecordMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.plan.PlanMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.stockindetail.StockInDetailMapper;
import cn.iocoder.yudao.module.mes.dal.redis.no.MesNoRedisDAO;
import cn.iocoder.yudao.module.mes.service.itemrequisition.ItemAnalysisService;
import cn.iocoder.yudao.module.mes.service.itemrequisition.ItemRequisitionService;
import cn.iocoder.yudao.module.mes.service.itemrequisition.entity.ItemRequisitionAndStock;
import cn.iocoder.yudao.module.mes.service.organization.OrganizationService;
import cn.iocoder.yudao.module.mes.service.paigongrecord.PaigongRecordService;
import cn.iocoder.yudao.module.mes.service.task.TaskService;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PLAN_CODE_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PLAN_NOT_EXISTS;

/**
 * 生产计划 Service 实现类
 *
 * @author 内蒙必硕
 */
@Service
@Validated
@Slf4j
public class PlanServiceImpl implements PlanService {

    @Resource
    private PlanMapper planMapper;
    @Resource
    private MesNoRedisDAO noRedisDAO;
    @Resource
    private TaskService taskService;
    @Resource
    private PaigongRecordMapper paigongRecordMapper;
    @Resource
    private ItemAnalysisService analysisService;

    @Resource
    private ErpStockOutService stockOutService;

    @Resource
    private ErpStockInService stockInService;

    @Autowired
    private AutoCodeUtil autoCodeUtil;

    @Resource
    private OrganizationService organizationService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPlan(PlanSaveReqVO createReqVO) {
        // 插入
        PlanDO plan = BeanUtils.toBean(createReqVO, PlanDO.class);
        if (StringUtils.isEmpty(plan.getCode())) {
            // 1.4 生成订单号，并校验唯一性
//            String no = noRedisDAO.generate3(MesNoRedisDAO.PLAN_NO_PREFIX);
//            if (planMapper.selectByNo(no) != null) {
//                throw exception(PLAN_NOT_EXISTS);
//            }
//            plan.setCode(no);
            plan.setCode(autoCodeUtil.genSerialCode("PLAN_CODE",null));
        } else {
                        if (planMapper.selectByNo(plan.getCode()) != null) {
                throw exception(PLAN_CODE_EXISTS);
            }
        }
        if(plan.getProductId()==null && plan.getTaskDetailId()!=null){
            TaskDetailDO taskDetailDO = taskService.getTaskDetail(plan.getTaskDetailId());
            plan.setProductId(taskDetailDO.getProductId());
        }
        if (plan.getIsEnable() == null) plan.setIsEnable(true);
        if(plan.getPriorityNum()==null) plan.setPriorityNum(1L);
        //  todo
        // planMapper.insert(plan);
        // 返回
        TaskDO taskDO = taskService.getTask(plan.getTaskId());
        if(taskDO!=null && taskDO.getStatus().equals(TaskStatusEnum.下达.getValue())) {
            taskDO.setStatus(TaskStatusEnum.已排产.getValue());
            taskService.updateTask(taskDO);
        }
        List<ItemRequisitionAndStock> list = analysisService.getItemAnalysis(plan); // 产品 num
        List<ItemRequisitionDetailDO> itemRequisitionDetails = new ArrayList<>();
        for (ItemRequisitionAndStock item : list) {
            ItemRequisitionDetailDO detailDO = new ItemRequisitionDetailDO()
                    .setProductId(item.getItemId())
                    .setUnitId(item.getUnitId())
                    .setNumber(item.getNumber())
                    .setIsEnable(true);
            itemRequisitionDetails.add(detailDO);
        }
        ItemRequisitionSaveReqVO saveReqVO = new ItemRequisitionSaveReqVO();
        saveReqVO.setFeedingPipeline(createReqVO.getFeedingPipeline());
        saveReqVO.setWorkerId(createReqVO.getWorkerId());
        saveReqVO.setItemRequisitionDetails(itemRequisitionDetails);
        saveReqVO.setStatus(ItemRequisitionStatusEnum.下达.getValue());
        if (saveReqVO.getRequisitionDate() == null)
            saveReqVO.setRequisitionDate(LocalDateTime.now());
//        // 创建领料单
        Long id = itemRequisitionService.createItemRequisition(saveReqVO);
        //更新计划状态
        plan.setStatus(PlanStatusEnum.已排产.getValue());
        plan.setIsEnable(true);
        plan.setProductionManagerId(createReqVO.getWorkerId());
        plan.setFeedingPipeline(createReqVO.getFeedingPipeline());
        plan.setStartTime(LocalDateTime.now());
        plan.setRequisitionId(id);
        planMapper.insert(plan);
        saveReqVO.setPlanId(plan.getId());
        saveReqVO.setId(id);
        itemRequisitionService.updateItemRequisition(saveReqVO);
        // 创建入领料出库单
        ErpStockOutSaveReqVO stockOut = new ErpStockOutSaveReqVO();
        List<ErpStockOutSaveReqVO.Item> itemList =  new ArrayList<>();
        stockOut.setOutTime(LocalDateTime.now());
        stockOut.setOutType("领料出库");
        for (ItemRequisitionAndStock requisition : list) {
            ErpStockOutSaveReqVO.Item item = new ErpStockOutSaveReqVO.Item();
            // todo 修改仓库
            item.setWarehouseId(3L);
            item.setProductId(requisition.getItemId());
            item.setCount(requisition.getNumber());
            itemList.add(item);
        }
        stockOut.setItems(itemList);
        stockOutService.createStockOut(stockOut);
        return plan.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createPlanStockIn(PlanStatusUpdateVO statusUpdateVO,PlanDO planDO) {
        // 创建入产品入库单
        ErpStockInSaveReqVO stockIn = new ErpStockInSaveReqVO();
        List<ErpStockInSaveReqVO.Item> itemList =  new ArrayList<>();
        stockIn.setInTime(LocalDateTime.now());
        stockIn.setInType("产品入库");
        ErpStockInSaveReqVO.Item item = new ErpStockInSaveReqVO.Item();
        // todo 修改仓库
        item.setWarehouseId(4L);
        item.setProductId(planDO.getProductId());
        if (planDO.getPassNumber() == null) {
            planDO.setPassNumber(0L);
        }
        item.setCount(BigDecimal.valueOf(planDO.getPassNumber()));
        itemList.add(item);
        stockIn.setItems(itemList);
        stockInService.createStockIn(stockIn);
    }

    @Override
    public void updatePlan(PlanSaveReqVO updateReqVO) {
        // 校验存在
        validatePlanExists(updateReqVO.getId());
        // 更新
        PlanDO updateObj = BeanUtils.toBean(updateReqVO, PlanDO.class);
        if (updateObj.getIsEnable() == null) updateObj.setIsEnable(true);
        if(updateObj.getProductId()==null && updateObj.getTaskDetailId()!=null){
            TaskDetailDO taskDetailDO = taskService.getTaskDetail(updateObj.getTaskDetailId());
            updateObj.setProductId(taskDetailDO.getProductId());
        }
        planMapper.updateById(updateObj);
    }

    @Override
    public void deletePlan(Long id) {
        // 校验存在
        validatePlanExists(id);
        // 删除
        planMapper.deleteById(id);
    }

    private void validatePlanExists(Long id) {
        if (planMapper.selectById(id) == null) {
            throw exception(PLAN_NOT_EXISTS);
        }
    }

    @Override
    public PlanDO getPlan(Long id) {
        return planMapper.selectById(id);
    }
    @Override
    public PlanRespVO getPlanRespVO(PlanDO planDO) {
        PlanRespVO planRespVO =  BeanUtils.toBean(planDO, PlanRespVO.class);
        planRespVO.setProductName(productService.getProduct(planDO.getProductId()).getName());
        planRespVO.setTaskCode(taskService.getTask(planDO.getTaskId()).getCode());
        planRespVO.setWorker(userService.getUser(planDO.getWorkerId()).getUsername());
        planRespVO.setFeedingPipelineName(organizationService.getOrganization(planDO.getFeedingPipeline()).getName());
        return planRespVO;
    }

    @Override
    public List<PlanDO> getPlanCapacity(List<Integer> statusList, LocalDateTime  startTime, LocalDateTime  endTime) {

        return planMapper.getPlanCapacity(statusList,startTime,endTime);

    }

    @Override
    public List<Map<String, Object>> getLastSevenDaysCompletedCount() {
        // 查询数据库
        List<Map<String, Object>> dbResult = planMapper.getLastSevenDaysCompletedCount();

        // 创建日期到数量的映射
        Map<String, Long> countMap = Optional.ofNullable(dbResult)
                .orElse(Collections.emptyList())
                .stream()
                .filter(Objects::nonNull)
                .filter(map -> map.get("date") != null)
                .collect(Collectors.toMap(
                        map -> map.get("date").toString(),
                        map -> {
                            Object value = map.get("totalWangong");
                            if (value instanceof Number) {
                                return ((Number) value).longValue();
                            }
                            return 0L;
                        },
                        Long::sum
                ));

        // 生成最近7天
        List<Map<String, Object>> result = new ArrayList<>();
        LocalDate today = LocalDate.now();

        for (int i = 6; i >= 0; i--) {
            String date = today.minusDays(i).toString();

            HashMap<String, Object> map = new HashMap<>();
            map.put("date",date);
            map.put("totalWangong",countMap.getOrDefault(date, 0L));
            result.add(map);

        }

        return result;
    }


    @Override
    public PageResult<PlanRespVO> getPlanPage(PlanPageReqVO pageReqVO) {
        PageResult<PlanDO> pageResult = planMapper.selectPage(pageReqVO);
        return new PageResult<>(buildVOList(pageResult.getList()), pageResult.getTotal());
    }

    @Resource
    private ErpProductService productService;
    @Resource
    private AdminUserService userService;
    @Resource
    private StockInDetailMapper stockInDetailMapper;

    @Override
    public List<PlanRespVO> buildVOList(List<PlanDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        //入库数量统计
        list = buildProgress(list);
        Map<Long, ErpProductDO> map = productService.getProductMap(
                convertSet(list, PlanDO::getProductId));
        Map<Long, AdminUserDO> userMap = userService.getUserMap(
                convertSet(list, PlanDO::getProductionManagerId));
        Map<Long, TaskDO> taskMap = taskService.getMap(
                convertSet(list, PlanDO::getTaskId));
        Map<Long, OrganizationDO> organizationMap = organizationService.getMap(
                convertSet(list, PlanDO::getFeedingPipeline));
        return BeanUtils.toBean(list, PlanRespVO.class, item -> {
            MapUtils.findAndThen(map, item.getProductId(),
                    product -> item.setProductName(product.getName()));

            MapUtils.findAndThen(userMap, item.getProductionManagerId(),
                    unit -> item.setProductionManager(unit.getNickname()));

            MapUtils.findAndThen(taskMap, item.getTaskId(),
                    task -> item.setTaskCode(task.getCode()));

            MapUtils.findAndThen(organizationMap, item.getFeedingPipeline(),
                    organization -> item.setFeedingPipelineName(organization.getName()));
        });
    }
    @Override
    public List<PlanDO> buildProgress(List<PlanDO> list){
        for (PlanDO plan : list) {
            BigDecimal count = stockInDetailMapper.selectSumBy(plan.getId());
            plan.setFinishNumber(count.longValue());
        }
        return list;
    }
    /**
     * 查询按日期排序最后一个未完成(已经下达)的计划
     **/
    public PlanDO selectLastOne() {
        return planMapper.selectLastOne();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveBatchPlan(List<PlanDO> planDOList) {
        // 返回
        if(planDOList!=null && planDOList.size()>0){
            TaskDO taskDO = taskService.getTask(planDOList.get(0).getTaskId());
            if(taskDO!=null && taskDO.getStatus().equals(TaskStatusEnum.下达.getValue())) {
                taskDO.setStatus(TaskStatusEnum.已排产.getValue());
                taskService.updateTask(taskDO);
            }
            return planMapper.insertBatch(planDOList);
        }
        return false;
    }

    @Resource
    private ItemRequisitionService itemRequisitionService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean arrangePlan(ItemRequisitionSaveReqVO saveReqVO, PlanDO planDO, List<ItemRequisitionAndStock> list) {

        List<ItemRequisitionDetailDO> itemRequisitionDetails = new ArrayList<>();
        for (ItemRequisitionAndStock item : list) {
            ItemRequisitionDetailDO detailDO = new ItemRequisitionDetailDO()
                    .setProductId(item.getItemId())
                    .setUnitId(item.getUnitId())
                    .setNumber(item.getNumber())
                    .setIsEnable(true);
            itemRequisitionDetails.add(detailDO);
        }
        saveReqVO.setItemRequisitionDetails(itemRequisitionDetails);
        saveReqVO.setStatus(ItemRequisitionStatusEnum.下达.getValue());
        if (saveReqVO.getRequisitionDate() == null)
            saveReqVO.setRequisitionDate(LocalDateTime.now());
        // 创建领料单
        Long id = itemRequisitionService.createItemRequisition(saveReqVO);
        //更新计划状态
        planDO.setStatus(PlanStatusEnum.已排产.getValue());
        planDO.setIsEnable(true);
        planDO.setProductionManagerId(saveReqVO.getWorkerId());
        //planDO.setFeedingPipeline(saveReqVO.getFeedingPipeline());
        planDO.setStartTime(LocalDateTime.now());
        planDO.setRequisitionId(id);
        planMapper.updateById(planDO);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean arrangePlanNum(ItemRequisitionSaveReqVO saveReqVO, PlanDO planDO) {
        // 创建派工记录
        PaigongRecordDO paigongRecordDO = new PaigongRecordDO()
                .setPaigongNum(saveReqVO.getPaigongNum())
                .setPipeline(saveReqVO.getPipeline())
                .setIsPreProduction(saveReqVO.getIsPreProduction())
                .setPlanId(planDO.getId())
                .setPaigongTime(LocalDateTime.now());
        paigongRecordMapper.insert(paigongRecordDO);
        // 更新未派工数量
        planDO.setNoPaigongNumber(planDO.getPlanNumber()-saveReqVO.getPaigongNum());
        planMapper.updateById(planDO);
        return true;
    }

    @Override
    public List<PlanDO> getPlanList(Collection<Long> ids) {
        return planMapper.selectBatchIds(ids);
    }

    @Override
    public List<PlanDO> getPlanByStatus(List<Integer> statusList) {
        return planMapper.selectBy(null,statusList);
    }
    @Override
    public
    List<PlanDO> getPlanByStatusAndProduct(List<Integer> statusList, Long productId){
        return planMapper.selectBy(productId,statusList);
    }
    @Override
    public List<PlanDO> getPlanByStatusAndPipeline(List<Integer> statusList, String pipeline) {
        return planMapper.selectBy(statusList, pipeline);
    }
    @Override
    public  void updateStatus( Long id,Integer status){
        if(status.equals(PlanStatusEnum.量产.getValue())){
            PlanDO plan = planMapper.selectById(id);
            TaskDO taskDO = taskService.getTask(plan.getTaskId());
            if( taskDO.getStatus().equals(TaskStatusEnum.已排产.getValue())) {
                taskDO.setStatus(TaskStatusEnum.开工.getValue());
                taskService.updateTask(taskDO);
            }
        }
        planMapper.updateStatus(id, status);
    }
    @Override
    public List<ErpProductRespVO> getProductByPlanStatus(List<Integer> statusList){
        List<PlanDO> planDOList =  getPlanByStatus(statusList);
        List<Long> ids = planDOList.stream().map(PlanDO::getProductId).collect(Collectors.toList());
       if(ids!=null && ids.size()>0){
           List<ErpProductRespVO> list =  productService.buildProductVOList(productService.getProductList(ids));
           return list;
       }
        return new ArrayList<>();
    }

    @Override
    public List<PlanDO> getPlanBy(PlanPageReqVO pageReqVO){
        List<PlanDO> planDOList = planMapper.selectBy(pageReqVO);
        return planDOList;
    }
}