package com.zt.plat.module.base.service.base;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationPageReqVO;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationRespVO;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationSaveReqVO;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationSimplePageReqVO;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationSimpleRespVO;
import com.zt.plat.module.base.dal.dao.materialclasses.MaterialClassesMapper;
import com.zt.plat.module.base.dal.dao.materialhasclasses.MaterialHasClassesMapper;
import com.zt.plat.module.base.dal.dao.materialhasproperties.MaterialHasPropertiesMapper;
import com.zt.plat.module.base.dal.dao.materialproperties.MaterialPropertiesMapper;
import com.zt.plat.module.base.dal.dataobject.base.MaterialInfomationDO;
import com.zt.plat.module.base.dal.dataobject.materialclasses.MaterialClassesDO;
import com.zt.plat.module.base.dal.dataobject.materialhasclasses.MaterialHasClassesDO;
import com.zt.plat.module.base.dal.dataobject.materialhasproperties.MaterialHasPropertiesDO;
import com.zt.plat.module.base.dal.dataobject.materialproperties.MaterialPropertiesDO;
import com.zt.plat.module.base.dal.mysql.base.MaterialInfomationMapper;
import com.zt.plat.module.base.service.masterdatasync.support.MasterDataPropertyDefinition;
import com.zt.plat.module.erp.api.ErpExternalApi;
import com.zt.plat.module.erp.api.dto.ErpProductiveVersionReqDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.base.enums.ErrorCodeConstants.MATERIAL_CLASSES_LEVEL3_REQUIRED_FOR_MATERIAL;
import static com.zt.plat.module.base.enums.ErrorCodeConstants.MATERIAL_CLASSES_NOT_EXISTS;
import static com.zt.plat.module.base.enums.ErrorCodeConstants.MATERIAL_INFOMATION_NOT_EXISTS;

/**
 * 物料信息 Service 实现类
 *
 * @author 后台管理
 */
@Service
@Validated
public class MaterialInfomationServiceImpl implements MaterialInfomationService {

	@Resource
	private MaterialInfomationMapper materialInfomationMapper;

	@Resource
	private ErpExternalApi erpExternalApi;

	@Resource
	private MaterialHasClassesMapper materialHasClassesMapper;

	@Resource
	private MaterialClassesMapper materialClassesMapper;

	@Resource
	private MaterialHasPropertiesMapper materialHasPropertiesMapper;

	@Resource
	private MaterialPropertiesMapper materialPropertiesMapper;

	@Override
	@Transactional(rollbackFor = Exception.class)
	public MaterialInfomationRespVO createMaterialInfomation(MaterialInfomationSaveReqVO createReqVO) {
		validateMaterialClassForBinding(createReqVO.getClassesId());
		MaterialInfomationDO materialInfomation = BeanUtils.toBean(createReqVO, MaterialInfomationDO.class);
		materialInfomationMapper.insert(materialInfomation);
		bindMaterialClassIfAbsent(materialInfomation.getId(), createReqVO.getClassesId());
		return CollUtil.getFirst(buildRespList(Collections.singletonList(materialInfomation)));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public String createMaterialInfomationQuickly(MaterialInfomationSaveReqVO createReqVO) {
		validateMaterialClassForBinding(createReqVO.getClassesId());
		// 校验同一小类下是否已存在相同编码和名称的物料，若存在则不再创建
		boolean exists = existsMaterialInClass(createReqVO.getCode(), createReqVO.getName(), createReqVO.getClassesId());
		if (exists) {
			return "该小类下已存在相同编码和名称的物料，不再重复添加";
		}
		MaterialInfomationRespVO respVO = createMaterialInfomation(createReqVO);
		if (respVO == null || respVO.getId() == null) {
			return "物料创建失败";
		}
		return "物料创建成功，ID=" + respVO.getId();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateMaterialInfomation(MaterialInfomationSaveReqVO updateReqVO) {
		validateMaterialInfomationExists(updateReqVO.getId());
		validateMaterialClassForBinding(updateReqVO.getClassesId());
		MaterialInfomationDO updateObj = BeanUtils.toBean(updateReqVO, MaterialInfomationDO.class);
		materialInfomationMapper.updateById(updateObj);
		materialHasClassesMapper.delete(new LambdaUpdateWrapper<MaterialHasClassesDO>()
				.eq(MaterialHasClassesDO::getInfomationId, updateReqVO.getId()));
		bindMaterialClassIfAbsent(updateReqVO.getId(), updateReqVO.getClassesId());
	}

	/**
	 * 绑定物料与分类关系（若已存在则跳过），避免唯一索引冲突
	 */
	private void bindMaterialClassIfAbsent(Long materialId, Long classesId) {
		if (materialId == null || classesId == null) {
			return;
		}
		Long exists = materialHasClassesMapper.selectCount(new LambdaQueryWrapperX<MaterialHasClassesDO>()
				.eq(MaterialHasClassesDO::getInfomationId, materialId)
				.eq(MaterialHasClassesDO::getClassesId, classesId));
		if (exists != null && exists > 0) {
			return;
		}
		MaterialHasClassesDO relation = MaterialHasClassesDO.builder()
				.classesId(classesId)
				.infomationId(materialId)
				.build();
		materialHasClassesMapper.insert(relation);
	}

	/**
	 * 判断在指定小类下，是否已存在相同编码和名称的物料
	 */
	private boolean existsMaterialInClass(String code, String name, Long classesId) {
		// 先根据编码和名称查出物料ID列表
		List<MaterialInfomationDO> infoList = materialInfomationMapper.selectList(
				new LambdaQueryWrapperX<MaterialInfomationDO>()
						.eq(MaterialInfomationDO::getCode, code)
						.eq(MaterialInfomationDO::getName, name));
		if (CollUtil.isEmpty(infoList)) {
			return false;
		}
		List<Long> infoIds = infoList.stream()
				.map(MaterialInfomationDO::getId)
				.filter(Objects::nonNull)
				.collect(Collectors.toList());
		if (CollUtil.isEmpty(infoIds)) {
			return false;
		}
		Long count = materialHasClassesMapper.selectCount(
				new LambdaQueryWrapperX<MaterialHasClassesDO>()
						.in(MaterialHasClassesDO::getInfomationId, infoIds)
						.eq(MaterialHasClassesDO::getClassesId, classesId));
		return count != null && count > 0;
	}

	@Override
	public void deleteMaterialInfomation(Long id) {
		validateMaterialInfomationExists(id);
		materialInfomationMapper.deleteById(id);
		materialHasClassesMapper.delete(new LambdaUpdateWrapper<MaterialHasClassesDO>()
				.eq(MaterialHasClassesDO::getInfomationId, id));
	}

	@Override
	public void deleteMaterialInfomationListByIds(List<Long> ids) {
		validateMaterialInfomationExists(ids);
		materialInfomationMapper.deleteByIds(ids);
	}

	private void validateMaterialInfomationExists(List<Long> ids) {
		List<MaterialInfomationDO> list = materialInfomationMapper.selectByIds(ids);
		if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
			throw exception(MATERIAL_INFOMATION_NOT_EXISTS);
		}
	}

	private void validateMaterialInfomationExists(Long id) {
		if (materialInfomationMapper.selectById(id) == null) {
			throw exception(MATERIAL_INFOMATION_NOT_EXISTS);
		}
	}

	private void validateMaterialClassForBinding(Long classesId) {
		if (classesId == null) {
			throw exception(MATERIAL_CLASSES_LEVEL3_REQUIRED_FOR_MATERIAL);
		}
		MaterialClassesDO materialClasses = materialClassesMapper.selectById(classesId);
		if (materialClasses == null) {
			throw exception(MATERIAL_CLASSES_NOT_EXISTS);
		}
		if (!isThirdLevelClass(materialClasses)) {
			throw exception(MATERIAL_CLASSES_LEVEL3_REQUIRED_FOR_MATERIAL);
		}
	}

	private boolean isThirdLevelClass(MaterialClassesDO materialClasses) {
		Long level = materialClasses.getLevel();
		if (Objects.equals(level, 3L)) {
			return true;
		}
		if (level != null && level > 3L) {
			return false;
		}
		Long depth = resolveClassDepth(materialClasses);
		return depth != null && depth == 3L;
	}

	private Long resolveClassDepth(MaterialClassesDO materialClasses) {
		long depth = 1L;
		Set<Long> visited = new HashSet<>();
		if (materialClasses.getId() != null) {
			visited.add(materialClasses.getId());
		}
		Long parentId = materialClasses.getParentId();
		while (parentId != null) {
			if (!visited.add(parentId)) {
				return null;
			}
			MaterialClassesDO parent = materialClassesMapper.selectById(parentId);
			if (parent == null) {
				return null;
			}
			depth++;
			if (depth > 3L) {
				return depth;
			}
			parentId = parent.getParentId();
		}
		return depth;
	}

	@Override
	public MaterialInfomationRespVO getMaterialInfomation(Long id) {
		MaterialInfomationDO info = materialInfomationMapper.selectById(id);
		if (info == null) {
			return null;
		}
		MaterialHasClassesDO relation = materialHasClassesMapper.selectFirstOne(MaterialHasClassesDO::getInfomationId, id);
		if (relation != null) {
			info.setClassesId(relation.getClassesId());
		}
		return CollUtil.getFirst(buildRespList(Collections.singletonList(info)));
	}

	@Override
	public List<MaterialInfomationRespVO> getMaterialInfomationListByIds(Collection<Long> ids) {
		if (CollUtil.isEmpty(ids)) {
			return Collections.emptyList();
		}
		List<MaterialInfomationDO> infoList = materialInfomationMapper.selectBatchIds(ids);
		if (CollUtil.isEmpty(infoList)) {
			return Collections.emptyList();
		}
		List<MaterialHasClassesDO> relationList = materialHasClassesMapper.selectList(MaterialHasClassesDO::getInfomationId, ids);
		Map<Long, Long> infoClassMap = relationList.stream()
				.filter(item -> item.getInfomationId() != null)
				.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId, MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));
		infoList.forEach(item -> item.setClassesId(infoClassMap.get(item.getId())));
		return buildRespList(infoList);
	}

	@Override
	public PageResult<MaterialInfomationRespVO> getMaterialInfomationPage(MaterialInfomationPageReqVO pageReqVO) {
		List<Long> infomationIds = null;
		List<MaterialHasClassesDO> relationList = Collections.emptyList();
		if (pageReqVO.getClassesId() != null) {
			relationList = materialHasClassesMapper.selectList(MaterialHasClassesDO::getClassesId, pageReqVO.getClassesId());
			if (CollUtil.isEmpty(relationList)) {
				return PageResult.empty();
			}
			infomationIds = relationList.stream()
					.map(MaterialHasClassesDO::getInfomationId)
					.filter(Objects::nonNull)
					.distinct()
					.collect(Collectors.toList());
		}

		// 如果搜索物料编码，需要同时搜索中铜编码和中铝编码（作为物料属性存储）
		if (StrUtil.isNotBlank(pageReqVO.getCode())) {
			// 优化：将分类筛选结果传入，在数据库层面就完成筛选，减少数据传输
			List<Long> codeMatchedIds = queryMaterialIdsByCodeOrAttribute(pageReqVO.getCode(), infomationIds);
			if (CollUtil.isEmpty(codeMatchedIds)) {
				// 按编码查询无结果，直接返回空（无论是否有分类筛选）
				return PageResult.empty();
			}
			// 直接使用编码查询结果（已包含分类筛选）
			infomationIds = codeMatchedIds;
		}

		PageResult<MaterialInfomationDO> pageResult = materialInfomationMapper.selectPage(pageReqVO, infomationIds);
		if (CollUtil.isEmpty(pageResult.getList())) {
			return PageResult.empty(pageResult.getTotal());
		}

		List<Long> currentInfoIds = pageResult.getList().stream()
				.map(MaterialInfomationDO::getId)
				.collect(Collectors.toList());

		if (pageReqVO.getClassesId() == null) {
			relationList = materialHasClassesMapper.selectList(MaterialHasClassesDO::getInfomationId, currentInfoIds);
		}

		Map<Long, Long> infoClassMap = relationList.stream()
				.filter(item -> item.getInfomationId() != null)
				.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId, MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));

		pageResult.getList().forEach(item -> item.setClassesId(infoClassMap.get(item.getId())));
		List<MaterialInfomationRespVO> respList = buildRespList(pageResult.getList());
		return new PageResult<>(respList, pageResult.getTotal());
	}

	/**
	 * 根据物料编码或属性编码（中铜编码、中铝编码）查询物料ID列表
	 * 优化版本：减少数据库查询次数，提升性能
	 *
	 * @param code 编码关键字（支持模糊查询）
	 * @param classIdFilter 分类筛选的物料ID列表（可为null）
	 * @return 匹配的物料ID列表
	 */
	private List<Long> queryMaterialIdsByCodeOrAttribute(String code, List<Long> classIdFilter) {
		Set<Long> materialIds = new HashSet<>();

		// 1. 查询物料表的 code 字段
		List<MaterialInfomationDO> directMatches = materialInfomationMapper.selectList(
				new LambdaQueryWrapperX<MaterialInfomationDO>()
						.like(MaterialInfomationDO::getCode, code)
						.in(Objects.nonNull(classIdFilter), MaterialInfomationDO::getId, classIdFilter)
		);
		if (CollUtil.isNotEmpty(directMatches)) {
			materialIds.addAll(directMatches.stream()
					.map(MaterialInfomationDO::getId)
					.filter(Objects::nonNull)
					.collect(Collectors.toSet()));
		}

		// 2. 查询中铜编码和中铝编码属性（使用缓存的属性ID）
		List<Long> propertyIds = getCachedCodePropertyIds();
		if (CollUtil.isNotEmpty(propertyIds)) {
			// 查询属性值表中匹配的物料
			List<MaterialHasPropertiesDO> hasProperties = materialHasPropertiesMapper.selectList(
					new LambdaQueryWrapperX<MaterialHasPropertiesDO>()
							.in(MaterialHasPropertiesDO::getPropertiesId, propertyIds)
							.like(MaterialHasPropertiesDO::getValue, code)
							.in(Objects.nonNull(classIdFilter),MaterialHasPropertiesDO::getInfomationId, classIdFilter)
			);

			if (CollUtil.isNotEmpty(hasProperties)) {
				materialIds.addAll(hasProperties.stream()
						.map(MaterialHasPropertiesDO::getInfomationId)
						.filter(Objects::nonNull)
						.collect(Collectors.toSet()));
			}
		}

		return new ArrayList<>(materialIds);
	}

	/**
	 * 获取中铜编码和中铝编码的属性ID（使用本地缓存避免重复查询）
	 */
	private static volatile List<Long> cachedCodePropertyIds = null;

	private List<Long> getCachedCodePropertyIds() {
		if (cachedCodePropertyIds == null) {
			synchronized (MaterialInfomationServiceImpl.class) {
				if (cachedCodePropertyIds == null) {
					List<MaterialPropertiesDO> properties = materialPropertiesMapper.selectList(
							new LambdaQueryWrapperX<MaterialPropertiesDO>()
									.in(MaterialPropertiesDO::getCode, Arrays.asList(
											MasterDataPropertyDefinition.CHALCO_CODE.getCode(),
											MasterDataPropertyDefinition.ZHONGTONG_CODE.getCode()))
					);
					if (CollUtil.isNotEmpty(properties)) {
						cachedCodePropertyIds = properties.stream()
								.map(MaterialPropertiesDO::getId)
								.filter(Objects::nonNull)
								.collect(Collectors.toList());
					} else {
						cachedCodePropertyIds = Collections.emptyList();
					}
				}
			}
		}
		return cachedCodePropertyIds;
	}

	@Override
	public String getOneTest() {
		ErpProductiveVersionReqDTO reqDTO = new ErpProductiveVersionReqDTO();
		reqDTO.setFactoryNumber("5020");
		reqDTO.setMaterialNumber("224814");
		CommonResult<String> erpProductiveVersion = erpExternalApi.getErpProductiveVersionByFM(reqDTO);
		return erpProductiveVersion.getData();
	}

	@Override
	public PageResult<MaterialInfomationSimpleRespVO> getMaterialInfomationSimplePage(MaterialInfomationSimplePageReqVO pageReqVO) {
		LambdaQueryWrapperX<MaterialInfomationDO> wrapper = new LambdaQueryWrapperX<>();
		if (StrUtil.isNotBlank(pageReqVO.getKeyword())) {
			wrapper.and(w -> w.like(MaterialInfomationDO::getCode, pageReqVO.getKeyword())
					.or().like(MaterialInfomationDO::getName, pageReqVO.getKeyword()));
		}

		Map<Long, Long> infoClassMap = Collections.emptyMap();
		if (pageReqVO.getClassesId() != null) {
			List<MaterialHasClassesDO> relations = materialHasClassesMapper.selectList(MaterialHasClassesDO::getClassesId, pageReqVO.getClassesId());
			if (CollUtil.isEmpty(relations)) {
				return new PageResult<>(Collections.emptyList(), 0L);
			}
			List<Long> infoIds = relations.stream()
					.map(MaterialHasClassesDO::getInfomationId)
					.filter(Objects::nonNull)
					.distinct()
					.toList();
			if (CollUtil.isEmpty(infoIds)) {
				return new PageResult<>(Collections.emptyList(), 0L);
			}
			wrapper.in(MaterialInfomationDO::getId, infoIds);
			infoClassMap = relations.stream()
					.filter(item -> item.getInfomationId() != null)
					.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId,
							MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));
		}

		wrapper.orderByAsc(MaterialInfomationDO::getCode);

		PageResult<MaterialInfomationDO> pageResult = materialInfomationMapper.selectSimplePage(pageReqVO, wrapper);
		if (CollUtil.isEmpty(pageResult.getList())) {
			return new PageResult<>(Collections.emptyList(), pageResult.getTotal());
		}

		if (pageReqVO.getClassesId() == null) {
			List<Long> infoIds = pageResult.getList().stream().map(MaterialInfomationDO::getId).toList();
			List<MaterialHasClassesDO> relations = materialHasClassesMapper.selectList(MaterialHasClassesDO::getInfomationId, infoIds);
			infoClassMap = relations.stream()
					.filter(item -> item.getInfomationId() != null)
					.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId,
							MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));
		}

		Map<Long, MaterialPropertiesAggregate> propertyAggregateMap = buildPropertyAggregates(
				pageResult.getList().stream().map(MaterialInfomationDO::getId).collect(Collectors.toList()));

		Map<Long, Long> finalInfoClassMap = infoClassMap;
		List<MaterialInfomationSimpleRespVO> respList = pageResult.getList().stream().map(item -> {
			MaterialInfomationSimpleRespVO vo = new MaterialInfomationSimpleRespVO();
			Map<String, Object> flatMap = new LinkedHashMap<>();
			flatMap.put("id", item.getId());
			flatMap.put("code", item.getCode());
			flatMap.put("name", item.getName());
			flatMap.put("classesId", finalInfoClassMap.get(item.getId()));
			flatMap.put("remark", item.getRemark());

			MaterialPropertiesAggregate aggregate = propertyAggregateMap.get(item.getId());
			if (aggregate != null && CollUtil.isNotEmpty(aggregate.getAttributes())) {
				aggregate.getAttributes().forEach((k, v) -> {
					if (!flatMap.containsKey(k)) {
						flatMap.put(k, v);
					}
				});
			}
			vo.setFlatAttributes(flatMap);
			return vo;
		}).collect(Collectors.toList());

		return new PageResult<>(respList, pageResult.getTotal());
	}

	private List<MaterialInfomationRespVO> buildRespList(List<MaterialInfomationDO> list) {
		if (CollUtil.isEmpty(list)) {
			return Collections.emptyList();
		}
		Map<Long, MaterialPropertiesAggregate> propertyAggregateMap = buildPropertyAggregates(
				list.stream().map(MaterialInfomationDO::getId).filter(Objects::nonNull).collect(Collectors.toList()));
		return list.stream()
				.map(item -> {
					MaterialInfomationRespVO vo = BeanUtils.toBean(item, MaterialInfomationRespVO.class);
					Map<String, Object> flatMap = new LinkedHashMap<>();
					flatMap.put("id", item.getId());
					flatMap.put("code", item.getCode());
					flatMap.put("name", item.getName());
					flatMap.put("classesId", item.getClassesId());
					flatMap.put("remark", item.getRemark());
					flatMap.put("createTime", item.getCreateTime());

					MaterialPropertiesAggregate aggregate = propertyAggregateMap.get(item.getId());
					if (aggregate != null && CollUtil.isNotEmpty(aggregate.getAttributes())) {
						aggregate.getAttributes().forEach((k, v) -> {
							// 基础字段优先，存在同名则跳过属性值
							if (!flatMap.containsKey(k)) {
								flatMap.put(k, v);
							}
						});
					}
					vo.setFlatAttributes(flatMap);
					return vo;
				})
				.collect(Collectors.toList());
	}

	private Map<Long, MaterialPropertiesAggregate> buildPropertyAggregates(Collection<Long> infoIds) {
		if (CollUtil.isEmpty(infoIds)) {
			return Collections.emptyMap();
		}

		List<MaterialHasPropertiesDO> hasPropertiesList = materialHasPropertiesMapper.selectList(
				new LambdaQueryWrapperX<MaterialHasPropertiesDO>().in(MaterialHasPropertiesDO::getInfomationId, infoIds));
		if (CollUtil.isEmpty(hasPropertiesList)) {
			return Collections.emptyMap();
		}

		Set<Long> propertiesIds = hasPropertiesList.stream()
				.map(MaterialHasPropertiesDO::getPropertiesId)
				.filter(Objects::nonNull)
				.collect(Collectors.toSet());
		Map<Long, MaterialPropertiesDO> propertiesMap = propertiesIds.isEmpty()
				? Collections.emptyMap()
				: materialPropertiesMapper.selectBatchIds(propertiesIds).stream()
				.filter(Objects::nonNull)
				.collect(Collectors.toMap(MaterialPropertiesDO::getId, Function.identity()));

		Map<Long, List<MaterialHasPropertiesDO>> infoPropsMap = hasPropertiesList.stream()
				.collect(Collectors.groupingBy(MaterialHasPropertiesDO::getInfomationId));

		Map<Long, MaterialPropertiesAggregate> result = new HashMap<>();
		for (Map.Entry<Long, List<MaterialHasPropertiesDO>> entry : infoPropsMap.entrySet()) {
			Long infoId = entry.getKey();
			List<MaterialHasPropertiesDO> props = entry.getValue();
			// 按 sort 升序，保持 deterministic，重复 code 时保留首条
			props.sort(Comparator.comparing((MaterialHasPropertiesDO o) -> o.getSort() == null ? Long.MAX_VALUE : o.getSort())
					.thenComparing(MaterialHasPropertiesDO::getPropertiesId, Comparator.nullsLast(Long::compareTo))
					.thenComparing(MaterialHasPropertiesDO::getId, Comparator.nullsLast(Long::compareTo)));

			Map<String, Object> flatMap = new LinkedHashMap<>();
			for (MaterialHasPropertiesDO item : props) {
				MaterialPropertiesDO property = propertiesMap.get(item.getPropertiesId());
				String code = property != null ? property.getCode() : null;
				if (StrUtil.isBlank(code)) {
					continue;
				}
				if (flatMap.containsKey(code)) {
					continue;
				}
				flatMap.put(code, item.getValue());
			}

			result.put(infoId, new MaterialPropertiesAggregate(flatMap));
		}

		return result;
	}

	private record MaterialPropertiesAggregate(Map<String, Object> attributes) {
		public Map<String, Object> getAttributes() {
			return attributes;
		}
	}
}
