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

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.CommonResult;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.organ.core.aop.OrganIgnore;
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.module.executor.controller.admin.manage.parts.vo.PartsCadPageReqVO;
import com.cf.imes.module.executor.controller.admin.manage.parts.vo.PartsImportExcelVO;
import com.cf.imes.module.executor.controller.admin.manage.parts.vo.PartsPageReqVO;
import com.cf.imes.module.executor.controller.admin.manage.parts.vo.PartsSaveReqVO;
import com.cf.imes.module.executor.dal.dataobject.orderParts.PartsDO;
import com.cf.imes.module.executor.dal.mysql.orderParts.PartsBatchMapper;
import com.cf.imes.module.executor.dal.mysql.orderParts.PartsMapper;
import com.cf.imes.module.executor.enums.manage.parts.CategoryEnum;
import com.cf.imes.module.executor.util.file.FileHelperUtil;
import com.cf.imes.module.executor.util.number.NumberHelperUtil;
import com.cf.imes.module.system.api.organ.OrganApi;
import com.cf.imes.module.system.api.permission.PermissionApi;
import com.cf.imes.module.system.enums.ErrorCodeConstants;
import lombok.extern.slf4j.Slf4j;
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.List;
import java.util.Map;
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.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.*;
import static com.cf.imes.module.executor.framework.executor.config.ExecutorThreadPoolConfiguration.EXECUTOR_IMPOT_THREAD_POOL_TASK_EXECUTOR;


/**
 * 配件表 parts_{N} Service 实现类
 *
 * @author 晨丰科技
 */
@Service
@Validated
@Slf4j
public class PartsServiceImpl implements PartsService {

    @Resource
    private PartsMapper partsMapper;

    @Resource
    private PartsBatchMapper partsBatchMapper;

    @Resource
    private OrganApi organApi;

    @Resource
    private PermissionApi permissionApi;

    @Resource
    private NumberHelperUtil numberUtil;

    @Resource(name = EXECUTOR_IMPOT_THREAD_POOL_TASK_EXECUTOR)
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private final static String BASE_PARTS_IMPORT_PERMISSION = "base:accessory:import"; // 导入板材
    private final static String BASE_PARTS_DELETE_PERMISSION = "base:accessory:delete"; // 删除板材信息
    private final static String BASE_PARTS_CREATE_PERMISSION = "base:accessory:create"; // 创建板材信息
    private final static String BASE_PARTS_UPDATE_PERMISSION = "base:accessory:update"; // 更新板材信息
    private final static String BASE_PARTS_QUERY_PERMISSION = "base:accessory:query"; // 获得板材信息

    private static final String EMPTY_STRING = new String();

    @Override
    @OrganIgnore
    public Long create(PartsSaveReqVO createReqVO) {

        validateFieldExists(createReqVO);

        PartsDO partsDO = BeanUtils.toBean(createReqVO, PartsDO.class);
        Long organId = getOrganId(BASE_PARTS_CREATE_PERMISSION, getLoginUser().getId(), partsDO.getOrganId());
        partsDO.setOrganId(organId).setIsComposite(createReqVO.getIsComposite() != null && createReqVO.getIsComposite());
        // 判断是否存在--- goodsId
        if (validateExistsByGoodsId(partsDO.getGoodsId(), organId)) {
            throw new ServiceException(PARTS_GOOD_ID_EXISTS);
        }

        partsMapper.insert(partsDO);
        // 返回
        return partsDO.getId();
    }

    @Override
    @OrganIgnore
    @Transactional(rollbackFor = Exception.class)
    public void update(PartsSaveReqVO updateReqVO) {

        validateFieldExists(updateReqVO);

        Long organId = getOrganId(BASE_PARTS_UPDATE_PERMISSION, getLoginUser().getId(), updateReqVO.getOrganId());
        // 校验id存在
        boolean index = validateExistsById(updateReqVO.getId(), organId);

        if (!index) {
            throw new ServiceException(PARTS_NOT_EXISTS);
        }

        // 校验goodsId是否存在
        if (!validateExistsExceptGoodsId(updateReqVO.getId(), updateReqVO.getGoodsId(), organId)) {
            throw new ServiceException(PARTS_GOOD_ID_EXISTS);
        }
        // 更新
        updateReqVO.setOrganId(organId).setIsComposite(updateReqVO.getIsComposite() != null && updateReqVO.getIsComposite());
        PartsDO updateObj = PartsDO.builder()
                .id(updateReqVO.getId())
                .organId(organId)
                .goodsId(updateReqVO.getGoodsId())
                .name(updateReqVO.getName())
                .color(getEmpty(updateReqVO.getColor()))
                .material(getEmpty(updateReqVO.getMaterial()))
                .category(updateReqVO.getCategory())
                .type(getEmpty(updateReqVO.getType()))
                .model(getEmpty(updateReqVO.getModel()))
                .price(getZeroDouble(updateReqVO.getPrice()))
                .width(getZeroDouble(updateReqVO.getWidth()))
                .length(getZeroDouble(updateReqVO.getLength()))
                .thickness(getZeroDouble(updateReqVO.getThickness()))
                .spec(getEmpty(updateReqVO.getSpec()))
                .brand(getEmpty(updateReqVO.getBrand()))
                .factory(getEmpty(updateReqVO.getFactory()))
                .unit(getEmpty(updateReqVO.getUnit()))
                .isComposite(updateReqVO.getIsComposite() != null && updateReqVO.getIsComposite())
                .subparts(getEmpty(updateReqVO.getSubparts()))
                .remark(getEmpty(updateReqVO.getRemark()))
                .build();

        partsMapper.updateById(updateObj);
    }

    @Override
    @OrganIgnore
    public void delete(Long id, Long organId) {
        // 校验存在
        if (!validateExistsById(id, getOrganId(BASE_PARTS_DELETE_PERMISSION, getLoginUser().getId(), organId))) {
            throw new ServiceException(PARTS_NOT_EXISTS);
        }

        // 删除
        partsMapper.deleteById(id);
    }

    private boolean validateExistsById(Long id, Long organId) {
        if (partsMapper.selectById(id, organId) == null) {
            return false;
        }
        return true;
    }

    private boolean validateExistsByGoodsId(String goodsId, Long organId) {
        if (partsMapper.selectByGoodID(goodsId, organId) == null) {
            return false;
        }
        return true;
    }

    private boolean validateExistsExceptGoodsId(Long id, String goodsId, Long organId) {
        if (partsMapper.selectExceptGoodID(id, goodsId, organId) == null) { // 不存在相同的goods_id
            return true;
        }
        return false;
    }

    @Override
    @OrganIgnore
    public PartsDO get(Long id, Long organId) {
        organId = getOrganId(BASE_PARTS_QUERY_PERMISSION, getLoginUser().getId(), organId);
        return partsMapper.selectById(id, organId);
    }

    @Override
    @OrganIgnore
    public PageResult<PartsDO> getPage(PartsPageReqVO pageReqVO) {
        pageReqVO.setOrganId(getOrganId(BASE_PARTS_QUERY_PERMISSION, getLoginUser().getId(), pageReqVO.getOrganId()))
                .setLength(numberUtil.compareLength(pageReqVO.getLength()))
                .setWidth(numberUtil.compareLength(pageReqVO.getWidth()))
                .setThickness(numberUtil.compareLength(pageReqVO.getThickness()));

        return partsMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<PartsDO> webCadGetPage(PartsPageReqVO pageReqVO) {
        pageReqVO.setLength(numberUtil.compareLength(pageReqVO.getLength()))
                .setWidth(numberUtil.compareLength(pageReqVO.getWidth()))
                .setThickness(numberUtil.compareLength(pageReqVO.getThickness()));

        return partsMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<PartsDO> getCadPage(PartsCadPageReqVO pageReqVO) {
//        pageReqVO.setOrganId(getOrganId(BASE_PARTS_QUERY_PERMISSION, getLoginUser().getId(), pageReqVO.getOrganId()));

        return partsMapper.selectCadPage(pageReqVO);
    }

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

    @Override
    @OrganIgnore
    @Transactional(rollbackFor = Exception.class)
    public void importPartsList(List<PartsImportExcelVO> importParts, boolean isUpdateSupport, Long organId) {

        // 组织id
        organId = getOrganId(BASE_PARTS_IMPORT_PERMISSION, getLoginUser().getId(), organId);

        // 批量插入集合
        List<PartsDO> insertList = new CopyOnWriteArrayList<>();
        // 批量更新集合
        List<PartsDO> updateList = new CopyOnWriteArrayList<>();

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

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

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

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

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

                    List<String> goodsIds = batch.stream().map(PartsImportExcelVO::getGoodsId).collect(Collectors.toList());
                    Map<String, PartsDO> partsMap = partsMapper.selectList(
                                    new LambdaQueryWrapper<PartsDO>().in(PartsDO::getGoodsId, goodsIds).eq(PartsDO::getOrganId, organId))
                            .stream().collect(Collectors.toMap(PartsDO::getGoodsId, p -> p));

                    // 整理到insertLis、updateList
                    doBatch(batch, partsMap, 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(PARTS_IMPORT_INTERRUPT_ERROR);
        }
        return hasError.get();
    }

    /**
     * 遍历批次列表，存在更新，不存在新增
     *
     * @param batch
     * @param partsMap        已经存在goodsId的part map
     * @param organId
     * @param isUpdateSupport
     * @param insertList
     * @param updateList
     */
    private void doBatch(List<PartsImportExcelVO> batch, Map<String, PartsDO> partsMap, Long organId,
                         boolean isUpdateSupport, List<PartsDO> insertList, List<PartsDO> updateList) {
        for (PartsImportExcelVO importPart : batch) {
            PartsDO existPlate = partsMap.get(importPart.getGoodsId());
            if (existPlate == null) {
                PartsDO partsDO = BeanUtils.toBean(importPart, PartsDO.class).setOrganId(organId);
                partsDO.setIsComposite(getDefaultFalse(partsDO.getIsComposite()))
                        .setCategory(getEmpty(partsDO.getCategory()))
                        .setSubparts(getEmpty(partsDO.getType()))
                        .setColor(getEmpty(partsDO.getColor()))
                        .setFactory(getEmpty(partsDO.getFactory()))
                        .setRemark(getEmpty(partsDO.getRemark()))
                        .setBrand(getEmpty(partsDO.getBrand()))
                        .setModel(getEmpty(partsDO.getModel()))
                        .setSpec(getEmpty(partsDO.getSpec()))
                        .setMaterial(getEmpty(partsDO.getMaterial()))
                        .setLength(getZeroDouble(partsDO.getLength()))
                        .setWidth(getZeroDouble(partsDO.getWidth()))
                        .setThickness(getZeroDouble(partsDO.getThickness()))
                        .setPrice(getZeroDouble(partsDO.getPrice()))
                        .setUnit(getEmpty(partsDO.getUnit()))
                        .setOrganId(organId)
                        .setDeleted(false);
                insertList.add(partsDO);
            } else if (!isUpdateSupport) {
                PartsDO partsDO = BeanUtils.toBean(importPart, PartsDO.class)
                        .setOrganId(organId)
                        .setId(existPlate.getId());
                updateList.add(partsDO);
            }
        }
    }

    /**
     * 每batchSize条数据处理一次DO
     *
     * @param dataList
     * @param batchSize
     * @param processor
     */
    private void batchProcess(List<PartsDO> dataList, int batchSize, Consumer<List<PartsDO>> 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<PartsDO> subList = dataList.subList(i, end);

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

    /**
     * 拆分导入数据为batchSize一组
     *
     * @param list
     * @param batchSize
     * @return
     */
    private List<List<PartsImportExcelVO>> createBatches(List<PartsImportExcelVO> list, int batchSize) {
        List<List<PartsImportExcelVO>> 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;
    }

    /**
     * value为空返回EMPTY_STRING
     *
     * @param value
     * @return
     */
    private String getEmpty(String value) {
        return StringUtils.defaultIfEmpty(value, EMPTY_STRING);
    }

    /**
     * value为空返回0.0
     *
     * @param value
     * @return
     */
    private double getZeroDouble(Double value) {
        return ObjectUtil.defaultIfNull(value, 0.0);
    }

    /**
     * value为空返回false
     *
     * @param value
     * @return
     */
    private Boolean getDefaultFalse(Boolean value) {
        return ObjectUtil.defaultIfNull(value, false);
    }

    @Override
    public List<PartsDO> getPartAll(String category) {
        return partsMapper.getAllByOrganId(OrganContextHolder.getOrganId(), category);
    }

    private void validateOrganExists(Long organId) {
        CommonResult<Boolean> index = organApi.validOrgan(organId);
        if (!index.isSuccess()) {
            throw new ServiceException(ErrorCodeConstants.ORGAN_NOT_EXISTS);
        }
    }

    //    组织id返回
    private Long getOrganId(String type, Long userId, Long organId) {

        boolean hasPermission = permissionApi.hasRoles(userId, type).getData();

        Long organIdDefault = OrganContextHolder.getOrganId();

        if (hasPermission && organId != null && organId != 0) {

            validateOrganExists(organId);

            organIdDefault = organId;
        }

        return organIdDefault;
    }

    //    必填字段判断
    private void validateFieldExists(PartsSaveReqVO partsSaveReqVO) {
        if (!CategoryEnum.OTHER.getValue().equals(partsSaveReqVO.getCategory())) { // 不为其他
            if (partsSaveReqVO.getUnit() == null || partsSaveReqVO.getUnit().isEmpty()) {
                throw new ServiceException(PARTS_UNIT_NOT_NULL);
            }
            if (CategoryEnum.EDGING.getValue().equals(partsSaveReqVO.getCategory())) { // 为封边
                if (partsSaveReqVO.getColor() == null || partsSaveReqVO.getWidth() == null || partsSaveReqVO.getThickness() == null) {
                    throw new ServiceException(PARTS_EDGE_NOT_NULL);
                }
            }
        }

    }


}