package com.awspaas.user.apps.donenow_ivt.event;

import com.actionsoft.bpms.bo.engine.BO;
import com.actionsoft.bpms.bpmn.engine.core.delegate.ProcessExecutionContext;
import com.actionsoft.bpms.bpmn.engine.listener.ExecuteListener;
import com.actionsoft.bpms.bpmn.engine.model.run.delegate.ProcessInstance;
import com.actionsoft.bpms.commons.database.RowMap;
import com.actionsoft.bpms.util.DBSql;
import com.actionsoft.sdk.local.SDK;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 库存转移单保存后事件处理器
 * 功能：处理转移单保存后的库存更新逻辑，包括出库、入库及批次管理
 */
public class ivtTransferFormSaveAfter extends ExecuteListener {
    // 日志记录器
    private static final Logger logger = LoggerFactory.getLogger(ivtTransferFormSaveAfter.class);
    // 常量定义 - 流程定义ID（库存产品新增流程）
    private static final String PROCESS_DEF_ID = "obj_41f06b1e807647db8a69dd10d4be5764";
    // 常量定义 - 转移类型：仓库间转移
    private static final String TRANSFER_TYPE_WAREHOUSE = "1481";
    // 常量定义 - 状态值：已处理
    private static final String STATUS_PROCESSED = "1";

    @Override
    public String getDescription() {
        return "表单保存后，处理库存转移相关的数据更新";
    }

    /**
     * 执行库存转移单保存后的业务逻辑
     * 主要流程：校验状态 -> 处理出库库存 -> 处理入库库存（仓库间转移时）-> 更新转移单状态
     */
    @Override
    public void execute(ProcessExecutionContext processExecutionContext) throws Exception {
        // 获取流程实例ID
        String processId = processExecutionContext.getProcessInstance().getId();
        // 查询转移单主数据
        RowMap rowMap = DBSql.getMap("select * from BO_EU_DNIVT_TRANSFER where BINDID =?", new Object[]{processId});

        // 若状态已为"已处理"，直接返回（避免重复处理）
        if (STATUS_PROCESSED.equals(rowMap.getString("STATUS"))) {
            logger.info("转移单[{}]已处理，无需重复执行", processId);
            return;
        }

        // 获取SN列表（序列化产品标识）
        String sns = rowMap.getString("SN");
        Connection conn = null;

        try {
            // 获取数据库连接并关闭自动提交（开启事务）
            conn = DBSql.open();
            conn.setAutoCommit(false);

            // 获取关键业务参数
            String productId = rowMap.getString("PRODUCT_ID");
            String fromWarehouseId = rowMap.getString("FROM_WAREHOUSE_ID");
            String typeId = rowMap.getString("TYPE_ID");

            // 校验关键参数非空
            validateRequiredParams(productId, fromWarehouseId);

            // 处理非默认类型的转移（TYPE_ID不为空且不等于"0"）
            if (StringUtils.isNotBlank(typeId) && !"0".equals(typeId)) {
                // 处理出库库存及批次
                List<Map<String, Object>> outboundLots = processOutboundInventory(conn, productId, fromWarehouseId, rowMap, sns);

                // 若为仓库间转移，处理入库库存及批次
                if (TRANSFER_TYPE_WAREHOUSE.equals(typeId)) {
                    processInboundInventory(conn, processExecutionContext, productId, rowMap, outboundLots);
                }
            }

            // 更新转移单状态为"已处理"
            DBSql.update(conn, "update BO_EU_DNIVT_TRANSFER set STATUS=? where BINDID=?",
                    new Object[]{STATUS_PROCESSED, processId});

            // 提交事务
            conn.commit();
            logger.info("转移单[{}]处理成功，事务已提交", processId);

        } catch (Exception e) {
            // 异常时回滚事务
            if (conn != null) {
                try {
                    conn.rollback();
                    logger.error("转移单[{}]处理失败，事务已回滚", processId, e);
                } catch (SQLException rollbackEx) {
                    logger.error("转移单[{}]事务回滚失败", processId, rollbackEx);
                }
            }
            throw e; // 向上抛出异常，便于上层处理
        } finally {
            // 关闭数据库连接
            if (conn != null) {
                DBSql.close(conn);
            }
        }
    }

    /**
     * 校验必要参数非空
     * @param productId 产品ID
     * @param fromWarehouseId 转出仓库ID
     */
    private void validateRequiredParams(String productId, String fromWarehouseId) {
        if (StringUtils.isBlank(productId)) {
            throw new RuntimeException("产品ID不能为空");
        }
        if (StringUtils.isBlank(fromWarehouseId)) {
            throw new RuntimeException("转出仓库ID不能为空");
        }
    }

    /**
     * 处理出库库存及批次信息
     * @param conn 数据库连接
     * @param productId 产品ID
     * @param fromWarehouseId 转出仓库ID
     * @param rowMap 转移单数据
     * @param sns SN列表字符串
     * @return 出库批次信息列表
     */
    private List<Map<String, Object>> processOutboundInventory(Connection conn, String productId,
                                                               String fromWarehouseId, RowMap rowMap, String sns) {
        // 查询转出仓库的产品库存信息
        BO fromBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT")
                .connection(conn)
                .addQuery("PRODUCT_ID=", productId)
                .addQuery("WAREHOUSE_ID=", fromWarehouseId)
                .detail();

        if (fromBO == null) {
            throw new RuntimeException("转出仓库不存在该产品的库存记录");
        }

        // 计算出库后库存数量（校验库存充足性）
        int transferQuantity = rowMap.getInt("QUANTITY");
        int currentStock = parseQuantity(fromBO.get("QUANTITY"));
        int afterOutboundStock = currentStock - transferQuantity;

        if (afterOutboundStock < 0) {
            throw new RuntimeException("转出数量不能大于库存数量，当前库存：" + currentStock + "，转出：" + transferQuantity);
        }

        // 更新转出仓库的产品库存
        fromBO.set("QUANTITY", afterOutboundStock);
        SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", fromBO, conn);
        logger.debug("产品[{}]从仓库[{}]出库，出库数量：{}，剩余库存：{}",
                productId, fromWarehouseId, transferQuantity, afterOutboundStock);

        // 处理批次信息（区分序列化和非序列化产品）
        return processOutboundLots(conn, fromBO, transferQuantity, sns);
    }

    /**
     * 处理出库批次信息
     * @param conn 数据库连接
     * @param fromBO 转出仓库产品信息
     * @param transferQuantity 转移数量
     * @param sns SN列表字符串
     * @return 出库批次信息列表
     */
    private List<Map<String, Object>> processOutboundLots(Connection conn, BO fromBO,
                                                          int transferQuantity, String sns) {
        List<Map<String, Object>> outboundLots = new ArrayList<>();
        // 查询转出仓库的有效批次（数量>0）
        List<BO> lots = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT")
                .addQuery("BINDID=", fromBO.getBindId())
                .addQuery("QUANTITY>0", null)
                .orderByCreated()
                .list();

        if (StringUtils.isNotBlank(sns)) {
            // 处理序列化产品（有SN标识）
            outboundLots = processSerializedOutboundLots(conn, lots, sns);
        } else {
            // 处理非序列化产品（无SN标识，按数量处理）
            outboundLots = processNonSerializedOutboundLots(conn, lots, transferQuantity);
        }

        return outboundLots;
    }

    /**
     * 处理序列化产品的出库批次
     * @param conn 数据库连接
     * @param lots 批次列表
     * @param sns SN列表字符串
     * @return 出库批次信息列表
     */
    private List<Map<String, Object>> processSerializedOutboundLots(Connection conn, List<BO> lots, String sns) {
        List<Map<String, Object>> outboundLots = new ArrayList<>();
        String[] snArray = sns.split(",");
        Set<String> snSet = new HashSet<>(Arrays.asList(snArray)); // 使用Set提高匹配效率
        int processedSnCount = 0; // 已处理的SN数量

        for (BO lot : lots) {
            if (processedSnCount >= snArray.length) {
                break; // 所有SN处理完毕，退出循环
            }

            String lotSN = lot.getString("SN");
            if (StringUtils.isBlank(lotSN)) {
                continue; // 批次无SN，跳过
            }

            // 处理当前批次的SN
            Set<String> currentLotSnSet = new HashSet<>(Arrays.asList(lotSN.split(",")));
            List<String> matchedSns = new ArrayList<>(); // 本批次中匹配的SN

            // 筛选本批次中需要转移的SN
            for (String sn : snArray) {
                if (currentLotSnSet.contains(sn) && snSet.contains(sn)) {
                    matchedSns.add(sn);
                    snSet.remove(sn); // 标记为已处理
                    processedSnCount++;
                    if (processedSnCount >= snArray.length) {
                        break;
                    }
                }
            }

            if (!matchedSns.isEmpty()) {
                // 更新当前批次的SN和数量（移除已转移的SN）
                currentLotSnSet.removeAll(matchedSns);
                String updatedSn = StringUtils.join(currentLotSnSet, ",");
                lot.set("SN", updatedSn);
                lot.set("QUANTITY", currentLotSnSet.size());
                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);

                // 记录出库批次信息（用于入库处理）
                Map<String, Object> lotMap = new HashMap<>(lot.asMap());
                lotMap.remove("ID");
                lotMap.remove("BINDID");
                lotMap.put("SN", StringUtils.join(matchedSns, ","));
                lotMap.put("QUANTITY", matchedSns.size());
                outboundLots.add(lotMap);
            }
        }

        // 校验是否所有SN都已处理
        if (processedSnCount < snArray.length) {
            throw new RuntimeException("部分SN未找到对应批次，已处理：" + processedSnCount + "，总需处理：" + snArray.length);
        }

        return outboundLots;
    }

    /**
     * 处理非序列化产品的出库批次
     * @param conn 数据库连接
     * @param lots 批次列表
     * @param remainingTransferQuantity 剩余待转移数量
     * @return 出库批次信息列表
     */
    private List<Map<String, Object>> processNonSerializedOutboundLots(Connection conn, List<BO> lots,
                                                                       int remainingTransferQuantity) {
        List<Map<String, Object>> outboundLots = new ArrayList<>();
        int transferLeft = remainingTransferQuantity;

        for (BO lot : lots) {
            if (transferLeft <= 0) {
                break; // 所有数量处理完毕，退出循环
            }

            int lotQuantity = parseQuantity(lot.get("QUANTITY"));
            if (lotQuantity <= 0) {
                continue; // 批次数量为0，跳过
            }

            // 计算本批次需要转移的数量
            int transferQty = Math.min(lotQuantity, transferLeft);
            int remainingQty = lotQuantity - transferQty;

            // 更新当前批次的剩余数量
            lot.set("QUANTITY", remainingQty);
            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);

            // 记录出库批次信息（用于入库处理）
            Map<String, Object> lotMap = new HashMap<>(lot.asMap());
            lotMap.remove("ID");
            lotMap.remove("BINDID");
            lotMap.put("SN", "");
            lotMap.put("QUANTITY", transferQty);
            outboundLots.add(lotMap);

            // 更新剩余待转移数量
            transferLeft -= transferQty;
        }

        // 校验是否所有数量都已处理
        if (transferLeft > 0) {
            throw new RuntimeException("批次库存不足，剩余未转移数量：" + transferLeft);
        }

        return outboundLots;
    }

    /**
     * 处理入库库存及批次信息（仓库间转移时）
     * @param conn 数据库连接
     * @param context 流程执行上下文
     * @param productId 产品ID
     * @param rowMap 转移单数据
     * @param inboundLots 待入库批次信息列表
     */
    private void processInboundInventory(Connection conn, ProcessExecutionContext context,
                                         String productId, RowMap rowMap, List<Map<String, Object>> inboundLots) {
        String toWarehouseId = rowMap.getString("TO_WAREHOUSE_ID");
        if (StringUtils.isBlank(toWarehouseId)) {
            throw new RuntimeException("转入仓库不能为空");
        }

        // 查询或创建转入仓库的产品库存记录
        BO toBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT")
                .connection(conn)
                .addQuery("PRODUCT_ID=", productId)
                .addQuery("WAREHOUSE_ID=", toWarehouseId)
                .detail();

        int transferQuantity = rowMap.getInt("QUANTITY");
        if (toBO == null) {
            // 转入仓库无该产品库存，创建新记录
            ProcessInstance processInstance = SDK.getProcessAPI()
                    .createBOProcessInstance(PROCESS_DEF_ID, context.getUserContext().getUID(),
                            "新增库存产品[" + productId + "]仓库间转移至[" + toWarehouseId + "]");
            toBO = new BO();
            toBO.set("PRODUCT_ID", productId);
            toBO.set("WAREHOUSE_ID", toWarehouseId);
            toBO.set("QUANTITY", transferQuantity);
            toBO.setBindId(processInstance.getId());
            SDK.getBOAPI().create("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toBO, processInstance.getId(),
                    context.getUserContext().getUID(), conn);
            logger.debug("产品[{}]在转入仓库[{}]创建新库存记录，初始数量：{}",
                    productId, toWarehouseId, transferQuantity);
        } else {
            // 转入仓库已有该产品库存，更新数量
            int currentStock = parseQuantity(toBO.get("QUANTITY"));
            toBO.set("QUANTITY", currentStock + transferQuantity);
            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toBO, conn);
            logger.debug("产品[{}]在转入仓库[{}]更新库存，原库存：{}，新增：{}，现库存：{}",
                    productId, toWarehouseId, currentStock, transferQuantity, currentStock + transferQuantity);
        }

        // 处理入库批次信息
        processInboundLots(conn, context, toBO, inboundLots);
    }

    /**
     * 处理入库批次信息
     * @param conn 数据库连接
     * @param context 流程执行上下文
     * @param toBO 转入仓库产品信息
     * @param inboundLots 待入库批次信息列表
     */
    private void processInboundLots(Connection conn, ProcessExecutionContext context,
                                    BO toBO, List<Map<String, Object>> inboundLots) {
        for (Map<String, Object> lotData : inboundLots) {
            String lotNo = (String) lotData.get("LOT");
            String orderProductId = (String) lotData.get("ORDER_PRODUCT_ID");

            // 查询转入仓库是否已有相同批次
            BO toLot = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT")
                    .addQuery("LOT=", lotNo)
                    .addQuery("ORDER_PRODUCT_ID=", orderProductId)
                    .addQuery("BINDID=", toBO.getBindId())
                    .detail();

            if (toLot != null) {
                // 已有相同批次，更新数量和SN
                int currentQty = parseQuantity(toLot.get("QUANTITY"));
                int addQty = parseQuantity(lotData.get("QUANTITY"));
                toLot.set("QUANTITY", currentQty + addQty);

                String existingSn = toLot.getString("SN");
                String newSn = (String) lotData.get("SN");
                if (StringUtils.isNotBlank(newSn)) {
                    toLot.set("SN", StringUtils.isBlank(existingSn) ? newSn : existingSn + "," + newSn);
                }
                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, conn);
            } else {
                // 无相同批次，创建新批次
                BO newLot = new BO();
                newLot.setAll(lotData);
                newLot.setBindId(toBO.getBindId());
                newLot.set("ORDER_PRODUCT_ID", lotData.get("ORDER_PRODUCT_ID"));
                newLot.set("WAREHOUSE_ID", toBO.get("WAREHOUSE_ID"));
                newLot.set("PRODUCT_ID", toBO.get("PRODUCT_ID"));
                SDK.getBOAPI().createDataBO("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", newLot,
                        context.getUserContext(), conn);
            }
        }
        logger.debug("转入仓库批次处理完成，共处理{}个批次", inboundLots.size());
    }

    /**
     * 安全解析数量（避免空指针和类型转换异常）
     * @param quantityObj 数量对象
     * @return 解析后的整数数量
     */
    private int parseQuantity(Object quantityObj) {
        if (quantityObj == null) {
            return 0;
        }
        if (quantityObj instanceof Number) {
            return ((Number) quantityObj).intValue();
        }
        try {
            return Integer.parseInt(quantityObj.toString().trim());
        } catch (NumberFormatException e) {
            throw new RuntimeException("数量格式错误：" + quantityObj, e);
        }
    }
}