package cn.iocoder.yudao.module.pms.service.iotdevicematerial;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.pms.controller.admin.iotdevicematerial.vo.IotDeviceMaterialPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdevicematerial.vo.IotDeviceMaterialRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdevicematerial.vo.IotDeviceMaterialSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotlockstock.vo.IotLockStockPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotmaterial.vo.IotMaterialPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotsapstock.vo.IotSapStockPageReqVO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicematerial.IotDeviceMaterialDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotlockstock.IotLockStockDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotmaterial.IotMaterialDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotsapstock.IotSapStockDO;
import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotdevicematerial.IotDeviceMaterialMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotlockstock.IotLockStockMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotmaterial.IotMaterialMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotsapstock.IotSapStockMapper;
import cn.iocoder.yudao.module.pms.service.iotbom.IotBomService;
import cn.iocoder.yudao.module.system.api.dept.DeptSapOrgApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptSapOrgRespDTO;
import cn.iocoder.yudao.module.system.api.saporg.SapOrgApi;
import cn.iocoder.yudao.module.system.api.saporg.dto.SapOrgRespDTO;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import com.google.common.collect.ImmutableMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

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

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_DEVICE_MATERIAL_NOT_EXISTS;


/**
 * PMS 设备BOM物料关联 Service 实现类
 *
 * @author ruiqi
 */
@Service
@Validated
public class IotDeviceMaterialServiceImpl implements IotDeviceMaterialService {

    @Resource
    private IotDeviceMaterialMapper iotDeviceMaterialMapper;
    @Resource
    private IotDeviceMapper iotDeviceMapper;
    @Resource
    private IotBomService iotBomService;
    @Autowired
    private DeptService deptService;
    @Autowired
    private DeptSapOrgApi deptSapOrgApi;
    @Autowired
    private SapOrgApi sapOrgApi;
    @Autowired
    private IotLockStockMapper iotLockStockMapper;
    @Autowired
    private IotSapStockMapper iotSapStockMapper;
    @Autowired
    private IotMaterialMapper iotMaterialMapper;

    @Override
    public Long createIotDeviceMaterial(IotDeviceMaterialSaveReqVO createReqVO) {
        // 插入
        IotDeviceMaterialDO iotDeviceMaterial = BeanUtils.toBean(createReqVO, IotDeviceMaterialDO.class);
        iotDeviceMaterialMapper.insert(iotDeviceMaterial);
        // 设置 设备BOM所属 设备的 bom_sync_status = 1
        iotDeviceMapper.updateBomSyncStatus(iotDeviceMaterial.getDeviceId());
        // 返回
        return iotDeviceMaterial.getId();
    }

    @Override
    public void updateIotDeviceMaterial(IotDeviceMaterialSaveReqVO updateReqVO) {
        // 校验存在
        validateIotDeviceMaterialExists(updateReqVO.getId());
        // 更新
        IotDeviceMaterialDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceMaterialDO.class);
        iotDeviceMaterialMapper.updateById(updateObj);
        // 修改当前设备的 bom_sync_status = 1 不再接收分类BOM的同步信息
        iotDeviceMapper.updateBomSyncStatus(updateReqVO.getDeviceId());
    }

    @Override
    public void deleteIotDeviceMaterial(Long id) {
        // 校验存在
        validateIotDeviceMaterialExists(id);
        // 删除
        iotDeviceMaterialMapper.deleteById(id);
    }

    private void validateIotDeviceMaterialExists(Long id) {
        if (iotDeviceMaterialMapper.selectById(id) == null) {
            throw exception(IOT_DEVICE_MATERIAL_NOT_EXISTS);
        }
    }

    @Override
    public IotDeviceMaterialDO getIotDeviceMaterial(Long id) {
        return iotDeviceMaterialMapper.selectById(id);
    }

    @Override
    public PageResult<IotDeviceMaterialDO> getIotDeviceMaterialPage(IotDeviceMaterialPageReqVO pageReqVO) {
        return iotDeviceMaterialMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<IotDeviceMaterialDO> getDeviceMaterialsByBomId(IotDeviceMaterialPageReqVO reqVO) {
        return iotDeviceMaterialMapper.getMaterialsByBomId(reqVO);
    }

    @Override
    public List<IotDeviceMaterialDO> getIotDeviceMaterials(IotDeviceMaterialSaveReqVO saveReqVO) {
        return iotDeviceMaterialMapper.selectList(saveReqVO);
    }

    @Override
    public List<IotDeviceMaterialDO> deviceBomMaterials(IotDeviceMaterialPageReqVO reqVO) {
        return iotDeviceMaterialMapper.selectList(reqVO);
    }

    @Override
    public void deleteDeviceMaterial(Long bomNodeId, String code) {
        if (ObjUtil.isEmpty(bomNodeId) || StrUtil.isBlank(code)) {
            throw exception(IOT_DEVICE_MATERIAL_NOT_EXISTS);
        }
        // 查询指定 bomNodeId code 的记录是否存在
        IotDeviceMaterialSaveReqVO reqVO = new IotDeviceMaterialSaveReqVO();
        reqVO.setBomNodeId(bomNodeId);
        reqVO.setCode(code);
        List<IotDeviceMaterialDO> deviceMaterials = iotDeviceMaterialMapper.selectList(reqVO);
        if (CollUtil.isEmpty(deviceMaterials)) {
            throw exception(IOT_DEVICE_MATERIAL_NOT_EXISTS);
        }
        int count = iotDeviceMaterialMapper.deleteByMap(ImmutableMap.of(
                "bom_node_id", bomNodeId,
                "code", code
        ));
        // 设置关联设备的 bom_sync_status = 1
        iotDeviceMapper.updateBomSyncStatus(deviceMaterials.get(0).getDeviceId());
    }

    @Override
    public Long addMaterials(List<IotDeviceMaterialSaveReqVO> materials) {
        Long resultCount = 0L;
        if (CollUtil.isEmpty(materials)) {
            throw exception(IOT_DEVICE_MATERIAL_NOT_EXISTS);
        }
        Long bomNodeId = materials.get(0).getBomNodeId();
        Long deviceId = materials.get(0).getDeviceId();

        if (ObjUtil.isEmpty(bomNodeId)) {
            throw exception(IOT_DEVICE_MATERIAL_NOT_EXISTS);
        }
        // 先查询当前BOM节点下所有物料 与要添加的物料匹配 筛选出在数据库中不存在的物料 插入到数据库
        IotDeviceMaterialPageReqVO reqVO = new IotDeviceMaterialPageReqVO();
        reqVO.setBomNodeId(bomNodeId);
        List<IotDeviceMaterialDO> existMaterials = iotDeviceMaterialMapper.selectList(reqVO);
        List<IotDeviceMaterialSaveReqVO> newMaterials = new ArrayList<>();
        if (CollUtil.isNotEmpty(existMaterials)) {
            // 提取已存在的物料编码集合
            Set<String> existCodes = existMaterials.stream()
                    .map(IotDeviceMaterialDO::getCode)    // 获取DO对象的code字段
                    .filter(Objects::nonNull)             // 过滤可能的null值
                    .collect(Collectors.toSet());
            // 筛选出不存在的物料
            newMaterials = materials.stream()
                    .filter(material -> material.getCode() != null)     // 确保物料编码不为null
                    .filter(material -> !existCodes.contains(material.getCode())) // 检查是否不存在
                    .collect(Collectors.toList());
        } else {
            newMaterials = materials;
        }
        List<IotDeviceMaterialDO> tobeAddedMaterials = new ArrayList<>();
        if (CollUtil.isNotEmpty(newMaterials)) {
            newMaterials.forEach(material -> {
                IotDeviceMaterialDO iotCommonBomMaterial = BeanUtils.toBean(material, IotDeviceMaterialDO.class);
                tobeAddedMaterials.add(iotCommonBomMaterial);
            });
            iotDeviceMaterialMapper.insertBatch(tobeAddedMaterials);
            resultCount = Long.valueOf(tobeAddedMaterials.size());
        }
        // 设置设备bom同步状态 sync_bom_status = 1
        iotDeviceMapper.updateBomSyncStatus(deviceId);
        return resultCount;
    }

    @Override
    public Map<String, List<IotDeviceMaterialRespVO>> bomMaterials(Set<Long> deptIds, Set<Long> deviceIds, Set<Long> bomNodeIds) {
        // key设备id-bomid    value保养项关联的物料列表
        Map<String, List<IotDeviceMaterialRespVO>> deviceBomMaterialPair = new HashMap<>();
        // 查询每个 维修/保养 项绑定的物料
        IotDeviceMaterialPageReqVO reqVO = new IotDeviceMaterialPageReqVO();
        reqVO.setDeviceIds(new ArrayList<>(deviceIds));
        reqVO.setBomNodeIds(new ArrayList<>(bomNodeIds));
        List<IotDeviceMaterialDO> deviceBomMaterials = iotDeviceMaterialMapper.selectList(reqVO);
        Set<String> materialCodes = new HashSet<>();
        if (CollUtil.isNotEmpty(deviceBomMaterials)) {
            deviceBomMaterials.forEach(material -> {
                // 设置物料编码组合
                materialCodes.add(material.getCode());
            });
            // 查询每个保养项上 关联的物料数量 关联查询并直接绑定 设备所属部门的SAP组织成本中心的库存物料 与 保养项关联的物料 匹配
            // 物料按照优化级绑定 本地库存 > sap库存 > sap主数据
            /// Set<Long> deptIds = new HashSet<>();
            Set<Long> allDeptIds = new HashSet<>();
            List<IotLockStockDO> lockStocks = new ArrayList<>();
            List<IotSapStockDO> sapStocks = new ArrayList<>();
            // key物料编码  value:本地库存对象
            Map<String, IotLockStockDO> lockStockPair = new HashMap<>();
            // key物料编码  value:sap库存对象
            Map<String, IotSapStockDO> sapStockPair = new HashMap<>();
            // key物料编码  value:sap主数据对象
            Map<String, IotMaterialDO> sapMasterDataPair = new HashMap<>();

            Map<Long, String> factoryPair = new HashMap<>();
            // key成本中心id    value成本中心名称
            Map<Long, String> costCenterPair = new HashMap<>();
            // key库存地点id    value库存地点名称
            Map<Long, String> storageLocationPair = new HashMap<>();
            if (CollUtil.isNotEmpty(deptIds)) {
                // 查询每个部门下的所有子部门 将所有 部门id设置到 相同集合
                if (CollUtil.isNotEmpty(deptIds)) {
                    deptIds.forEach(deptId -> {
                        Set<Long> tempDeptIds = new HashSet<>();
                        tempDeptIds = deptService.getChildDeptIdListFromCache(deptId);
                        tempDeptIds.add(deptId);
                        allDeptIds.addAll(tempDeptIds);
                    });
                }
                // 查询所有部门关联的SAP工厂 成本中心 库存地点 列表
                List<DeptSapOrgRespDTO> sapOrgS = deptSapOrgApi.getSapOrgListByDeptIds(allDeptIds);
                Set<Long> factoryIds = new HashSet<>();         // 工厂 id 集合
                Set<Long> costCenterIds = new HashSet<>();      // 成本中心id集合
                Set<Long> storageLocationIds = new HashSet<>(); // 库存地点id集合
                Set<Long> sapOrgIds = new HashSet<>();          // 查询本地库存 工厂id-成本中心id
                Set<Long> factoryStorageIds = new HashSet<>();          // 查询sap库存 工厂id-库存地点id
                if (CollUtil.isNotEmpty(sapOrgS)) {
                    sapOrgS.forEach(org -> {
                        if (ObjUtil.isNotEmpty(org.getFactoryId())) {
                            factoryIds.add(org.getFactoryId());
                        }
                        if (ObjUtil.isNotEmpty(org.getCostCenterId())) {
                            costCenterIds.add(org.getCostCenterId());
                        }
                        if (ObjUtil.isNotEmpty(org.getStockLocationId())) {
                            storageLocationIds.add(org.getStockLocationId());
                        }
                    });
                    if (CollUtil.isNotEmpty(factoryIds)) {
                        sapOrgIds.addAll(factoryIds);
                        factoryStorageIds.addAll(factoryIds);
                    }
                    if (CollUtil.isNotEmpty(costCenterIds)) {
                        sapOrgIds.addAll(costCenterIds);
                    }
                    if (CollUtil.isNotEmpty(storageLocationIds)) {
                        factoryStorageIds.addAll(storageLocationIds);
                    }
                }
                // 查询所有sap 工厂 成本中心下的指定物料的库存信息
                if (CollUtil.isNotEmpty(factoryIds) && CollUtil.isNotEmpty(costCenterIds) && CollUtil.isNotEmpty(materialCodes)) {
                    IotLockStockPageReqVO stockReqVO = new IotLockStockPageReqVO();
                    stockReqVO.setFactoryIds(factoryIds);
                    stockReqVO.setCostCenterIds(costCenterIds);
                    stockReqVO.setMaterialCodes(materialCodes);
                    lockStocks = iotLockStockMapper.selectList(stockReqVO);
                    // 查询各工厂 成本中心的名称
                    List<SapOrgRespDTO> sapOrganizations = sapOrgApi.getSapOrgList(sapOrgIds);
                    if (CollUtil.isNotEmpty(sapOrganizations)) {
                        sapOrganizations.forEach(org -> {
                            // 工厂 名称集合
                            if (ObjUtil.isNotEmpty(org.getType()) && 1==org.getType()) {
                                factoryPair.put(org.getId(), org.getFactoryName());
                            }
                            // 成本中心名称集合
                            if (ObjUtil.isNotEmpty(org.getType()) && 2==org.getType()) {
                                costCenterPair.put(org.getId(), org.getCostCenterName());
                            }
                        });
                    }
                    if (CollUtil.isNotEmpty(lockStocks)) {
                        lockStocks.forEach(stock -> {
                            lockStockPair.put(stock.getMaterialCode(), stock);
                        });
                    }
                }
                // 查询SAP库存
                if (CollUtil.isNotEmpty(factoryIds) && CollUtil.isNotEmpty(storageLocationIds) && CollUtil.isNotEmpty(materialCodes)) {
                    IotSapStockPageReqVO sapStockReqVO = new IotSapStockPageReqVO();
                    sapStockReqVO.setFactoryIds(factoryIds);
                    sapStockReqVO.setStorageLocationIds(storageLocationIds);
                    sapStockReqVO.setMaterialCodes(materialCodes);
                    sapStocks = iotSapStockMapper.selectList(sapStockReqVO);
                    // 查询各工厂 库存地点的名称
                    List<SapOrgRespDTO> sapOrganizations = sapOrgApi.getSapOrgList(factoryStorageIds);
                    if (CollUtil.isNotEmpty(sapOrganizations)) {
                        sapOrganizations.forEach(org -> {
                            // 工厂 名称集合
                            if (ObjUtil.isNotEmpty(org.getType()) && 1==org.getType()) {
                                factoryPair.put(org.getId(), org.getFactoryName());
                            }
                            // 库存地点名称集合
                            if (ObjUtil.isNotEmpty(org.getType()) && 3==org.getType()) {
                                storageLocationPair.put(org.getId(), org.getStorageLocationName());
                            }
                        });
                    }
                    if (CollUtil.isNotEmpty(sapStocks)) {
                        sapStocks.forEach(stock -> {
                            sapStockPair.put(stock.getMaterialCode(), stock);
                        });
                    }
                }
                // 查询SAP主数据
                if (CollUtil.isNotEmpty(materialCodes)) {
                    IotMaterialPageReqVO materialReqVO = new IotMaterialPageReqVO();
                    materialReqVO.setCodes(new ArrayList<>(materialCodes));
                    List<IotMaterialDO> materials = iotMaterialMapper.selectList(materialReqVO, null);
                    if (CollUtil.isNotEmpty(materials)) {
                        materials.forEach(material -> {
                            sapMasterDataPair.put(material.getCode(), material);
                        });
                    }
                }

                // 设置保养项关联的物料列表
                List<IotDeviceMaterialRespVO> deviceMaterials = deviceBomMaterials.stream()
                        .map(source -> {
                            IotDeviceMaterialRespVO target = new IotDeviceMaterialRespVO();
                            BeanUtil.copyProperties(source, target, "id");
                            return target;
                        })
                        .collect(Collectors.toList());
                // 绑定保养项关联的物料 优先级 本地库存 > sap库存 > sap主数据
                deviceMaterials.forEach(material -> {
                    if (lockStockPair.containsKey(material.getCode())) {
                        // 本地库存物料
                        IotLockStockDO tempLockStock = lockStockPair.get(material.getCode());
                        material.setStockQuantity(tempLockStock.getQuantity());
                        material.setUnit(tempLockStock.getUnit());
                        material.setUnitPrice(tempLockStock.getUnitPrice());
                        material.setFactoryId(tempLockStock.getFactoryId());
                        material.setCostCenterId(tempLockStock.getCostCenterId());
                        if (factoryPair.containsKey(tempLockStock.getFactoryId())) {
                            material.setFactory(factoryPair.get(tempLockStock.getFactoryId()));
                        }
                        if (costCenterPair.containsKey(tempLockStock.getCostCenterId())) {
                            material.setCostCenter(costCenterPair.get(tempLockStock.getCostCenterId()));
                        }
                        material.setMaterialSource("本地库存");
                    } else if (sapStockPair.containsKey(material.getCode())) {
                        // sap库存物料
                        IotSapStockDO tempSapStock = sapStockPair.get(material.getCode());
                        material.setStockQuantity(tempSapStock.getQuantity());
                        material.setUnit(tempSapStock.getUnit());
                        material.setUnitPrice(tempSapStock.getUnitPrice());
                        material.setFactoryId(tempSapStock.getFactoryId());
                        material.setStorageLocationId(tempSapStock.getStorageLocationId());
                        if (factoryPair.containsKey(tempSapStock.getFactoryId())) {
                            material.setFactory(factoryPair.get(tempSapStock.getFactoryId()));
                        }
                        if (storageLocationPair.containsKey(tempSapStock.getStorageLocationId())) {
                            material.setStorageLocation(storageLocationPair.get(tempSapStock.getStorageLocationId()));
                        }
                        material.setMaterialSource("sap库存");
                    } else if (sapMasterDataPair.containsKey(material.getCode())) {
                        // sap主数据
                        IotMaterialDO tempMaterial = sapMasterDataPair.get(material.getCode());
                        material.setStockQuantity(BigDecimal.ZERO);
                        material.setUnit(tempMaterial.getUnit());
                        material.setUnitPrice(BigDecimal.ZERO);
                        material.setFactoryId(null);
                        material.setStorageLocationId(null);
                        material.setCostCenterId(null);
                        material.setFactory(StrUtil.EMPTY);
                        material.setStorageLocation(StrUtil.EMPTY);
                        material.setCostCenter(StrUtil.EMPTY);
                        material.setMaterialSource("sap主数据");
                    }
                });
                deviceMaterials.forEach(bomMaterial -> {
                    String uniqueKey = StrUtil.join("-", bomMaterial.getDeviceId(), bomMaterial.getBomNodeId());
                    if (deviceBomMaterialPair.containsKey(uniqueKey)) {
                        List<IotDeviceMaterialRespVO> tempBomMaterials = deviceBomMaterialPair.get(uniqueKey);
                        tempBomMaterials.add(bomMaterial);
                        deviceBomMaterialPair.put(uniqueKey, tempBomMaterials);
                    } else {
                        List<IotDeviceMaterialRespVO> tempBomMaterials = new ArrayList<>();
                        tempBomMaterials.add(bomMaterial);
                        deviceBomMaterialPair.put(uniqueKey, tempBomMaterials);
                    }
                });
            }
        }
        return deviceBomMaterialPair;
    }

}