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

import cn.hutool.core.collection.CollUtil;
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.erp.utils.MyRedisConfig;
import com.zt.plat.module.erp.enums.OftenEnum;
import com.zt.plat.module.erp.controller.admin.erp.vo.ErpProcessDetailPageReqVO;
import com.zt.plat.module.erp.controller.admin.erp.vo.ErpProcessDetailRespVO;
import com.zt.plat.module.erp.controller.admin.erp.vo.ErpProcessDetailSaveReqVO;
import com.zt.plat.module.erp.dal.dataobject.erp.ErpProcessDetailDO;
import com.zt.plat.module.erp.dal.mysql.erp.ErpProcessDetailMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.erp.enums.ErrorCodeConstants.ERP_PROCESS_DETAIL_NOT_EXISTS;

/**
 * ERP工艺路线明细 Service 实现类
 *
 * @author 后台管理
 */
@Service
@Validated
public class ErpProcessDetailServiceImpl implements ErpProcessDetailService {

    @Resource
    private ErpProcessDetailMapper erpProcessDetailMapper;

    @Resource
    private MyRedisConfig myRedisConfig;

    @Override
    public ErpProcessDetailRespVO createErpProcessDetail(ErpProcessDetailSaveReqVO createReqVO) {
        // 插入
        ErpProcessDetailDO erpProcessDetail = BeanUtils.toBean(createReqVO, ErpProcessDetailDO.class);
        erpProcessDetailMapper.insert(erpProcessDetail);
        // 返回
        return BeanUtils.toBean(erpProcessDetail, ErpProcessDetailRespVO.class);
    }

    @Override
    public void updateErpProcessDetail(ErpProcessDetailSaveReqVO updateReqVO) {
        // 校验存在
        validateErpProcessDetailExists(updateReqVO.getId());
        // 更新
        ErpProcessDetailDO updateObj = BeanUtils.toBean(updateReqVO, ErpProcessDetailDO.class);
        erpProcessDetailMapper.updateById(updateObj);
    }

    @Override
    public void deleteErpProcessDetail(Long id) {
        // 校验存在
        validateErpProcessDetailExists(id);
        // 删除
        erpProcessDetailMapper.deleteById(id);
    }

    @Override
    public void deleteErpProcessDetailListByIds(List<Long> ids) {
        // 校验存在
        validateErpProcessDetailExists(ids);
        // 删除
        erpProcessDetailMapper.deleteByIds(ids);
    }

    private void validateErpProcessDetailExists(List<Long> ids) {
        List<ErpProcessDetailDO> list = erpProcessDetailMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(ERP_PROCESS_DETAIL_NOT_EXISTS);
        }
    }

    private void validateErpProcessDetailExists(Long id) {
        if (erpProcessDetailMapper.selectById(id) == null) {
            throw exception(ERP_PROCESS_DETAIL_NOT_EXISTS);
        }
    }

    @Override
    public ErpProcessDetailDO getErpProcessDetail(Long id) {
        return erpProcessDetailMapper.selectById(id);
    }

    @Override
    public PageResult<ErpProcessDetailDO> getErpProcessDetailPage(ErpProcessDetailPageReqVO pageReqVO) {
        return erpProcessDetailMapper.selectPage(pageReqVO);
    }

    public void saveOrUpdateErpProcessDetail(List<ErpProcessDetailDO> updateReqVOS) {
        String key = "erpMap" + OftenEnum.FuncnrEnum.工艺路线.getFuncnr() + "Detail";
        // 1. 处理公司数据，区分新增和更新
        ProcessingResult result = processData(updateReqVOS, key);

        // 2. 批量保存数据
        saveData(result);
    }

    private ProcessingResult processData(List<ErpProcessDetailDO> updateReqVOS, String key) {
        if (myRedisConfig.getRedisCacheMap(key).isEmpty()) {
            initializeMap(key);
        }
        List<ErpProcessDetailDO> toUpdate = new ArrayList<>();
        List<ErpProcessDetailDO> toInsert = new ArrayList<>();

        List<String> dataArrayNumbers = new ArrayList<>();
        Map<String, Long> existingNumbers = myRedisConfig.getRedisCacheMap(key);
        for (ErpProcessDetailDO updateReqVO : updateReqVOS) {
            if (updateReqVO.getProcessingName() == null || updateReqVO.getProcessingName().isEmpty()) {
                continue;
            }
            String mapKey = updateReqVO.getProcessId() + "-" + updateReqVO.getProcessingNumber()+"-" + updateReqVO.getWorkCenterNumber();
            if (existingNumbers.containsKey(mapKey)) {
                updateReqVO.setId(existingNumbers.get(mapKey));
            }
            if (updateReqVO.getId() == null) {
                toInsert.add(updateReqVO);
            } else {
                toUpdate.add(updateReqVO);
            }
            dataArrayNumbers.add(mapKey);
        }

        Map<String, Long> deleteNumbers = new HashMap<>();
        for (Map.Entry<String, Long> entry : existingNumbers.entrySet()) {
            if (!dataArrayNumbers.contains(entry.getKey())) {
                deleteNumbers.put(entry.getKey(), entry.getValue());
            }
        }

        return new ProcessingResult(toUpdate, toInsert, deleteNumbers, key);
    }

    private void saveData(ProcessingResult result) {
        // 批量新增和更新
        if (!result.toInsert.isEmpty()) {
            erpProcessDetailMapper.insertBatch(result.toInsert);
            // 批量查询刚插入数据的id，提升效率
            List<ErpProcessDetailDO> insertedRecords = erpProcessDetailMapper.selectList(
                    new LambdaQueryWrapperX<ErpProcessDetailDO>()
                            .in(ErpProcessDetailDO::getProcessId, result.toInsert.stream().map(ErpProcessDetailDO::getProcessId).distinct().collect(Collectors.toList()))
                            .in(ErpProcessDetailDO::getProcessingNumber, result.toInsert.stream().map(ErpProcessDetailDO::getProcessingNumber).distinct().collect(Collectors.toList()))
                            .in(ErpProcessDetailDO::getProcessingName, result.toInsert.stream().map(ErpProcessDetailDO::getProcessingName).distinct().collect(Collectors.toList()))
                            .in(ErpProcessDetailDO::getWorkCenterNumber, result.toInsert.stream().map(ErpProcessDetailDO::getWorkCenterNumber).distinct().collect(Collectors.toList()))
            );
            Map<String, Long> numberIdMap = insertedRecords.stream()
                    .collect(Collectors.toMap(
                            asset -> asset.getProcessId() + "-" + asset.getProcessingNumber() + "-" + asset.getProcessingName() + "-" + asset.getWorkCenterNumber(),
                            ErpProcessDetailDO::getId, (existing, replacement) -> replacement));
            myRedisConfig.addRedisCacheMap(result.key, numberIdMap);
        }
        if (!result.toUpdate.isEmpty()) {
            erpProcessDetailMapper.updateBatch(result.toUpdate);
        }
        if (!result.deleteNumbers.isEmpty()) {
            // 使用流式处理和批处理优化删除逻辑
            List<Long> idsToDelete = new ArrayList<>(result.deleteNumbers.values());
            if (!idsToDelete.isEmpty()) {
                erpProcessDetailMapper.deleteByIds(idsToDelete);
            }
            myRedisConfig.deleteRedisCacheMap(result.key, new ArrayList<>(result.deleteNumbers.keySet()));
        }
    }

    private static class ProcessingResult {
        private final List<ErpProcessDetailDO> toUpdate;
        private final List<ErpProcessDetailDO> toInsert;
        private final Map<String, Long> deleteNumbers;
        private final String key;

        public ProcessingResult(List<ErpProcessDetailDO> toUpdate, List<ErpProcessDetailDO> toInsert, Map<String, Long> deleteNumbers, String key) {
            this.toUpdate = toUpdate;
            this.toInsert = toInsert;
            this.deleteNumbers = deleteNumbers;
            this.key = key;
        }
    }

    private void initializeMap(String key) {
        List<ErpProcessDetailDO> assets = erpProcessDetailMapper.selectList(new LambdaQueryWrapperX<ErpProcessDetailDO>());
        Map<String, Long> existingNumbers = new HashMap<>();
        for (ErpProcessDetailDO asset : assets) {
            String mapKey = asset.getProcessId() + "-" + asset.getProcessingNumber()+ "-" + asset.getProcessingName()+ "-" + asset.getWorkCenterNumber();
            existingNumbers.put(mapKey, asset.getId());
        }
        myRedisConfig.addRedisCacheMap(key, existingNumbers);
    }
}