package com.cf.imes.module.executor.service.manage.plate;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.executor.controller.admin.manage.plate.vo.plate.PlateImportExcelVO;
import com.cf.imes.module.executor.controller.admin.manage.plate.vo.plate.PlatePageReqVO;
import com.cf.imes.module.executor.controller.admin.manage.plate.vo.plate.PlateSaveReqVO;
import com.cf.imes.module.executor.dal.dataobject.plate.PlateGoodDO;
import com.cf.imes.module.executor.dal.mysql.plate.PlateGoodBatchMapper;
import com.cf.imes.module.executor.dal.mysql.plate.PlateGoodMapper;
import com.cf.imes.module.executor.util.file.FileHelperUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static com.cf.imes.module.executor.enums.ErrorCodeConstants.PLATE_ATTR_GROUP_EXISTS;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.PLATE_EXISTS;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.PLATE_GOODS_ID_NOT_SAME;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.PLATE_ID_NULL_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.PLATE_IMPORT_INTERRUPT_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.PLATE_IMPORT_QUERY_INTERRUPT_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.REMAIN_PLATE_NOT_EXISTS;
import static com.cf.imes.module.executor.framework.executor.config.ExecutorThreadPoolConfiguration.EXECUTOR_IMPOT_THREAD_POOL_TASK_EXECUTOR;

/**
 * 板材信息表 plate_{N} Service 实现类
 *
 * @author 晨丰科技
 */
@Service
@Validated
public class PlateManageServiceImpl implements PlateManageService {

    @Resource
    private PlateGoodMapper plateGoodMapper;

    @Resource
    private PlateGoodBatchMapper plateGoodBatchMapper;

    @Resource(name = EXECUTOR_IMPOT_THREAD_POOL_TASK_EXECUTOR)
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    public static final Double ZERO = 0.0;

    @Override
    public Long createPlate(PlateSaveReqVO createReqVO) {
        PlateGoodDO plate = BeanUtils.toBean(createReqVO, PlateGoodDO.class);

        // 判断板材是否存在
        validateGoodExists(plate.getGoodsId());

        // 校验板材名称、材质、颜色、厚度、品牌在组织内唯一
        validateGoodAttr(plate);

        plateGoodMapper.insert(plate);
        // 返回
        return plate.getId();
    }

    @Override
    public void updatePlate(PlateSaveReqVO updateReqVO) {
        if (updateReqVO.getId() == null) {
            throw new ServiceException(PLATE_ID_NULL_ERROR);
        }

        validatePlateExists(updateReqVO.getId());
        PlateGoodDO updateObj = BeanUtils.toBean(updateReqVO, PlateGoodDO.class);

        // 校验板材goods_id是否相同
        if (Boolean.TRUE.equals(plateGoodMapper.isByGoodIDAndId(updateReqVO.getGoodsId(), updateReqVO.getId()))) {
            throw new ServiceException(PLATE_GOODS_ID_NOT_SAME);
        }

        // 校验板材名称、材质、颜色、厚度、品牌在组织内唯一
        validateGoodAttr(updateObj);
        plateGoodMapper.updateById(updateObj);
    }

    /**
     * 校验板材名称、材质、颜色、厚度、品牌在组织内唯一
     *
     * @param plate
     */
    private void validateGoodAttr(PlateGoodDO plate) {
        Long count = plateGoodMapper.selectCount(new LambdaQueryWrapperX<PlateGoodDO>()
                .eq(PlateGoodDO::getGoodsName, plate.getGoodsName())
                .eq(PlateGoodDO::getMaterial, plate.getMaterial())
                .eq(PlateGoodDO::getColor, plate.getColor())
                .eq(PlateGoodDO::getThickness, plate.getThickness())
                .eqIfPresent(PlateGoodDO::getBrand, plate.getBrand())
                .neIfPresent(PlateGoodDO::getId, plate.getId()));
        if (count > 0) {
            throw new ServiceException(PLATE_ATTR_GROUP_EXISTS);
        }
    }

    @Override
    public void deletePlate(Long id, Long organId) {
        // 校验存在
        validatePlateExists(id);
        // 删除
        plateGoodMapper.deleteById(id);
    }

    private void validatePlateExists(Long id) {
        if (plateGoodMapper.selectOneById(id, SecurityFrameworkUtils.getUserOrganId()) == null) {
            throw new ServiceException(REMAIN_PLATE_NOT_EXISTS);
        }
    }

    @Override
    public PlateGoodDO getPlate(Long id, Long organId) {
        return plateGoodMapper.selectOneById(id, organId);
    }

    @Override
    public PlateGoodDO getPlateGoods(String id) {
        return plateGoodMapper.selectByGoodID(id , false);
    }

    @Override
    public PageResult<PlateGoodDO> getPlatePage(PlatePageReqVO pageReqVO) {
        return plateGoodMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<PlateGoodDO> webCadGetPlatePage(PlatePageReqVO pageReqVO) {
        return plateGoodMapper.selectPage(pageReqVO);
    }


    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入
    public void importPlateList(List<PlateImportExcelVO> importPlates, boolean isUpdateSupport, Long organId) {
        // 批量插入集合
        List<PlateGoodDO> insertList = new CopyOnWriteArrayList<>();
        // 批量更新集合
        List<PlateGoodDO> updateList = new CopyOnWriteArrayList<>();

        // 每100条 goodsId in查询一次
        List<List<PlateImportExcelVO>> batches = createBatches(importPlates, 100);
        boolean hasError = processBatchesConcurrently(batches, organId, isUpdateSupport, insertList, updateList);

        if (hasError) {
            throw new ServiceException(PLATE_IMPORT_QUERY_INTERRUPT_ERROR);
        }

        // 每100条插入/更新一次，避免语句过大
        if (!insertList.isEmpty()) {
            batchProcess(insertList, 100, plateGoodBatchMapper::insertBatchSomeColumn);
        }
        if (!updateList.isEmpty()) {
            batchProcess(updateList, 100, plateGoodBatchMapper::updateBatch);
        }
    }

    /**
     * 处理excel配件批次数据，多线程查询是新增还是更新
     *
     * @param batches
     * @param organId
     * @param isUpdateSupport
     * @param insertList
     * @param updateList
     * @return
     */
    private boolean processBatchesConcurrently(List<List<PlateImportExcelVO>> batches, Long organId,
                                               boolean isUpdateSupport, List<PlateGoodDO> insertList,
                                               List<PlateGoodDO> updateList) {
        // 查询过程中是否异常了
        AtomicBoolean hasError = new AtomicBoolean(false);
        // 计数器
        CountDownLatch latch = new CountDownLatch(batches.size());
        // 主线程的data_code
        String peek = DynamicDataSourceContextHolder.peek();

        for (List<PlateImportExcelVO> batch : batches) {
            threadPoolTaskExecutor.submit(() -> {
                try {
                    // 子线程同步主线程的多数据源
                    DynamicDataSourceContextHolder.push(peek);

                    List<String> goodsIds = batch.stream().map(PlateImportExcelVO::getGoodsId).toList();
                    Map<String, PlateGoodDO> plateGoodMap = plateGoodMapper.selectList(
                                    new LambdaQueryWrapper<PlateGoodDO>().in(PlateGoodDO::getGoodsId, goodsIds).eq(PlateGoodDO::getOrganId, organId))
                            .stream().collect(Collectors.toMap(PlateGoodDO::getGoodsId, p -> p));

                    // 整理到insertLis、updateList
                    doBatch(batch, plateGoodMap, organId, isUpdateSupport, insertList, updateList);

                } catch (Exception e) {
                    boolean b = hasError.get();
                    if (!b) {
                        hasError.set(true);
                    }
                } finally {
                    latch.countDown(); // 任务完成，计数器减1
                }
            });
        }

        try {
            // 等待所有任务查询完成
            latch.await(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new ServiceException(PLATE_IMPORT_INTERRUPT_ERROR);
        }
        return hasError.get();
    }

    /**
     * 遍历批次列表，存在更新，不存在新增
     *
     * @param batch
     * @param plateGoodMap    已经存在goodsId的plate map
     * @param organId
     * @param isUpdateSupport
     * @param insertList
     * @param updateList
     */
    private void doBatch(List<PlateImportExcelVO> batch, Map<String, PlateGoodDO> plateGoodMap, Long organId,
                         boolean isUpdateSupport, List<PlateGoodDO> insertList, List<PlateGoodDO> updateList) {
        for (PlateImportExcelVO importPart : batch) {
            PlateGoodDO existPlate = plateGoodMap.get(importPart.getGoodsId());
            if (existPlate == null) {
                PlateGoodDO plateGoodDO = BeanUtils.toBean(importPart, PlateGoodDO.class).setOrganId(organId);
                plateGoodDO.setOrganId(organId)
                        .setDeleted(false);
                insertList.add(plateGoodDO);
            } else if (!isUpdateSupport) {
                PlateGoodDO plateGoodDO = BeanUtils.toBean(importPart, PlateGoodDO.class)
                        .setOrganId(organId)
                        .setId(existPlate.getId());
                updateList.add(plateGoodDO);
            }
        }
    }

    /**
     * 每batchSize条数据处理一次DO
     *
     * @param dataList
     * @param batchSize
     * @param processor
     */
    private void batchProcess(List<PlateGoodDO> dataList, int batchSize, Consumer<List<PlateGoodDO>> processor) {
        if (dataList == null || dataList.isEmpty()) {
            return;
        }

        int total = dataList.size();
        for (int i = 0; i < total; i += batchSize) {
            int end = Math.min(i + batchSize, total);
            List<PlateGoodDO> subList = dataList.subList(i, end);

            if (!subList.isEmpty()) {
                processor.accept(subList);
            }
        }
    }

    /**
     * 拆分导入数据为batchSize一组
     *
     * @param list
     * @param batchSize
     * @return
     */
    private List<List<PlateImportExcelVO>> createBatches(List<PlateImportExcelVO> list, int batchSize) {
        List<List<PlateImportExcelVO>> batches = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            batches.add(list.subList(i, Math.min(i + batchSize, list.size())));
        }
        return batches;
    }

    @Override
    public void exportTemplate(HttpServletResponse response, String value) {
        String rootPath = getSavePath();
        FileHelperUtil.getFile(rootPath, response, value);
    }

    @Override
    public List<PlateGoodDO> getPlateAll() {
        return plateGoodMapper.getAllByOrganId(OrganContextHolder.getOrganId());
    }

    @Override
    public Set<String> getOrgPlateGoodsId(Long organId) {
        return plateGoodMapper.selectList(new LambdaQueryWrapper<PlateGoodDO>()
                        .eq(PlateGoodDO::getOrganId, organId)
                        .eq(PlateGoodDO::getDeleted, false)
                        .select(PlateGoodDO::getGoodsId))
                .stream().map(PlateGoodDO::getGoodsId).collect(Collectors.toSet());
    }

    @Override
    public List<LinkedHashMap<String, Object>> getOrgAttrGroup(Long organId) {
        return plateGoodMapper.getOrgAttrGroup(organId);
    }

    //    当前组织中板材是否存在
    private void validateGoodExists(String goodsId) {
        if (plateGoodMapper.selectByGoodID(goodsId, false) != null) {
            throw new ServiceException(PLATE_EXISTS);
        }
    }
}