package com.fjhx.mes.service.production.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.fjhx.common.constant.SourceConstant;
import com.fjhx.common.enums.CodingRuleEnum;
import com.fjhx.common.enums.PushBusinessTypeEnum;
import com.fjhx.common.service.coding.CodingRuleService;
import com.fjhx.item.entity.product.po.ProductInfo;
import com.fjhx.item.service.product.ProductInfoService;
import com.fjhx.mes.entity.production.po.ProductionPlan;
import com.fjhx.mes.entity.production.po.ProductionTask;
import com.fjhx.mes.entity.production.po.ProductionTaskProcessesDetail;
import com.fjhx.mes.entity.production.vo.ProductionTaskVo;
import com.fjhx.mes.entity.work.po.WorkOrder;
import com.fjhx.mes.entity.work.po.WorkOrderProductionProcesses;
import com.fjhx.mes.entity.work.vo.WorkOrderVo;
import com.fjhx.mes.mapper.production.ProductionPlanMapper;
import com.fjhx.mes.service.production.ProductionPlanService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.mes.service.production.ProductionTaskService;
import com.fjhx.mes.service.work.WorkOrderService;
import com.fjhx.mes.utils.code.CodeEnum;
import com.fjhx.socket.core.PushTypeEnum;
import com.fjhx.socket.core.WebSocketPush;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.wrapper.SqlField;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.utils.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fjhx.mes.entity.production.vo.ProductionPlanVo;
import com.fjhx.mes.entity.production.dto.ProductionPlanSelectDto;
import com.ruoyi.common.utils.wrapper.IWrapper;
import com.fjhx.mes.entity.production.dto.ProductionPlanDto;
import cn.hutool.core.bean.BeanUtil;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * <p>
 * 生产计划 服务实现类
 * </p>
 *
 * @author
 * @since 2023-03-29
 */
@Service
public class ProductionPlanServiceImpl extends ServiceImpl<ProductionPlanMapper, ProductionPlan> implements ProductionPlanService {

    @Autowired
    WorkOrderService workOrderService;
    @Autowired
    ProductInfoService productInfoService;
    @Autowired
    ProductionTaskService productionTaskService;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private CodingRuleService codingRuleService;

    @Override
    public Page<ProductionPlanVo> getPage(ProductionPlanSelectDto dto) {
        IWrapper<ProductionPlan> wrapper = getWrapper();
        if(ObjectUtil.isNotEmpty(dto.getKeyword())) {
            wrapper.keyword(dto.getKeyword(),
                    new SqlField(ProductionPlan::getStartDate),
                    new SqlField(ProductionPlan::getStopDate),
                    new SqlField(ProductionPlan::getQuantity),
                    new SqlField(ProductionPlan::getCode),
                    new SqlField("pp.productName"),
                    new SqlField("pp.workOrderCode")
            );
        }

        //计划状态
        wrapper.eq(ProductionPlan::getStatus ,dto.getStatus());

        //开始时间高级检索
        wrapper.ge("pp",ProductionPlan::getStartDate,dto.getBeginTime());
        wrapper.le("pp",ProductionPlan::getStartDate,dto.getEndTime());

        wrapper.orderByAsc("pp", ProductionPlan::getStartDate);

        //过滤待排程（待创建任务的计划）
        wrapper.gt(Objects.equals(dto.getIsRemaining(),1),"pp.remainingQuantity",0);

        Page<ProductionPlanVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);
        return page;
    }

    @Override
    public ProductionPlanVo detail(Long id) {
        ProductionPlan ProductionPlan = this.getById(id);
        ProductionPlanVo result = BeanUtil.toBean(ProductionPlan, ProductionPlanVo.class);
        //赋值产品信息
        WorkOrder workOrder = workOrderService.getById(result.getWorkOrderId());
        ProductInfo productInfo = productInfoService.getById(workOrder.getProductId());
        if(ObjectUtil.isNotEmpty(productInfo)) {
            result.setProductName(productInfo.getName());
            result.setProductSpec(productInfo.getSpec());
        }
        //赋值生产任务列表
        List<ProductionTask> list = productionTaskService.list(q -> q.eq(ProductionTask::getProductionPlanId, id));
        List<ProductionTaskVo> productionTaskVos = BeanUtil.copyToList(list, ProductionTaskVo.class);
        //赋值负责人名字
        UserUtil.assignmentNickName(productionTaskVos, ProductionTask::getPersonLiableId, ProductionTaskVo::setPersonLiableName);
        result.setProductionTaskList(productionTaskVos);

        //获取工序列表
        result.setProductionProcessesList(workOrderService.getProductionProcessesList(workOrder));

        return result;
    }

    @DSTransactional
    @Override
    public synchronized void add(ProductionPlanDto productionPlanDto) {
        Long workOrderId = productionPlanDto.getWorkOrderId();
        WorkOrder workOrder = workOrderService.getById(workOrderId);

        //检查待排程数量防止并发
        List<ProductionPlan> productionPlanList = this.list(q -> q.eq(ProductionPlan::getWorkOrderId, productionPlanDto.getWorkOrderId()));
        BigDecimal planCount = productionPlanList.stream().map(ProductionPlan::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        if(planCount.add(productionPlanDto.getQuantity()).compareTo(workOrder.getProductionQuantity())>0){
            throw new ServiceException("计划数量大于工单生产数量，请检查!");
        }

        productionPlanDto.setStatus(0);
//        productionPlanDto.setCode(CodeEnum.PRODUCTION_PLAN.getCode());
        productionPlanDto.setCode(codingRuleService.createCode(CodingRuleEnum.JXST_PRODUCTION_PLAN.getKey(),null));
        this.save(productionPlanDto);
        //修改工单状态为进行中
        workOrder.setStatus(1);
        workOrderService.updateById(workOrder);

        //推送消息给生产任务下发的负责人
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        List<Long> userIds = userService.getUserIdsByRoleKey("design");
        DynamicDataSourceContextHolder.poll();
        WebSocketPush.byUsers(PushTypeEnum.MESSAGE, userIds, "您有新的生产计划待下发！", PushBusinessTypeEnum.NEW_PLAN.getType());
    }

    @Override
    public void edit(ProductionPlanDto productionPlanDto) {
        this.updateById(productionPlanDto);
    }

    @Override
    public void delete(Long id) {
        this.removeById(id);
    }

}
