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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

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

import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.collection.CollectionUtils;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.base.controller.admin.departmentmaterial.vo.*;
import com.zt.plat.module.base.dal.dao.departmentmaterial.DepartmentMaterialMapper;
import com.zt.plat.module.base.dal.dao.materialclasses.MaterialClassesMapper;
import com.zt.plat.module.base.dal.dataobject.base.MaterialInfomationDO;
import com.zt.plat.module.base.dal.dataobject.departmentmaterial.DepartmentMaterialDO;
import com.zt.plat.module.base.dal.dataobject.materialclasses.MaterialClassesDO;
import com.zt.plat.module.base.dal.mysql.base.MaterialInfomationMapper;
import com.zt.plat.module.base.dal.dao.businessdictionarytype.BusinessDictionaryDataMapper;
import com.zt.plat.module.base.dal.dataobject.businessdictionarytype.BusinessDictionaryDataDO;
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.zt.plat.module.system.api.dept.DeptApi;
import com.zt.plat.module.system.api.dept.dto.DeptRespDTO;

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

/**
 * 组织架构物料 Service 实现类
 *
 * @author 后台管理-1
 */
@Service
@Validated
public class DepartmentMaterialServiceImpl implements DepartmentMaterialService {

    @Resource
    private DepartmentMaterialMapper departmentMaterialMapper;

    @Resource
    private MaterialInfomationMapper materialInfomationMapper;

    @Resource
    private MaterialClassesMapper materialClassesMapper;

    @Resource
    private BusinessDictionaryDataMapper businessDictionaryDataMapper;

    @Resource
    private DeptApi deptApi;

    @Override
    public DepartmentMaterialRespVO createDepartmentMaterial(DepartmentMaterialSaveReqVO createReqVO) {
        // 插入
        DepartmentMaterialDO departmentMaterial = BeanUtils.toBean(createReqVO, DepartmentMaterialDO.class);
        departmentMaterialMapper.insert(departmentMaterial);
        // 构造完整响应，方便前端直接刷新数据
    return CollUtil.getFirst(decorateDepartmentMaterials(Collections.singletonList(departmentMaterial)));
    }

    @Override
    public void updateDepartmentMaterial(DepartmentMaterialSaveReqVO updateReqVO) {
        // 校验存在
        validateDepartmentMaterialExists(updateReqVO.getId());
        // 更新
        DepartmentMaterialDO updateObj = BeanUtils.toBean(updateReqVO, DepartmentMaterialDO.class);
        departmentMaterialMapper.updateById(updateObj);
    }

    @Override
    public void deleteDepartmentMaterial(Long id) {
        // 校验存在
        validateDepartmentMaterialExists(id);
        // 删除
        departmentMaterialMapper.deleteById(id);
    }

    @Override
        public void deleteDepartmentMaterialListByIds(List<Long> ids) {
        // 校验存在
        validateDepartmentMaterialExists(ids);
        // 删除
        departmentMaterialMapper.deleteByIds(ids);
        }

    private void validateDepartmentMaterialExists(List<Long> ids) {
        List<DepartmentMaterialDO> list = departmentMaterialMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(DEPARTMENT_MATERIAL_NOT_EXISTS);
        }
    }

    private void validateDepartmentMaterialExists(Long id) {
        if (departmentMaterialMapper.selectById(id) == null) {
            throw exception(DEPARTMENT_MATERIAL_NOT_EXISTS);
        }
    }

    @Override
    public DepartmentMaterialRespVO getDepartmentMaterial(Long id) {
        DepartmentMaterialDO data = departmentMaterialMapper.selectById(id);
        if (data == null) {
            return null;
        }
    return CollUtil.getFirst(decorateDepartmentMaterials(Collections.singletonList(data)));
    }

    @Override
    public PageResult<DepartmentMaterialRespVO> getDepartmentMaterialPage(DepartmentMaterialPageReqVO pageReqVO) {
        // 预处理物料名称/编码查询条件
        List<Long> matchedInfoIds = null;
        if (StrUtil.isNotBlank(pageReqVO.getMaterialName()) || StrUtil.isNotBlank(pageReqVO.getMaterialNumber())) {
            LambdaQueryWrapperX<MaterialInfomationDO> infoWrapper = new LambdaQueryWrapperX<>();
            infoWrapper.likeIfPresent(MaterialInfomationDO::getName, pageReqVO.getMaterialName())
                    .likeIfPresent(MaterialInfomationDO::getCode, pageReqVO.getMaterialNumber());
            List<MaterialInfomationDO> infoCandidates = materialInfomationMapper.selectList(infoWrapper);
            if (CollUtil.isEmpty(infoCandidates)) {
                return PageResult.empty();
            }
            matchedInfoIds = infoCandidates.stream().map(MaterialInfomationDO::getId).toList();
            pageReqVO.setInfomationIds(matchedInfoIds);
        }

        PageResult<DepartmentMaterialDO> pageResult = departmentMaterialMapper.selectPage(pageReqVO);
        // 重置，避免后续使用该入参出现意外的 in 条件
        pageReqVO.setInfomationIds(null);

        if (CollUtil.isEmpty(pageResult.getList())) {
            return PageResult.empty(pageResult.getTotal());
        }

        List<DepartmentMaterialRespVO> respList = decorateDepartmentMaterials(pageResult.getList());
        return new PageResult<>(respList, pageResult.getTotal());
    }

    private List<DepartmentMaterialRespVO> decorateDepartmentMaterials(List<DepartmentMaterialDO> sourceList) {
        if (CollUtil.isEmpty(sourceList)) {
            return Collections.emptyList();
        }

        // 关联数据准备
        Set<Long> infoIds = sourceList.stream()
                .map(DepartmentMaterialDO::getInfomationId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, MaterialInfomationDO> infoMap = infoIds.isEmpty()
                ? Collections.emptyMap()
                : materialInfomationMapper.selectBatchIds(infoIds).stream()
                    .filter(Objects::nonNull)
                    .collect(Collectors.toMap(MaterialInfomationDO::getId, Function.identity()));

        Set<Long> classIds = new HashSet<>();
        sourceList.forEach(item -> {
            if (item.getClassesId() != null) {
                classIds.add(item.getClassesId());
            }
            MaterialInfomationDO info = infoMap.get(item.getInfomationId());
            if (info != null && info.getClassesId() != null) {
                classIds.add(info.getClassesId());
            }
        });

        Map<Long, MaterialClassesDO> classCache = loadClassHierarchy(classIds);

        Set<Long> deptIds = sourceList.stream()
                .map(DepartmentMaterialDO::getDeptId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        Map<Long, DeptRespDTO> deptMap = Collections.emptyMap();
        if (CollUtil.isNotEmpty(deptIds)) {
            try {
                deptMap = CollectionUtils.convertMap(deptApi.getDeptList(deptIds).getCheckedData(), DeptRespDTO::getId);
            } catch (Exception ignore) {
                deptMap = Collections.emptyMap();
            }
        }

        Set<String> dictionaryValues = sourceList.stream()
                .map(DepartmentMaterialDO::getDictionaryDataValue)
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toSet());
        Map<String, BusinessDictionaryDataDO> dictionaryMap = dictionaryValues.isEmpty()
                ? Collections.emptyMap()
                : businessDictionaryDataMapper.selectListByValues(dictionaryValues).stream()
                        .filter(Objects::nonNull)
                        .collect(Collectors.toMap(BusinessDictionaryDataDO::getValue, Function.identity(), (exist, replace) -> exist));

        List<DepartmentMaterialRespVO> respList = new ArrayList<>(sourceList.size());
        for (DepartmentMaterialDO item : sourceList) {
            DepartmentMaterialRespVO respVO = BeanUtils.toBean(item, DepartmentMaterialRespVO.class);

            MaterialInfomationDO info = infoMap.get(item.getInfomationId());
            if (info != null) {
                respVO.setMaterialNumber(info.getCode());
                respVO.setMaterialName(info.getName());
            }

            Long targetClassId = item.getClassesId();
            if (targetClassId == null && info != null) {
                targetClassId = info.getClassesId();
            }
            if (targetClassId != null) {
                List<String> classPath = buildClassPath(targetClassId, classCache);
                if (!classPath.isEmpty()) {
                    respVO.setCategoryLargeName(classPath.get(0));
                    if (classPath.size() > 1) {
                        respVO.setCategoryMediumName(classPath.get(1));
                    }
                    if (classPath.size() > 2) {
                        respVO.setCategorySmallName(classPath.get(classPath.size() - 1));
                    }
                    respVO.setCategoryPath(String.join("/", classPath));
                }
            }

            DeptRespDTO deptRespDTO = deptMap.get(item.getDeptId());
            if (deptRespDTO != null) {
                respVO.setDeptName(deptRespDTO.getName());
            }

            BusinessDictionaryDataDO dictionaryData = dictionaryMap.get(respVO.getDictionaryDataValue());
            if (dictionaryData != null) {
                respVO.setDictionaryDataLabel(dictionaryData.getLabel());
            } else {
                respVO.setDictionaryDataLabel(respVO.getDictionaryDataValue());
            }

            respList.add(respVO);
        }
        return respList;
    }

    private Map<Long, MaterialClassesDO> loadClassHierarchy(Set<Long> initialIds) {
        if (CollUtil.isEmpty(initialIds)) {
            return Collections.emptyMap();
        }
        Map<Long, MaterialClassesDO> cache = new HashMap<>();
        Set<Long> pending = new HashSet<>(initialIds);
        while (!pending.isEmpty()) {
            List<MaterialClassesDO> fetched = materialClassesMapper.selectBatchIds(pending);
            pending = new HashSet<>();
            for (MaterialClassesDO cls : fetched) {
                if (cls == null || cache.containsKey(cls.getId())) {
                    continue;
                }
                cache.put(cls.getId(), cls);
                Long parentId = cls.getParentId();
                if (parentId != null && parentId > 0 && !cache.containsKey(parentId)) {
                    pending.add(parentId);
                }
            }
        }
        return cache;
    }

    private List<String> buildClassPath(Long classId, Map<Long, MaterialClassesDO> cache) {
        List<String> path = new ArrayList<>();
        MaterialClassesDO current = cache.get(classId);
        int guard = 0;
        while (current != null && guard++ < 10) {
            path.add(current.getName());
            Long parentId = current.getParentId();
            if (parentId == null || parentId == 0) {
                break;
            }
            current = cache.get(parentId);
        }
        Collections.reverse(path);
        return path;
    }

}