package com.cf.imes.module.executor.service.veneer;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.date.LocalDateTimeUtils;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateDataRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.UnplannedVeneerPlateRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageGoodsRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageOrderRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateListReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManagePlateRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageRemoveReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerManageReselectReqVO;
import com.cf.imes.module.executor.dal.dataobject.order.OrderDO;
import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderModelDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO;
import com.cf.imes.module.executor.dal.mysql.order.OrderMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.OrderPlateVeneerMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerManageMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
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 static com.cf.imes.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_NOT_EXIST;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_REMOVE_RECORD_REQUIRED;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_REMOVE_RECORD_STATUS_CHANGED;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_RESELECT_RECORD_REQUIRED;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_RESELECT_RECORD_STATUS_CHANGED;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_RESELECT_SOURCE_REQUIRED;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_UNPLANNED_SCOPE_INVALID;

/**
 * 贴皮管理 Service 实现。
 */
@Service
public class VeneerManageServiceImpl implements VeneerManageService {

    /**
     * 重选贴皮单批处理数量。
     */
    private static final int RESELECT_BATCH_SIZE = 1000;

    /**
     * 移除贴皮查询及更新的单批处理数量。
     */
    private static final int REMOVE_BATCH_SIZE = 1000;

    /**
     * 未排贴皮关联订单的单批查询数量。
     */
    private static final int UNPLANNED_ORDER_BATCH_SIZE = 100;

    @Resource
    private VeneerManageMapper veneerManageMapper;

    @Resource
    private OrderPlateVeneerMapper orderPlateVeneerMapper;

    @Resource
    private VeneerMapper veneerMapper;

    @Resource
    private VeneerDecomposeService veneerDecomposeService;

    @Resource
    private OrderMapper orderMapper;

    @Override
    public PageResult<VeneerManageOrderRespVO> getOrderPage(VeneerManageOrderPageReqVO req) {
        PageDTO<VeneerManageOrderRespVO> page = new PageDTO<>(req.getPageNo(), req.getPageSize());
        IPage<Long> rootPage = veneerManageMapper.selectRootOrderIdList(
                page, req, LocalDateTimeUtils.generateTimeSection(req.getOrderDate()));
        List<Long> rootIds = rootPage.getRecords();
        if (CollUtil.isEmpty(rootIds)) {
            return new PageResult<>(List.of(), rootPage.getTotal());
        }
        IPage<VeneerManageOrderRespVO> result = veneerManageMapper.selectOrderList(
                new PageDTO<>(req.getPageNo(), PAGE_SIZE_NONE), rootIds);
        List<VeneerManageOrderRespVO> rootOrders = result.getRecords();
        fillOrderChildren(rootOrders, veneerManageMapper.selectOrderChildList(
                rootIds, req, LocalDateTimeUtils.generateTimeSection(req.getOrderDate())));
        return new PageResult<>(rootOrders, rootPage.getTotal());
    }

    @Override
    public PageResult<VeneerManageGoodsRespVO> getGoodsPage(VeneerManageGoodsPageReqVO req) {
        IPage<VeneerManageGoodsRespVO> result = veneerManageMapper.selectGoodsPage(
                new PageDTO<>(req.getPageNo(), req.getPageSize()), req);
        return new PageResult<>(result.getRecords(), result.getTotal());
    }

    @Override
    public PageResult<VeneerManageOrderRespVO> getUnplannedOrderPage(VeneerManageOrderPageReqVO req) {
        PageDTO<VeneerManageOrderRespVO> page = new PageDTO<>(req.getPageNo(), req.getPageSize());
        IPage<Long> rootPage = veneerManageMapper.selectUnplannedRootOrderIdList(
                page, req, LocalDateTimeUtils.generateTimeSection(req.getOrderDate()));
        List<Long> rootIds = rootPage.getRecords();
        if (CollUtil.isEmpty(rootIds)) {
            return new PageResult<>(List.of(), rootPage.getTotal());
        }
        IPage<VeneerManageOrderRespVO> result = veneerManageMapper.selectOrderList(
                new PageDTO<>(req.getPageNo(), PAGE_SIZE_NONE), rootIds);
        List<VeneerManageOrderRespVO> rootOrders = result.getRecords();
        fillOrderChildren(rootOrders, veneerManageMapper.selectUnplannedOrderChildList(
                rootIds, req, LocalDateTimeUtils.generateTimeSection(req.getOrderDate())));
        return new PageResult<>(rootOrders, rootPage.getTotal());
    }

    /**
     * 按父订单ID分组并回填子订单，分页数量始终以根订单为准。
     *
     * @param rootOrders 当前页根订单
     * @param childOrders 当前页根订单下符合业务条件的子订单
     */
    private void fillOrderChildren(List<VeneerManageOrderRespVO> rootOrders,
                                   List<VeneerManageOrderRespVO> childOrders) {
        if (CollUtil.isEmpty(rootOrders) || CollUtil.isEmpty(childOrders)) {
            return;
        }
        Map<Long, List<VeneerManageOrderRespVO>> childMap = new HashMap<>();
        for (VeneerManageOrderRespVO childOrder : childOrders) {
            childMap.computeIfAbsent(childOrder.getParentId(), key -> new ArrayList<>()).add(childOrder);
        }
        for (VeneerManageOrderRespVO rootOrder : rootOrders) {
            rootOrder.setChild(childMap.get(rootOrder.getOrderId()));
        }
    }

    @Override
    public PageResult<VeneerManageGoodsRespVO> getUnplannedGoodsPage(VeneerManageGoodsPageReqVO req) {
        IPage<VeneerManageGoodsRespVO> result = veneerManageMapper.selectUnplannedGoodsPage(
                new PageDTO<>(req.getPageNo(), req.getPageSize()), req);
        return new PageResult<>(result.getRecords(), result.getTotal());
    }

    @Override
    public List<VeneerManagePlateRespVO> getOrderPlateList(VeneerManagePlateListReqVO req) {
        if (CollUtil.isEmpty(req.getOrderIds())) {
            return List.of();
        }
        return veneerManageMapper.selectOrderPlateList(req);
    }

    @Override
    public List<VeneerManagePlateRespVO> getGoodsPlateList(VeneerManagePlateListReqVO req) {
        if (CollUtil.isEmpty(req.getGoodsIds())) {
            return List.of();
        }
        return veneerManageMapper.selectGoodsPlateList(req.getGoodsIds());
    }

    @Override
    public UnplannedVeneerPlateDataRespVO getUnplannedPlateData(UnplannedVeneerPlateDataReqVO req) {
        boolean byOrder = CollUtil.isNotEmpty(req.getOrderIds());
        boolean byGoods = CollUtil.isNotEmpty(req.getGoodsIds());
        if (byOrder == byGoods) {
            throw new ServiceException(VENEER_UNPLANNED_SCOPE_INVALID);
        }

        // 一次查询贴皮和原板件信息；A/B 面分别保留，加工组重复行在内存中合并。
        List<UnplannedVeneerPlateRespVO> plates = mergeUnplannedPlateDetails(
                veneerManageMapper.selectUnplannedPlateList(req));
        if (CollUtil.isEmpty(plates)) {
            return buildEmptyUnplannedPlateData();
        }

        // A/B 面共享同一份原板造型，按原板件ID只解析并返回一次。
        Map<Long, OrderModelDO> modelMap = new LinkedHashMap<>();
        for (UnplannedVeneerPlateRespVO plate : plates) {
            if (plate.getPlateId() != null && StrUtil.isNotBlank(plate.getModelData())
                    && !modelMap.containsKey(plate.getPlateId())) {
                modelMap.put(plate.getPlateId(),
                        JsonUtils.parseObject(plate.getModelData(), OrderModelDO.class));
            }
        }
        List<OrderModelDO> plateModels = new ArrayList<>(modelMap.values());

        // 订单数量远小于板件数量，按小批次加载，避免一次生成过长的 IN 条件。
        List<Long> orderIds = plates.stream()
                .map(UnplannedVeneerPlateRespVO::getOrderId)
                .distinct()
                .sorted()
                .toList();
        List<OrderDO> orders = new ArrayList<>();
        for (List<Long> orderIdBatch : CollUtil.split(orderIds, UNPLANNED_ORDER_BATCH_SIZE)) {
            orders.addAll(orderMapper.selectBatchIds(orderIdBatch));
        }

        return UnplannedVeneerPlateDataRespVO.builder()
                .orderList(buildOrderTree(orders))
                .plateList(plates)
                .plateModels(plateModels)
                .build();
    }

    /**
     * 构造各数据段均为空集合的未排贴皮响应。
     *
     * @return 空的未排贴皮排单基础数据
     */
    private UnplannedVeneerPlateDataRespVO buildEmptyUnplannedPlateData() {
        return UnplannedVeneerPlateDataRespVO.builder()
                .orderList(List.of())
                .plateList(List.of())
                .plateModels(List.of())
                .build();
    }

    /**
     * 合并同一贴皮因多个加工组产生的重复记录。
     *
     * @param rawPlates 贴皮及原板件原始查询结果
     * @return 按板件贴皮记录ID合并后的列表
     */
    private List<UnplannedVeneerPlateRespVO> mergeUnplannedPlateDetails(
            List<UnplannedVeneerPlateRespVO> rawPlates) {
        if (CollUtil.isEmpty(rawPlates)) {
            return rawPlates;
        }
        Map<Long, UnplannedVeneerPlateRespVO> plateMap = new LinkedHashMap<>();
        Map<Long, Set<String>> groupNameMap = new HashMap<>();
        for (UnplannedVeneerPlateRespVO plate : rawPlates) {
            plateMap.putIfAbsent(plate.getId(), plate);
            if (plate.getId() != null && StrUtil.isNotBlank(plate.getProcessGroupName())) {
                groupNameMap.computeIfAbsent(plate.getId(), key -> new LinkedHashSet<>())
                        .add(plate.getProcessGroupName());
            }
        }
        plateMap.forEach((id, plate) -> {
            Set<String> groupNames = groupNameMap.get(id);
            plate.setProcessGroupName(CollUtil.isEmpty(groupNames) ? null : String.join(",", groupNames));
        });
        return new ArrayList<>(plateMap.values());
    }

    /**
     * 将所选订单整理为父子结构；缺少父订单的子单作为顶层数据保留。
     *
     * @param orders 所选贴皮关联的订单
     * @return 订单父子结构
     */
    private List<OrderDO> buildOrderTree(List<OrderDO> orders) {
        if (CollUtil.isEmpty(orders)) {
            return List.of();
        }
        Map<Long, OrderDO> orderMap = new LinkedHashMap<>();
        for (OrderDO order : orders) {
            orderMap.put(order.getId(), order);
        }
        List<OrderDO> result = new ArrayList<>();
        for (OrderDO order : orders) {
            if (OrderDO.PARENT_ID_ROOT.equals(order.getParentId())) {
                order.setChild(new ArrayList<>());
                result.add(order);
            }
        }
        for (OrderDO order : orders) {
            if (OrderDO.PARENT_ID_ROOT.equals(order.getParentId())) {
                continue;
            }
            OrderDO parent = orderMap.get(order.getParentId());
            if (parent == null) {
                result.add(order);
                continue;
            }
            if (parent.getChild() == null) {
                parent.setChild(new ArrayList<>());
            }
            parent.getChild().add(order);
        }
        return result;
    }

    /**
     * 批量重选未排单贴皮，仅替换所选 A/B 面记录的贴皮库关联及贴皮属性快照。
     *
     * @param req 批量重选贴皮参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reselectVeneer(VeneerManageReselectReqVO req) {
        if (CollUtil.isEmpty(req.getIds())) {
            throw new ServiceException(VENEER_RESELECT_RECORD_REQUIRED);
        }
        if (ObjectUtil.isNull(req.getVeneerId())) {
            throw new ServiceException(VENEER_RESELECT_SOURCE_REQUIRED);
        }

        List<Long> ids = req.getIds().stream()
                .filter(Objects::nonNull)
                .distinct()
                .sorted()
                .toList();
        if (CollUtil.isEmpty(ids)) {
            throw new ServiceException(VENEER_RESELECT_RECORD_REQUIRED);
        }

        // 查询新贴皮数据，后续统一更新为当前贴皮库属性快照。
        VeneerDO veneer = veneerMapper.selectById(req.getVeneerId());
        if (ObjectUtil.isNull(veneer)) {
            throw new ServiceException(VENEER_NOT_EXIST);
        }

        for (List<Long> idBatch : CollUtil.split(ids, RESELECT_BATCH_SIZE)) {
            // 仅查询未排单记录；已删除、已排单或状态发生变化时统一按并发冲突处理。
            List<OrderPlateVeneerDO> records = orderPlateVeneerMapper.selectList(
                    new LambdaQueryWrapper<OrderPlateVeneerDO>()
                            .select(OrderPlateVeneerDO::getId)
                            .eq(OrderPlateVeneerDO::getPlanId, 0L)
                            .in(OrderPlateVeneerDO::getId, idBatch));
            if (records.size() != idBatch.size()) {
                throw new ServiceException(VENEER_RESELECT_RECORD_STATUS_CHANGED);
            }

            // 更新时再次限制未排单状态，避免查询后被其他用户排单仍可重选贴皮。
            int updated = orderPlateVeneerMapper.update(
                    new LambdaUpdateWrapper<OrderPlateVeneerDO>()
                            .eq(OrderPlateVeneerDO::getPlanId, 0L)
                            .in(OrderPlateVeneerDO::getId, idBatch)
                            .set(OrderPlateVeneerDO::getSourceVeneerId, veneer.getId())
                            .set(OrderPlateVeneerDO::getSourceVeneerNo, veneer.getVeneerNo())
                            .set(OrderPlateVeneerDO::getVeneerName, veneer.getVeneerName())
                            .set(OrderPlateVeneerDO::getProductLevel, veneer.getProductLevel())
                            .set(OrderPlateVeneerDO::getColor,
                                    ObjectUtil.defaultIfNull(veneer.getColor(), ""))
                            .set(OrderPlateVeneerDO::getMaterial,
                                    ObjectUtil.defaultIfNull(veneer.getMaterial(), "")));
            if (updated != idBatch.size()) {
                throw new ServiceException(VENEER_RESELECT_RECORD_STATUS_CHANGED);
            }
        }
    }

    /**
     * 批量移除待排单板件贴皮。
     * 关键更新均校验影响行数，任意一批数量不一致时整笔事务回滚。
     *
     * @param req 批量移除参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeVeneer(VeneerManageRemoveReqVO req) {
        List<Long> ids = normalizeRemoveIds(req);

        // 删除条件包含待排单及未删除状态；更新数量不一致表示数据状态已发生变化。
        for (List<Long> idBatch : CollUtil.split(ids, REMOVE_BATCH_SIZE)) {
            int deleted = veneerManageMapper.softDeletePendingPlateVeneer(idBatch);
            if (deleted != idBatch.size()) {
                throw new ServiceException(VENEER_REMOVE_RECORD_STATUS_CHANGED);
            }
        }

        // 更新成功后批量读取本次移除记录的归属信息，用于判断原板是否需要恢复。
        List<OrderPlateVeneerDO> selectedRecords = new ArrayList<>();
        for (List<Long> idBatch : CollUtil.split(ids, REMOVE_BATCH_SIZE)) {
            selectedRecords.addAll(veneerManageMapper.selectRemovedPlateVeneerList(idBatch));
        }

        List<Long> plateIds = selectedRecords.stream()
                .map(OrderPlateVeneerDO::getOrderPlateId)
                .distinct()
                .sorted()
                .toList();

        // 批量查询删除后仍存在有效 A/B 面的原板件，不逐板执行数据库查询。
        Set<Long> remainingPlateIds = new HashSet<>();
        for (List<Long> plateIdBatch : CollUtil.split(plateIds, REMOVE_BATCH_SIZE)) {
            remainingPlateIds.addAll(veneerManageMapper.selectRemainingPlateIdList(plateIdBatch));
        }
        List<Long> restoredPlateIds = plateIds.stream()
                .filter(plateId -> !remainingPlateIds.contains(plateId))
                .toList();
        if (CollUtil.isEmpty(restoredPlateIds)) {
            return;
        }

        // 仅当原板已无任何有效贴皮面时恢复为未分解，并校验每批更新数量。
        for (List<Long> plateIdBatch : CollUtil.split(restoredPlateIds, REMOVE_BATCH_SIZE)) {
            int restored = veneerManageMapper.restorePlateUndecomposed(plateIdBatch);
            if (restored != plateIdBatch.size()) {
                throw new ServiceException(VENEER_REMOVE_RECORD_STATUS_CHANGED);
            }
        }

        List<Long> affectedOrderIds = selectedRecords.stream()
                .map(OrderPlateVeneerDO::getOrderId)
                .distinct()
                .sorted()
                .toList();
        List<Long> affectedOrderGoodsIds = selectedRecords.stream()
                .map(OrderPlateVeneerDO::getOrderGoodsId)
                .distinct()
                .sorted()
                .toList();

        // 以 order_plate 当前状态重新统计，不使用删除贴皮记录数进行增量扣减。
        veneerDecomposeService.refreshDecomposedPlateCount(
                affectedOrderIds, affectedOrderGoodsIds);
    }

    /**
     * 校验并标准化待移除记录ID。
     *
     * @param req 批量移除参数
     * @return 去重并升序排列的记录ID列表
     */
    private List<Long> normalizeRemoveIds(VeneerManageRemoveReqVO req) {
        if (req == null || CollUtil.isEmpty(req.getIds())
                || req.getIds().stream().anyMatch(Objects::isNull)) {
            throw new ServiceException(VENEER_REMOVE_RECORD_REQUIRED);
        }
        return req.getIds().stream().distinct().sorted().toList();
    }
}
