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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
import com.zt.plat.module.base.dal.dao.materialclasses.MaterialClassesMapper;
import com.zt.plat.module.base.dal.dataobject.materialclasses.MaterialClassesDO;
import com.zt.plat.module.base.dal.dataobject.masterdata.MdmMaterialCategoryDO;
import com.zt.plat.module.base.dal.mysql.masterdata.MdmMaterialCategoryMapper;
import com.zt.plat.module.base.enums.ErrorCodeConstants;
import com.zt.plat.module.base.framework.sync.config.MasterDataSyncProperties;
import com.zt.plat.module.base.service.masterdatasync.dto.MasterDataCategorySyncReport;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 物料分类同步实现。
 */
@Slf4j
@Service
public class MasterDataCategorySyncServiceImpl implements MasterDataCategorySyncService {

    private static final long ROOT_CLASS_PARENT_ID = 0L;

    @Resource
    private MdmMaterialCategoryMapper mdmMaterialCategoryMapper;
    @Resource
    private MaterialClassesMapper materialClassesMapper;
    @Resource
    private MasterDataSyncProperties properties;

    @Override
    public MasterDataCategorySyncReport syncAll() {
        if (!properties.isEnabled()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.MASTER_DATA_SYNC_DISABLED);
        }
        MasterDataCategorySyncReport report = MasterDataCategorySyncReport.start();
        try {
            List<MdmMaterialCategoryDO> categories = mdmMaterialCategoryMapper.selectAll();
            if (CollUtil.isEmpty(categories)) {
                report.markSuccess();
                return report;
            }
            categories.sort(Comparator.comparing(MdmMaterialCategoryDO::getCode));
            Map<String, MaterialClassesDO> classesByCode = loadExistingClasses(categories);
                Map<Long, MaterialClassesDO> classesById = classesByCode.values().stream()
                    .filter(item -> item.getId() != null)
                    .collect(Collectors.toMap(MaterialClassesDO::getId, item -> item, (a, b) -> a, LinkedHashMap::new));
                // 统一收集新增分类，批量写入提升性能
                List<MaterialClassesDO> pendingClassInserts = new ArrayList<>();
            for (MdmMaterialCategoryDO category : categories) {
                Long sourceId = category.getCodeId();
                if (sourceId == null) {
                    log.warn("分类 {} 缺少主键 CODEID，已跳过", category.getCode());
                    continue;
                }
                String code = normalizeCode(category.getCode());
                if (code == null) {
                    continue;
                }
                report.incrementProcessed(1);
                long level = determineLevel(code);
                Long parentId = resolveParentId(category, classesByCode, classesById, report);
                if (parentId == null) {
                    parentId = ROOT_CLASS_PARENT_ID;
                }
                String name = StrUtil.emptyIfNull(StrUtil.trim(category.getName()));
                String remark = StrUtil.emptyIfNull(StrUtil.trim(category.getRemark()));
                MaterialClassesDO current = classesByCode.get(code);
                if (current == null) {
                    MaterialClassesDO created = MaterialClassesDO.builder()
                            .id(sourceId)
                            .code(code)
                            .name(name)
                            .level(level)
                            .parentId(parentId)
                            .remark(remark)
                            .build();
                    pendingClassInserts.add(created);
                    classesByCode.put(code, created);
                    if (created.getId() != null) {
                        classesById.put(created.getId(), created);
                    }
                    report.incrementInserted();
                } else {
                    if (current.getId() == null && sourceId != null) {
                        current.setId(sourceId);
                        classesById.put(sourceId, current);
                    }
                    boolean needUpdate = false;
                    MaterialClassesDO update = new MaterialClassesDO();
                    update.setId(current.getId());
                    if (!Objects.equals(current.getParentId(), parentId)) {
                        update.setParentId(parentId);
                        current.setParentId(parentId);
                        needUpdate = true;
                    }
                    if (StrUtil.isNotBlank(name) && !StrUtil.equals(current.getName(), name)) {
                        update.setName(name);
                        current.setName(name);
                        needUpdate = true;
                    }
                    if (!Objects.equals(current.getLevel(), level)) {
                        update.setLevel(level);
                        current.setLevel(level);
                        needUpdate = true;
                    }
                    if (!Objects.equals(StrUtil.emptyIfNull(current.getRemark()), remark)) {
                        update.setRemark(remark);
                        current.setRemark(remark);
                        needUpdate = true;
                    }
                    if (needUpdate) {
                        materialClassesMapper.updateById(update);
                        report.incrementUpdated();
                    }
                }
            }
            if (CollUtil.isNotEmpty(pendingClassInserts)) {
                materialClassesMapper.insertBatch(pendingClassInserts);
            }
            report.markSuccess();
            return report;
        } catch (Throwable ex) {
            report.markFailure(ex.getMessage());
            throw ex;
        } finally {
            report.finish();
        }
    }

    private Map<String, MaterialClassesDO> loadExistingClasses(List<MdmMaterialCategoryDO> categories) {
        Set<String> codes = categories.stream()
                .map(MdmMaterialCategoryDO::getCode)
                .map(this::normalizeCode)
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        List<MaterialClassesDO> existing = materialClassesMapper.selectByCodes(codes);
        Map<String, MaterialClassesDO> result = new LinkedHashMap<>();
        if (existing != null) {
            existing.forEach(item -> {
                String key = normalizeCode(item.getCode());
                if (key != null) {
                    result.put(key, item);
                }
            });
        }
        return result;
    }

    private Long resolveParentId(MdmMaterialCategoryDO category,
                                 Map<String, MaterialClassesDO> classesByCode,
                                 Map<Long, MaterialClassesDO> classesById,
                                 MasterDataCategorySyncReport report) {
        if (category.getParentCodeId() != null && category.getParentCodeId() > 0) {
            MaterialClassesDO parent = classesById.get(category.getParentCodeId());
            if (parent != null) {
                return parent.getId();
            }
            log.warn("分类 {} 指定父级 ID {} 不存在，将尝试按编码推断", category.getCode(), category.getParentCodeId());
        }
        String parentCode = resolveParentCode(normalizeCode(category.getCode()));
        if (parentCode == null) {
            return ROOT_CLASS_PARENT_ID;
        }
        MaterialClassesDO parent = classesByCode.get(parentCode);
        if (parent == null) {
            log.warn("分类 {} 缺失父级 {}，将挂载到根节点", category.getCode(), parentCode);
            if (report != null) {
                report.incrementMissingParent();
            }
            return null;
        }
        return parent.getId();
    }

    private String resolveParentCode(String code) {
        String normalized = normalizeCode(code);
        if (normalized == null) {
            return null;
        }
        String numericPart = extractNumericPart(normalized);
        if (StrUtil.isBlank(numericPart) || numericPart.length() <= 2) {
            return null;
        }
        String prefix = normalized.substring(0, normalized.length() - numericPart.length());
        String parentNumeric = numericPart.substring(0, numericPart.length() - 2);
        return prefix + parentNumeric;
    }

    private long determineLevel(String code) {
        String normalized = normalizeCode(code);
        if (normalized == null) {
            return 1L;
        }
        String numericPart = extractNumericPart(normalized);
        if (StrUtil.isBlank(numericPart)) {
            if (normalized.length() <= 2) {
                return 1L;
            }
            if (normalized.length() <= 4) {
                return 2L;
            }
            return 3L;
        }
        int len = numericPart.length();
        if (len <= 2) {
            return 1L;
        }
        if (len <= 4) {
            return 2L;
        }
        return 3L;
    }

    private String normalizeCode(String code) {
        return StrUtil.isBlank(code) ? null : StrUtil.trim(code);
    }

    /**
     * 提取编码中连续的尾部数字部分，例如 ZT0103 -> 0103，ZT010312 -> 010312
     */
    private String extractNumericPart(String code) {
        if (code == null) {
            return null;
        }
        int idx = code.length();
        while (idx > 0 && Character.isDigit(code.charAt(idx - 1))) {
            idx--;
        }
        String numeric = code.substring(idx);
        return StrUtil.isBlank(numeric) ? null : numeric;
    }
}
