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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.area.utils.CustomizeAreaUtil;
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.common.utils.Assert;
import com.fjhx.customer.entity.customer.po.Customer;
import com.fjhx.customer.service.customer.CustomerService;
import com.fjhx.file.utils.ObsFileUtil;
import com.fjhx.item.entity.product.po.ProductInfo;
import com.fjhx.item.service.product.ProductInfoService;
import com.fjhx.mes.entity.bom.po.BomDetail;
import com.fjhx.mes.entity.bom.po.BomInfo;
import com.fjhx.mes.entity.production.po.ProductionPlan;
import com.fjhx.mes.entity.production.po.ProductionProcesses;
import com.fjhx.mes.entity.production.po.ProductionTask;
import com.fjhx.mes.entity.production.po.ProductionTaskDetail;
import com.fjhx.mes.entity.work.dto.WorkOrderDto;
import com.fjhx.mes.entity.work.dto.WorkOrderSelectDto;
import com.fjhx.mes.entity.work.po.WorkOrder;
import com.fjhx.mes.entity.work.po.WorkOrderBom;
import com.fjhx.mes.entity.work.po.WorkOrderProductionProcesses;
import com.fjhx.mes.entity.work.vo.WorkOrderVo;
import com.fjhx.mes.mapper.work.WorkOrderMapper;
import com.fjhx.mes.service.bom.BomDetailService;
import com.fjhx.mes.service.bom.BomInfoService;
import com.fjhx.mes.service.production.ProductionPlanService;
import com.fjhx.mes.service.production.ProductionProcessesService;
import com.fjhx.mes.service.production.ProductionTaskDetailService;
import com.fjhx.mes.service.production.ProductionTaskService;
import com.fjhx.mes.service.work.WorkOrderBomService;
import com.fjhx.mes.service.work.WorkOrderProductionProcessesService;
import com.fjhx.mes.service.work.WorkOrderService;
import com.fjhx.socket.core.PushTypeEnum;
import com.fjhx.socket.core.WebSocketPush;
import com.fjhx.wms.entity.stock.emums.StockWaitType;
import com.fjhx.wms.entity.stock.po.StockWait;
import com.fjhx.wms.entity.stock.po.StockWaitDetails;
import com.fjhx.wms.service.stock.StockService;
import com.fjhx.wms.service.stock.StockWaitDetailsService;
import com.fjhx.wms.service.stock.StockWaitService;
import com.ruoyi.common.core.domain.BasePo;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.wrapper.IWrapper;
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 java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 工单 服务实现类
 * </p>
 *
 * @author
 * @since 2023-03-29
 */
@Service
public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements WorkOrderService {

    @Autowired
    private ProductInfoService productInfoService;
    @Autowired
    private ProductionPlanService productionPlanService;
    @Autowired
    private BomInfoService bomInfoService;
    @Autowired
    private BomDetailService bomDetailService;
    @Autowired
    private StockService stockService;
    @Autowired
    private ProductionTaskService productionTaskService;
    @Autowired
    private ProductionTaskDetailService productionTaskDetailService;
    @Autowired
    private ProductionProcessesService productionProcessesService;
    @Autowired
    private WorkOrderProductionProcessesService workOrderProductionProcessesService;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private StockWaitService stockWaitService;
    @Autowired
    private StockWaitDetailsService stockWaitDetailsService;
    @Autowired
    private WorkOrderBomService workOrderBomService;
    @Autowired
    private CustomerService customerService;
    @Autowired
    private CodingRuleService codingRuleService;

    @Override
    public Page<WorkOrderVo> getPage(WorkOrderSelectDto dto) {
        IWrapper<WorkOrder> wrapper = getWrapper();
        if (ObjectUtil.isNotEmpty(dto.getKeyword())) {
            wrapper.keyword(dto.getKeyword(),
                    new SqlField("wo", WorkOrder::getCode),
                    new SqlField("wo", WorkOrder::getQuantity),
                    new SqlField("wo.contractCode")
            );
        }
        wrapper.eq("wo", WorkOrder::getSource, dto.getSource());


        if (ObjectUtil.isNotEmpty(dto.getIsHandle())) {
            SysUser user = SecurityUtils.getLoginUser().getUser();


            List<SysRole> sysRoleList = user.getRoles();
            List<String> roleKeys = sysRoleList.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
            //研发
            if (roleKeys.contains("dev")) {
                wrapper.eq("wo", WorkOrder::getResearchStatus, dto.getIsHandle());
            }
            //制图
            if (roleKeys.contains("design")) {
                wrapper.and(q -> q
                        .eq("wo", WorkOrder::getBomStatus, dto.getIsHandle())
                        .eq("wo", WorkOrder::getTechnologyStatus, dto.getIsHandle())
                );
            }
            //研发，制图
            if (roleKeys.contains("dev") && roleKeys.contains("design")) {
                wrapper.and(q -> q
                        .eq("wo", WorkOrder::getResearchStatus, dto.getIsHandle())
                        .or().and(q1 -> q1
                                .eq("wo", WorkOrder::getBomStatus, dto.getIsHandle())
                                .eq("wo", WorkOrder::getTechnologyStatus, dto.getIsHandle())
                        )
                );
            }
        }

        //过滤待排程（待创建计划的工单）
        if (Objects.equals(dto.getIsRemaining(), 1)) {
            //研发必须提交后
            wrapper.eq("wo", WorkOrder::getResearchStatus, 1);

            wrapper.gt("wo.remainingQuantity", 0);
            //过滤非定制/定制但已经重新配置了bom和工艺的工单
            wrapper.and(q -> q
                    .and(q1 -> q1
                            .eq("wo", WorkOrder::getIsCustomized, 1)
                            .eq("wo", WorkOrder::getBomStatus, 1)
                            .eq("wo", WorkOrder::getTechnologyStatus, 1)
                    ).or()
                    .eq("wo", WorkOrder::getIsCustomized, 0)
            );
            //过滤调需要人工干预生产数量的工单
            wrapper.isNotNull("wo.production_quantity");
        }

        //是否定制
        wrapper.eq("wo", WorkOrder::getIsCustomized, dto.getIsCustomized());

        wrapper.orderByDesc("wo", WorkOrder::getId);
        Page<WorkOrderVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);
        List<WorkOrderVo> records = page.getRecords();
        //赋值产品名
        List<Long> productIds = records.stream().map(WorkOrder::getProductId).collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(productIds)) {
            //赋值产品名称
            productInfoService.attributeAssign(records, WorkOrderVo::getProductId, (item, productInfo) -> {
                item.setProductName(productInfo.getName());
                item.setProductSpec(productInfo.getSpec());
            });
        }
        return page;
    }

    @Override
    public WorkOrderVo detail(Long id) {
        WorkOrderVo result = baseMapper.detail(IWrapper.<WorkOrder>getWrapper().eq("wo", WorkOrder::getId, id));
        List<ProductionPlan> list = productionPlanService.list(q -> q.eq(ProductionPlan::getWorkOrderId, id));
        result.setProductionPlans(list);
        ProductInfo productInfo = productInfoService.getById(result.getProductId());
        if (ObjectUtil.isNotEmpty(productInfo)) {
            result.setProductName(productInfo.getName());
            result.setProductSpec(productInfo.getSpec());
        }
        //赋值产品可用库存
        BigDecimal availableStockQuantity = stockService.getAvailableStockQuantity(result.getProductId());
        result.setAvailableStockQuantity(availableStockQuantity);

        //赋值创建人名称
        UserUtil.assignmentNickName(Collections.singletonList(result), WorkOrderVo::getContractCreateUser, WorkOrderVo::setContractCreateUserName);

        //赋值买方信息
        Customer customer = customerService.getById(result.getCustomerId());
        if (ObjectUtil.isNotEmpty(customer)) {
            //赋值客户国省市名称
            Map<Long, String> areaMapByIds = CustomizeAreaUtil.getAreaMapByIds(Arrays.asList(
                    customer.getCountryId(),
                    customer.getProvinceId(),
                    customer.getCityId())
            );
            result.setCustomerName(customer.getName());
            result.setBuyCountryName(areaMapByIds.get(customer.getCountryId()));
            result.setBuyProvinceName(areaMapByIds.get(customer.getProvinceId()));
            result.setBuyCityName(areaMapByIds.get(customer.getCityId()));
            result.setBuyAddress(customer.getAddress());
        }

        return result;
    }

    @DSTransactional
    @Override
    public void add(WorkOrderDto workOrderDto) {
        //查询一下BOM信息看看是否已经配置
        getBomInfo(workOrderDto);

        //生成工单编号,以及设置默认状态
//        workOrderDto.setCode(CodeEnum.WORK_ORDER.getCode());
        workOrderDto.setCode(codingRuleService.createCode(CodingRuleEnum.JXST_WORK_ORDER.getKey(), null));
        workOrderDto.setStatus(0);
        this.save(workOrderDto);
    }

    @Override
    public void edit(WorkOrderDto workOrderDto) {
        this.updateById(workOrderDto);
    }

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

    @Override
    public Page<WorkOrderVo> getPageByJxst(WorkOrderSelectDto dto) {
        Page<WorkOrderVo> page = getPage(dto);
        List<WorkOrderVo> records = page.getRecords();
        for (WorkOrderVo workOrderVo : records) {
            workOrderVo.setArrangedQuantity(BigDecimal.ZERO);
            workOrderVo.setCompletionRate(BigDecimal.ZERO);

            //人工生产数量为0 完工率 100
            if (ObjectUtil.isNotEmpty(workOrderVo.getProductionQuantity()) && workOrderVo.getProductionQuantity().compareTo(BigDecimal.ZERO) == 0) {
                workOrderVo.setCompletionRate(BigDecimal.valueOf(100));
            }

            if (ObjectUtil.isNotEmpty(workOrderVo.getProductionQuantity()) && workOrderVo.getProductionQuantity().compareTo(BigDecimal.ZERO) > 0) {
                //赋值已计划数量
                workOrderVo.setArrangedQuantity(workOrderVo.getProductionQuantity().subtract(workOrderVo.getRemainingQuantity()));
                //赋值完成率
                long count = 0;
                //获取工单下的所有计划
                List<Long> productionPlanIds = productionPlanService.listObject(ProductionPlan::getId,
                        q -> q.eq(ProductionPlan::getWorkOrderId, workOrderVo.getId()));
                if (ObjectUtil.isNotEmpty(productionPlanIds)) {
                    //获取计划下的所有任务
                    List<Long> productionTaskIds = productionTaskService.listObject(ProductionTask::getId,
                            q -> q.in(ProductionTask::getProductionPlanId, productionPlanIds));
                    if (ObjectUtil.isNotEmpty(productionTaskIds)) {
                        count = productionTaskDetailService.count(q ->
                                q.in(ProductionTaskDetail::getProductionTaskId, productionTaskIds)
                                        .eq(ProductionTaskDetail::getFinishStatus, 1));
                    }
                }
                BigDecimal divide = BigDecimal.valueOf(count).divide(workOrderVo.getProductionQuantity(), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                workOrderVo.setCompletionRate(divide);
            }
        }
        return page;
    }


    /**
     * 根据工单类型获取工序
     */
    @Override
    public List<WorkOrderProductionProcesses> getProductionProcessesList(WorkOrder workOrder) {
        if (Objects.equals(workOrder.getIsCustomized(), "1")) {
            //获取定制工序列表
            List<WorkOrderProductionProcesses> workOrderProductionProcesses = workOrderProductionProcessesService.list(q -> q.eq(WorkOrderProductionProcesses::getWorkOrderId, workOrder.getId()));
            if (ObjectUtil.isEmpty(workOrderProductionProcesses)) {
                throw new ServiceException("定制工单 工序列表为空");
            }
            return workOrderProductionProcesses;
        } else {
            //获取基础工序列表
            List<ProductionProcesses> baseProductionProcessesList = productionProcessesService.getBaseProductionProcessesList(workOrder);
            List<WorkOrderProductionProcesses> workOrderProductionProcesses = BeanUtil.copyToList(baseProductionProcessesList, WorkOrderProductionProcesses.class);
            int i = 0;
            for (WorkOrderProductionProcesses workOrderProductionProcess : workOrderProductionProcesses) {
                workOrderProductionProcess.setSort(i);
                i++;
            }
            return workOrderProductionProcesses;
        }
    }


    /**
     * 检查定制工单BOM 工艺配置状态 如果配置完成则发送消息
     */
    @Override
    public void checkCustomizedInfo(Long workOrderId) {
        WorkOrder workOrder1 = this.getById(workOrderId);
        if (Objects.equals(workOrder1.getBomStatus(), 1) && Objects.equals(workOrder1.getTechnologyStatus(), 1)) {
            //推送消息给生产计划下发的负责人
            sendPlanInfo();
        }
    }

    /**
     * 给下发生产计划的人发消息
     */
    @Override
    public void sendPlanInfo() {
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        List<Long> userIds = userService.getUserIdsByRoleKey("design");
        DynamicDataSourceContextHolder.poll();
        WebSocketPush.byUsers(PushTypeEnum.MESSAGE, userIds, "您有新的工单待下发！", PushBusinessTypeEnum.NEW_WORK_ORDER.getType());
    }

    /**
     * 下发需要人工干预的工单
     */
    @Override
    @DSTransactional
    public void distribute(WorkOrderDto dto) {
        Assert.notEmpty(dto.getId(), "工单Id不能为空");
        Assert.notEmpty(dto.getProductionQuantity(), "待生产数量不能为空");
        Assert.notEmpty(dto.getStockWaitQuantity(), "待出库数量不能为空");

        WorkOrder workOrder = this.getById(dto.getId());
        Assert.notEmpty(workOrder, "查询不到该工单的信息");
        boolean update = this.update(q -> q
                .eq(WorkOrder::getId, dto.getId())
                .set(WorkOrder::getProductionQuantity, dto.getProductionQuantity())
                .set(WorkOrder::getStockWaitQuantity, dto.getStockWaitQuantity())
                .set(BasePo::getUpdateTime, new Date())
                .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
        );
        if (!update) {
            throw new ServiceException("操作失败");
        }
        //创建待出库数据
        if (ObjectUtil.isNotEmpty(dto.getStockWaitQuantity()) && dto.getStockWaitQuantity().compareTo(BigDecimal.ZERO) > 0) {
            StockWait stockWait = new StockWait();
            stockWait.setType(2);
            stockWait.setBusinessType(StockWaitType.WORD_ORDER_OUT.getDetailType());
            stockWait.setBusinessCode(workOrder.getCode());
            stockWait.setStatus(0);
            stockWaitService.save(stockWait);
            StockWaitDetails stockWaitDetails = new StockWaitDetails();
            stockWaitDetails.setStockWaitId(stockWait.getId());
            stockWaitDetails.setProductId(workOrder.getProductId());
            stockWaitDetails.setQuantity(dto.getStockWaitQuantity());
            stockWaitDetails.setReceiptQuantity(BigDecimal.ZERO);
            stockWaitDetails.setStatus(0);
            stockWaitDetailsService.save(stockWaitDetails);
        }
    }

    /**
     * 获取bom信息
     *
     * @return
     */
    @Override
    public List<BomDetail> getBomInfo(WorkOrder workOrder) {
        //获取产品信息
        ProductInfo productInfo = productInfoService.getById(workOrder.getProductId());
        if (ObjectUtil.isEmpty(productInfo)) {
            throw new ServiceException("查询不到产品信息 产品id->" + workOrder.getProductId());
        }

        //搜索BOM明细
        List<BomDetail> bomDetailList = new ArrayList<>();
        //非定制
        BomInfo bomInfo = bomInfoService.getOne(q -> q.eq(BomInfo::getProductId, workOrder.getProductId()).eq(BomInfo::getCurrentVersion, 1));
        if (ObjectUtil.isEmpty(bomInfo)) {
            throw new ServiceException("查询不到产品的BOM信息 产品名称->" + productInfo.getName());
        }
        bomDetailList = bomDetailService.list(q -> q.eq(BomDetail::getBomInfoId, bomInfo.getId()));
        if (ObjectUtil.isEmpty(bomDetailList)) {
            throw new ServiceException("查询不到产品BOM明细 产品名称->" + productInfo.getName());
        }

        //如果是定制就获取定制的BOM
        if ("1".equals(workOrder.getIsCustomized())) {
            //定制
            List<WorkOrderBom> workOrderBoms = workOrderBomService.list(q -> q.eq(WorkOrderBom::getWorkOrderId, workOrder.getId()));
            bomDetailList = BeanUtil.copyToList(workOrderBoms, BomDetail.class);
        }
        return bomDetailList;
    }

//    /**
//     * 冻结工单BOM
//     */
//    @DSTransactional
//    @Override
//    public void freezeMaterial(WorkOrder workOrderDto){
//        WorkOrder workOrder = this.getById(workOrderDto.getId());
//
//        //如果定制且BOM未定制直接跳出
//        if ("1".equals(workOrder.getIsCustomized())){
//            //如果BOM未定制
//            if (ObjectUtil.isEmpty(workOrder.getBomStatus())||workOrder.getBomStatus()!=1) {
//                return;
//            }
//        }
//        //如果待生产数量为空(需要如果干预)直接跳过
//        if (ObjectUtil.isEmpty(workOrder.getProductionQuantity())){
//            return;
//        }
//
//        //获取Bom明细
//        List<BomDetail> bomDetailList = getBomInfo(workOrder);
//
//        //根据BOM明细冻结库存
//        List<Long> productIds = bomDetailList.stream().map(BomDetail::getProductId).collect(Collectors.toList());
//        Map<Long, List<Stock>> productMap = stockService.mapKGroup(Stock::getProductId, q -> q.in(Stock::getProductId, productIds));
//        List<StockFrozen> stockFrozenList = new ArrayList<>();
//        for (BomDetail bomDetail : bomDetailList) {
//            Long productId = bomDetail.getProductId();
//            //计算总数量 需要的物料数量*工单生产数量
//            BigDecimal multiply = bomDetail.getQuantity().multiply(workOrder.getProductionQuantity());
//            //添加冻结库存
//            List<Stock> stocks = productMap.get(productId);
//            if (ObjectUtil.isEmpty(stocks)) {
//                throw new ServiceException("物料库存不存在");
//            }
//            //计算库存是否充足（可用库存 - 冻结库存 - 所需的数量）是否大等于0
//            BigDecimal quantity = stocks.stream().map(Stock::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
//            Map<Long, StockFrozen> stockFrozenMap = stockFrozenService.mapKEntity(StockFrozen::getProductId, q -> q.in(StockFrozen::getProductId, productIds));
//            StockFrozen stockFrozen = stockFrozenMap.get(bomDetail.getProductId());
//            if (ObjectUtil.isEmpty(stockFrozen)) {
//                stockFrozen = new StockFrozen();
//                stockFrozen.setProductId(bomDetail.getProductId());
//                stockFrozen.setFrozenQuantity(BigDecimal.ZERO);
//            }
//            BigDecimal subtract = quantity.subtract(stockFrozen.getFrozenQuantity()).subtract(multiply);
//            if (subtract.compareTo(BigDecimal.ZERO) < 0) {
//                throw new ServiceException("物料库存不足");
//            }
//            //存在库存
//            BigDecimal add = stockFrozen.getFrozenQuantity().add(multiply);
//            stockFrozen.setFrozenQuantity(add);
//
//            stockFrozenList.add(stockFrozen);
//        }
//        stockFrozenService.saveOrUpdateBatch(stockFrozenList);
//    }

    /**
     * 研发上传附件
     */
    @Override
    @DSTransactional
    public void research(WorkOrderDto dto) {
        WorkOrder workOrder = this.getById(dto.getId());
        Assert.notEmpty(workOrder, "查询不到工单信息！");
        //修改研发状态
        this.update(q -> q
                .eq(WorkOrder::getId, dto.getId())
                .set(ObjectUtil.isNotEmpty(dto.getResearchStatus()), WorkOrder::getResearchStatus, dto.getResearchStatus())
                .set(BasePo::getUpdateTime, new Date())
                .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
        );
        //修改附件列表
        ObsFileUtil.editFile(dto.getFileList(), dto.getId());

        //发消息
        if (ObjectUtil.isNotEmpty(workOrder.getResearchStatus()) && workOrder.getResearchStatus() == 0 && dto.getResearchStatus() == 1) {
            if ("1".equals(workOrder.getIsCustomized())) {
                //定制工单 推送消息给工单配置的负责人
                DynamicDataSourceContextHolder.push(SourceConstant.BASE);
                List<Long> userIds = userService.getUserIdsByRoleKey("design");
                DynamicDataSourceContextHolder.poll();
                WebSocketPush.byUsers(PushTypeEnum.MESSAGE, userIds, "您有新的定制工单待配置！", PushBusinessTypeEnum.NEW_WORK_ORDER.getType());
            } else if ("0".equals(workOrder.getIsCustomized())) {
                //非定制工单 推送消息给生产计划下发的负责人
                sendPlanInfo();
            }
        }
    }

}
