package com.ffii.fpsms.modules.stock.service

import com.ffii.fpsms.modules.master.service.ItemUomService
import com.ffii.fpsms.modules.pickOrder.entity.PickOrder

import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue  //  添加
import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory
import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus  //  添加
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository
import com.ffii.fpsms.modules.stock.entity.*
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo
import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType
import com.ffii.fpsms.modules.stock.web.model.SaveSuggestedPickLotRequest
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPoRequest
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPolRequest
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotResponse
import com.ffii.fpsms.modules.master.entity.BomRepository
import org.springframework.stereotype.Service
import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime  //  添加
import java.time.format.DateTimeFormatter  //  添加
import kotlin.jvm.optionals.getOrDefault
import kotlin.jvm.optionals.getOrNull
import org.springframework.transaction.annotation.Transactional
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import java.math.RoundingMode 
import com.ffii.fpsms.modules.stock.entity.InventoryRepository
import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus
import com.ffii.fpsms.modules.stock.web.model.PickAnotherLotRequest
import com.ffii.fpsms.modules.stock.service.InventoryLotLineService
import com.ffii.fpsms.modules.stock.entity.FailInventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.StockOutRepository
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus
import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo  
import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository
import com.ffii.fpsms.modules.stock.web.model.StockOutStatus
import com.ffii.fpsms.modules.common.SecurityUtils
import com.ffii.fpsms.modules.deliveryOrder.service.DoFloorSupplierSettingsService
@Service
open class SuggestedPickLotService(
    val suggestedPickLotRepository: SuggestPickLotRepository,
    val stockOutLIneRepository: StockOutLIneRepository,
    val inventoryLotLineRepository: InventoryLotLineRepository,
    val pickOrderLineRepository: PickOrderLineRepository,
    val bomRepository: BomRepository,
    val inventoryLotLineService: InventoryLotLineService,
    val itemUomService: ItemUomService,
    val pickExecutionIssueRepository: PickExecutionIssueRepository,  //  添加逗号
    val pickOrderRepository: PickOrderRepository,
    val inventoryRepository: InventoryRepository,
    val failInventoryLotLineRepository: FailInventoryLotLineRepository,
    val stockOutRepository: StockOutRepository,
    val itemRepository: ItemsRepository,
    val stockOutLineRepository: StockOutLIneRepository,
    private val doFloorSupplierSettingsService: DoFloorSupplierSettingsService,
) {

    // Calculation Available Qty / Remaining Qty
    open fun calculateRemainingQtyForInfo(inventoryLotLine: InventoryLotLineInfo?): BigDecimal {
        val zero = BigDecimal.ZERO
        return (inventoryLotLine?.inQty ?: zero)
            .minus(inventoryLotLine?.outQty ?: zero)
            .minus((inventoryLotLine?.holdQty ?: zero))
    }

    open fun calculateRemainingQty(inventoryLotLine: InventoryLotLine?): BigDecimal {
        val zero = BigDecimal.ZERO
        return (inventoryLotLine?.inQty ?: zero)
            .minus(inventoryLotLine?.outQty ?: zero)
            .minus((inventoryLotLine?.holdQty ?: zero))
    }

    // Suggestion
    open fun suggestionForPickOrders(request: SuggestedPickLotForPoRequest): SuggestedPickLotResponse {
        val pos = request.pickOrders
        val suggestedList = mutableListOf<SuggestedPickLot>()
        var holdQtyMap = request.holdQtyMap

        pos.forEach {
            val response = suggestionForPickOrderLines(SuggestedPickLotForPolRequest(
                holdQtyMap = holdQtyMap,
                pickOrderLines = it.pickOrderLines
            ))

            holdQtyMap = response.holdQtyMap
            suggestedList += response.suggestedList
        }

        return SuggestedPickLotResponse(holdQtyMap = holdQtyMap,  suggestedList = suggestedList)
    }

    /** FP-MTMS Version Checklist | Functions Ref. No. 32 | v1.0.1 | 2026-07-22 */
    open fun suggestionForPickOrderLines(request: SuggestedPickLotForPolRequest): SuggestedPickLotResponse {
        val pols = request.pickOrderLines
        val itemIds = pols.mapNotNull { it.item?.id }
        val zero = BigDecimal.ZERO
        val one = BigDecimal.ONE
        val today = LocalDate.now()
    
        val suggestedList: MutableList<SuggestedPickLot> = mutableListOf()
        val holdQtyMap: MutableMap<Long?, BigDecimal?> = request.holdQtyMap
        val excludedWarehouseCodes = setOf<String>(
            "2F-W202-01-00",
            "2F-W200-#A-00",
        )
        val availableInventoryLotLines = inventoryLotLineService
            .allInventoryLotLinesByItemIdIn(itemIds)
            .filter { it.status == InventoryLotLineStatus.AVAILABLE.value }
            .filter { (it.inQty ?: zero).minus(it.outQty ?: zero).minus(it.holdQty ?: zero) > zero }
            .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today)}
            .sortedBy { it.expiryDate }
            .groupBy { it.item?.id }

        val (floorSuppliers2F, floorSuppliers4F) = doFloorSupplierSettingsService.loadDoFloorSupplierLists()
    
        // loop for suggest pick lot line
        pols.forEach { line ->
            
            val salesUnit = line.item?.id?.let { itemUomService.findSalesUnitByItemId(it) }
            val polUomId = line.uom?.id
            val lotLines = availableInventoryLotLines[line.item?.id].orEmpty()
                .filter { polUomId == null || it.uomId == polUomId }
            val ratio = one // (salesUnit?.ratioN ?: one).divide(salesUnit?.ratioD ?: one, 10, RoundingMode.HALF_UP)
            val pickOrder = line.pickOrder
            val isDoPickOrder = pickOrder?.type?.value == "do" || pickOrder?.type?.value == "delivery_order"
        
            val doPreferredFloor: String? = if (isDoPickOrder) {
                val supplierCode = pickOrder?.deliveryOrder?.supplier?.code
                doFloorSupplierSettingsService.preferredFloorForPickLotOrNull(
                    supplierCode,
                    floorSuppliers2F,
                    floorSuppliers4F,
                )
            } else {
                null
            }
        
            //  FIX: Calculate remaining quantity needed (not the full required quantity)
            val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(line.id!!)
            val totalPickedQty = stockOutLines
            .filter { 
                it.status == "completed" || 
                it.status == "partially_completed" || 
                (it.status == "rejected" && (it.qty ?: zero) > zero)  //  包含已 picked 的 rejected
            }
            .sumOf { it.qty ?: zero }
            val requiredQty = line.qty ?: zero
            val remainingQty = requiredQty.minus(totalPickedQty)
            
            println("=== SUGGESTION DEBUG for Pick Order Line ${line.id} ===")
            println("Required qty: $requiredQty")
            println("Total picked qty: $totalPickedQty")
            println("Remaining qty needed: $remainingQty")
            println("Stock out lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}")
            
            //  FIX: Use remainingQty instead of line.qty
            var remainingQtyToAllocate = remainingQty
            println("remaining1 $remainingQtyToAllocate (sales units)")
            val updatedLotLines = mutableListOf<InventoryLotLineInfo>()
    
            lotLines.forEachIndexed { index, lotLine ->
                if (remainingQtyToAllocate <= zero) return@forEachIndexed
    
                println("calculateRemainingQtyForInfo(lotLine) ${calculateRemainingQtyForInfo(lotLine)}")
                val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() }
                val warehouseStoreId=inventoryLotLine?.warehouse?.store_id
                // 3F filter only applies to delivery order release; no limit for job order, AssignAndRelease, etc.
                val isDeliveryOrderPick = line.pickOrder?.type?.value == "do"
                val warehouseCodeForExclusion = inventoryLotLine?.warehouse?.code
                if (isDeliveryOrderPick &&
                    warehouseCodeForExclusion != null &&
                    excludedWarehouseCodes.contains(warehouseCodeForExclusion)
                ) {
                    return@forEachIndexed
                }
                if (warehouseStoreId == "3F" && isDeliveryOrderPick) {
                    return@forEachIndexed
                }
                if (doPreferredFloor != null &&
                warehouseStoreId != "1F" &&
                warehouseStoreId != doPreferredFloor
            ) {
                // 例：首选 2F，但是 lot 在 4F -> 跳过
                return@forEachIndexed
            }
                //  修复：计算可用数量，转换为销售单位
                val availableQtyInBaseUnits = calculateRemainingQtyForInfo(lotLine)
                val holdQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero
                val availableQtyInSalesUnits = availableQtyInBaseUnits
                    .minus(holdQtyInBaseUnits)
                    .divide(ratio, 0, RoundingMode.HALF_UP)
                
                println("holdQtyMap[lotLine.id] ?: zero ${holdQtyMap[lotLine.id] ?: zero}")
    
                if (availableQtyInSalesUnits <= zero) {
                    updatedLotLines += lotLine
                    return@forEachIndexed
                }
                println("$index : ${lotLine.id}")
               // val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() }
                val originalHoldQty = inventoryLotLine?.holdQty
    
                //  修复：在销售单位中计算分配数量
                val assignQtyInSalesUnits = minOf(availableQtyInSalesUnits, remainingQtyToAllocate)
                remainingQtyToAllocate = remainingQtyToAllocate.minus(assignQtyInSalesUnits)
                val newHoldQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero 
                //  修复：将销售单位转换为基础单位来更新 holdQty
                val assignQtyInBaseUnits = assignQtyInSalesUnits.multiply(ratio)
                holdQtyMap[lotLine.id] = (holdQtyMap[lotLine.id] ?: zero).plus(assignQtyInBaseUnits)
                
                suggestedList += SuggestedPickLot().apply {
                    type = SuggestedPickLotType.PICK_ORDER
                    suggestedLotLine = inventoryLotLine
                    pickOrderLine = line
                    qty = assignQtyInSalesUnits //  保存销售单位
                }
            }
            //  修复：计算现有 suggestions 中 pending/checked 状态满足的数量
                var existingSatisfiedQty = BigDecimal.ZERO

                // 查询现有的 suggestions 用于这个 pick order line
                val existingSuggestions = suggestedPickLotRepository.findAllByPickOrderLineId(line.id!!)
                existingSuggestions.forEach { existingSugg ->
                    if (existingSugg.suggestedLotLine?.id != null) {
                        val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
                            line.id!!, existingSugg.suggestedLotLine?.id!!
                        )
                        val canCountAsSatisfied = stockOutLines.isEmpty() || stockOutLines.any { 
                            it.status == "pending" || it.status == "checked" || it.status == "partially_completed"
                        }
                        
                        if (canCountAsSatisfied) {
                            existingSatisfiedQty = existingSatisfiedQty.plus(existingSugg.qty ?: BigDecimal.ZERO)
                        }
                    }
                }

                //  调整 remainingQtyToAllocate，减去已经通过现有 suggestions 满足的数量
                remainingQtyToAllocate = remainingQtyToAllocate.minus(existingSatisfiedQty)
                println("Existing satisfied qty: $existingSatisfiedQty")
                println("Adjusted remaining qty: $remainingQtyToAllocate")

                // if still have remainingQty
                println("remaining2 $remainingQtyToAllocate (sales units)")

            // if still have remainingQty
            println("remaining2 $remainingQtyToAllocate (sales units)")
            if (remainingQtyToAllocate > zero) {
                suggestedList += SuggestedPickLot().apply {
                    type = SuggestedPickLotType.PICK_ORDER
                    suggestedLotLine = null
                    pickOrderLine = line
                    qty = remainingQtyToAllocate //  保存销售单位
                }
                try {
                    /*
                    val pickOrder = line.pickOrder
                    if (pickOrder != null) {
                        createInsufficientStockIssue(
                            pickOrder = pickOrder,
                            pickOrderLine = line,
                            insufficientQty = remainingQtyToAllocate
                        )
                    }
                    */
                   } catch (e: Exception) {
                    println("❌ Error creating insufficient stock issue: ${e.message}")
                    e.printStackTrace()
                }
            }
        }
        return SuggestedPickLotResponse(holdQtyMap = holdQtyMap,  suggestedList = suggestedList)
    }
    /** FP-MTMS Version Checklist | Functions Ref. No. 32 | v1.0.1 | 2026-07-22 */
    open fun suggestionForPickOrderLinesForJobOrder(request: SuggestedPickLotForPolRequest): SuggestedPickLotResponse {
        val pols = request.pickOrderLines
        val itemIds = pols.mapNotNull { it.item?.id }
        val wipItemIdSet: Set<Long> = bomRepository.findWipItemIds(itemIds).toSet()
        val zero = BigDecimal.ZERO
        val one = BigDecimal.ONE
        val today = LocalDate.now()
    
        val suggestedList: MutableList<SuggestedPickLot> = mutableListOf()
        val holdQtyMap: MutableMap<Long?, BigDecimal?> = request.holdQtyMap
        val excludedWarehouseCodes = setOf<String>(
            "2F-W202-01-00",
            // "4F-W401-#C-00",
        )
        // get current inventory lot line qty & grouped by item id
        val availableInventoryLotLines = inventoryLotLineService
            .allInventoryLotLinesByItemIdIn(itemIds)
            .filter { it.status == InventoryLotLineStatus.AVAILABLE.value }
            .filter { (it.inQty ?: zero).minus(it.outQty ?: zero).minus(it.holdQty ?: zero) > zero }
            .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today)}
            .sortedBy { it.expiryDate }
            .groupBy { it.item?.id }
    
        // loop for suggest pick lot line
        pols.forEach { line ->
            val itemId = line.item?.id
            val isWipItem = itemId != null && wipItemIdSet.contains(itemId)
            val polUomId = line.uom?.id

            val lotInfos = availableInventoryLotLines[itemId].orEmpty()
                .filter { polUomId == null || it.uomId == polUomId }
            val lotIds = lotInfos.mapNotNull { it.id }
            // 仅一次查询，避免后续重复 findById
            val lotEntities = if (lotIds.isNotEmpty()) inventoryLotLineRepository.findAllById(lotIds) else emptyList()
            val lotEntityById = lotEntities.associateBy { it.id }
        
            // 计算“默认优先仓位”（短期固定，后续可从 Items/配置取）
            val preferredWarehouseCode = if (isWipItem) "2F-W200-#A-00" else null
        
            // 分组：先默认仓位，再其他；组内保持 FEFO 顺序（lotInfos 已按 expiry 排过序）
            val (preferred, others) = if (!preferredWarehouseCode.isNullOrBlank()) {
                lotInfos.partition { li -> lotEntityById[li.id]?.warehouse?.code == preferredWarehouseCode }
            } else {
                Pair(emptyList(), lotInfos)
            }
            val orderedLotInfos = preferred + others
        
            // 分配时直接用 entity（不再 findById）
            var remainingQtyToAllocate = (line.qty ?: BigDecimal.ZERO).minus(
                stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(line.id!!)
                    .filter { it.status == "completed" || it.status == "partially_completed" || (it.status == "rejected" && (it.qty ?: BigDecimal.ZERO) > BigDecimal.ZERO) }
                    .sumOf { it.qty ?: BigDecimal.ZERO }
            )
        
            for (lotInfo in orderedLotInfos) {
                if (remainingQtyToAllocate <= BigDecimal.ZERO) break
                val lot = lotEntityById[lotInfo.id] ?: continue
        
                val warehouseCode = lot.warehouse?.code
                val storeId = lot.warehouse?.store_id
        
                // 既有规则
                if (warehouseCode != null && excludedWarehouseCodes.contains(warehouseCode)) continue
                val isJobOrder = line.pickOrder?.type?.value == "jo"
                if (storeId == "3F" && !isJobOrder) continue
        
                val availableQtyInBase = (lot.inQty ?: BigDecimal.ZERO)
                    .minus(lot.outQty ?: BigDecimal.ZERO)
                    .minus(lot.holdQty ?: BigDecimal.ZERO)
                val holdQtyInBase = holdQtyMap[lotInfo.id] ?: BigDecimal.ZERO
                val availableInSales = availableQtyInBase.minus(holdQtyInBase) // ratio 目前为 1
        
                if (availableInSales <= BigDecimal.ZERO) continue
        
                val assign = availableInSales.min(remainingQtyToAllocate)
                remainingQtyToAllocate = remainingQtyToAllocate.minus(assign)
        
                holdQtyMap[lotInfo.id] = (holdQtyMap[lotInfo.id] ?: BigDecimal.ZERO).plus(assign)
        
                suggestedList += SuggestedPickLot().apply {
                    type = SuggestedPickLotType.PICK_ORDER
                    suggestedLotLine = lot
                    pickOrderLine = line
                    qty = assign
                }
            }
        
            if (remainingQtyToAllocate > BigDecimal.ZERO) {
                suggestedList += SuggestedPickLot().apply {
                    type = SuggestedPickLotType.PICK_ORDER
                    suggestedLotLine = null
                    pickOrderLine = line
                    qty = remainingQtyToAllocate
                }
            }
        }
    
        return SuggestedPickLotResponse(holdQtyMap = holdQtyMap, suggestedList = suggestedList)
    }

    // Convertion
    open fun convertRequestsToEntities(request: List<SaveSuggestedPickLotRequest>): List<SuggestedPickLot> {
//        val zero = BigDecimal.ZERO
//        val entities = mutableListOf<SuggestedPickLot>()
//        val holdQtyCount = mutableMapOf<Long?, BigDecimal?>()
//        request.forEach {
//            val entity = convertRequestToEntity(it)
//            val suggestedLotLine = entity.suggestedLotLine
//            val remainQty = calculateRemainingQty(suggestedLotLine)
//            holdQtyCount[suggestedLotLine?.id] = (holdQtyCount[suggestedLotLine?.id] ?: zero).plus(suggestedLotLine?.holdQty ?: zero)
//            if (remainQty.minus(holdQtyCount[suggestedLotLine?.id] ?: zero) < zero) {
//                throw RuntimeException("The suggested pick qty is over. Please re-suggest the pick qty.");
//            }
//            entities += entity
//        }
//        return entities
        return request.map { convertRequestToEntity(it) }
    }

    open fun convertRequestToEntity(request: SaveSuggestedPickLotRequest): SuggestedPickLot{
        val suggestedPickLot =
            request.id?.let { id -> suggestedPickLotRepository.findById(id).getOrDefault(SuggestedPickLot()) }
                ?: SuggestedPickLot()
        val type = request.type?.let { _type -> SuggestedPickLotType.entries.find { it.value == _type } }
        val stockOutLine = request.stockOutLineId?.let { id -> stockOutLIneRepository.findById(id).getOrNull() }
        val suggestedLotLine =
            request.suggestedLotLineId?.let { id -> inventoryLotLineRepository.findById(id).getOrNull() }
        val pickOrderLine = request.pickOrderLineId?.let { id -> pickOrderLineRepository.findById(id).getOrNull() }

        suggestedPickLot.apply {
            this.type = type
            this.stockOutLine = stockOutLine
            this.suggestedLotLine = suggestedLotLine
            this.pickOrderLine = pickOrderLine
            qty = request.qty
        }

        return suggestedPickLot
    }

    // Save
    open fun saveSuggestedPickLot(request: SaveSuggestedPickLotRequest): SuggestedPickLot {
        val suggestedPickLot = convertRequestToEntity(request)

        return suggestedPickLotRepository.save(suggestedPickLot)
    }

    open fun saveAll(request: List<SuggestedPickLot>): List<SuggestedPickLot> {
        val saved = suggestedPickLotRepository.saveAllAndFlush(request)

        // For insufficient stock (suggestedLotLine == null), create a no-lot stock_out_line so UI can display & close the line.
        // Also backfill SuggestedPickLot.stockOutLineId for downstream flows (e.g. hierarchical API -> stockouts).
        val toBackfill = saved.filter { it.suggestedLotLine == null && it.pickOrderLine != null }
        if (toBackfill.isNotEmpty()) {
            val updated = mutableListOf<SuggestedPickLot>()
            toBackfill.forEach { spl ->
                val pickOrder = spl.pickOrderLine?.pickOrder
                if (pickOrder == null) return@forEach

                // Only create/backfill when stockOutLine is missing
                if (spl.stockOutLine == null) {
                    val sol = createStockOutLineForSuggestion(spl, pickOrder)
                    if (sol != null) {
                        spl.stockOutLine = sol
                        updated.add(spl)
                    }
                }
            }
            if (updated.isNotEmpty()) {
                suggestedPickLotRepository.saveAllAndFlush(updated)
            }
        }

        return saved
    }
    private fun createStockOutLineForSuggestion(
    suggestion: SuggestedPickLot,
    pickOrder: PickOrder
): StockOutLine? {
    try {
        val suggestedLotLine = suggestion.suggestedLotLine
        val pickOrderLine = suggestion.pickOrderLine
        
        // FIX: Allow creating stock out line even when suggestedLotLine is null (insufficient stock case)
        if (pickOrderLine == null) {
            println("Cannot create stock out line: missing pickOrderLine")
            return null
        }
        
        // Get pick order type value (do, job, material, etc.)
        val pickOrderTypeValue = pickOrder.type?.value ?: "do"
        
        // If suggestedLotLine is null, create stock out line with inventoryLotLineId = null
        if (suggestedLotLine == null) {
            println("Creating stock out line with no lot (insufficient stock) for pickOrderLineId: ${pickOrderLine.id}, type: $pickOrderTypeValue")
            
            // Check if stock out line already exists for this pick order line with null lot
            val existingStockOutLineInfo = stockOutLIneRepository
                .findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!)
                .find { it.inventoryLotLineId == null }
            
            if (existingStockOutLineInfo != null) {
                println("Stock out line already exists for pickOrderLineId: ${pickOrderLine.id} with no lot")
                // Get the actual entity by ID
                return stockOutLIneRepository.findById(existingStockOutLineInfo.id!!).orElse(null)
            }
            
            // Get or create StockOut

            val consoCode = (pickOrder.consoCode ?: "").trim()
            // Fallback when consoCode is blank (prevents querying "" and always getting null)
            val stockOutKey = if (consoCode.isNotEmpty()) consoCode else "PO-${pickOrder.id ?: 0L}"

            val stockOut = stockOutRepository.findFirstByConsoPickOrderCodeOrderByIdDesc(stockOutKey)
                ?: run {
                    val handlerId = pickOrder.assignTo?.id ?: SecurityUtils.getUser().orElse(null)?.id
                    require(handlerId != null) { "Cannot create StockOut: handlerId is null" }
                    val newStockOut = StockOut().apply {
                        this.consoPickOrderCode = stockOutKey
                        this.type = pickOrderTypeValue
                        this.status = StockOutStatus.PENDING.status
                        this.handler = handlerId
                    }
                    stockOutRepository.save(newStockOut)
                }

            val item = pickOrderLine.item ?: itemRepository.findById(pickOrderLine.item!!.id!!).orElseThrow()
            // Create stock out line with inventoryLotLineId = null
            val stockOutLine = StockOutLine().apply {
                this.stockOut = stockOut
                this.pickOrderLine = pickOrderLine
                this.item = item
                this.inventoryLotLine = null  // No lot available
                // qty on StockOutLine represents picked qty; for no-lot placeholder it must start from 0
                this.qty = 0.0
                this.status = StockOutLineStatus.PENDING.status
                this.deleted = false
                this.type = "NOR"
            }
            
            val savedStockOutLine = stockOutLIneRepository.save(stockOutLine)
            println("Created stock out line ${savedStockOutLine.id} with no lot for pickOrderLineId: ${pickOrderLine.id}")
            return savedStockOutLine
        }
        
        // Original logic for when suggestedLotLine is not null
        // Check if stock out line already exists
        val existingStockOutLine = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
            pickOrderLine.id!!,
            suggestedLotLine.id!!
        )
        
        if (existingStockOutLine.isNotEmpty()) {
            println("Stock out line already exists for pickOrderLineId: ${pickOrderLine.id}, inventoryLotLineId: ${suggestedLotLine.id}")
            return existingStockOutLine.first()
        }
        /* 
        // Get or create StockOut
        val stockOut = stockOutRepository.findFirstByConsoPickOrderCodeOrderByIdDesc(pickOrder.consoCode ?: "")
            .orElseGet {
                // Create new StockOut if it doesn't exist
                val newStockOut = StockOut().apply {
                    this.consoPickOrderCode = pickOrder.consoCode ?: ""
                    this.type = pickOrderTypeValue  // Use pick order type (do, job, material, etc.)
                    this.status = StockOutStatus.PENDING.status
                }
                stockOutRepository.save(newStockOut)
            }
         */
        val stockOut = stockOutRepository.findFirstByConsoPickOrderCodeOrderByIdDesc(pickOrder.consoCode ?: "")
        if (stockOut == null) {
            println(" StockOut not found for consoCode=${pickOrder.consoCode}, skip creating stockOutLine")
            return null
        }

        // Update pick order line status to PICKING
        val updatedPickOrderLine = pickOrderLineRepository.saveAndFlush(
            pickOrderLine.apply {
                this.status = PickOrderLineStatus.PICKING
            }
        )
        
        // Get item
        val item = itemRepository.findById(updatedPickOrderLine.item!!.id!!).orElseThrow()
        
        // Create stock out line
        val stockOutLine = StockOutLine().apply {
            this.item = item
            this.qty = 0.0
            this.stockOut = stockOut
            this.inventoryLotLine = suggestedLotLine
            this.pickOrderLine = updatedPickOrderLine
            this.status = StockOutLineStatus.PENDING.status
            this.type = "NOR"
        }
        
        val savedStockOutLine = stockOutLIneRepository.saveAndFlush(stockOutLine)
        println(" Created stock out line ID: ${savedStockOutLine.id} for suggestion ID: ${suggestion.id}")
        
        return savedStockOutLine
        
    } catch (e: Exception) {
        println("❌ Error creating stock out line for suggestion: ${e.message}")
        e.printStackTrace()
        return null
    }
}
/** FP-MTMS Version Checklist | Functions Ref. No. 32 | v1.0.1 | 2026-07-22 */
@Transactional(rollbackFor = [Exception::class])
open fun resuggestPickOrder(pickOrderId: Long): MessageResponse {
    try {
        val pickOrder = pickOrderRepository.findById(pickOrderId).orElseThrow()
        
        println("=== RESUGGEST DEBUG START ===")
        println("Pick Order ID: $pickOrderId")
        println("Pick Order Code: ${pickOrder.code}")
        println("Pick Order Status: ${pickOrder.status}")
        
       //  NEW: Get ALL pick orders for the same items
val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct()
println("Item IDs in current pick order: $itemIds")

// 收集所有競爭 pick order，並按 id 去重
// ✅ 优化：只考虑活跃的 pick orders（排除已完成和已取消的）
val allCompetingPickOrders = itemIds.flatMap { itemId ->
    val competingOrders = pickOrderLineRepository.findAllPickOrdersByItemId(itemId)
        .filter { 
            it.id != pickOrderId && // Exclude current pick order
            it.status != PickOrderStatus.COMPLETED
        }
    if (competingOrders.isNotEmpty()) {
        println("Found ${competingOrders.size} active competing pick orders for item $itemId")
    }
    competingOrders
}.distinctBy { it.id }

// basePickOrders 也做一次去重，避免後面 filter 產生大量重複記錄
val basePickOrders = (listOf(pickOrder) + allCompetingPickOrders).distinctBy { it.id }

        val allPickOrderLineIdsToCheck = basePickOrders
            .flatMap { it.pickOrderLines }
            .mapNotNull { it.id }
            .distinct()
        
        println("Checking ${allPickOrderLineIdsToCheck.size} pick order lines for rejected stock out lines")
        
        //  OPTIMIZATION 3: 批量查询所有 stock out lines（一次查询代替 N*M 次查询）
        val allStockOutLines = if (allPickOrderLineIdsToCheck.isNotEmpty()) {
            stockOutLIneRepository.findAllByPickOrderLineIdInAndDeletedFalse(allPickOrderLineIdsToCheck)
        } else {
            emptyList()
        }
        
        println("Found ${allStockOutLines.size} stock out lines in batch query (1 query instead of ${allPickOrderLineIdsToCheck.size} queries)")
        
        //  OPTIMIZATION 3: 按 pickOrderLineId 分组，便于后续查找
        val stockOutLinesByPickOrderLineId = allStockOutLines
            .groupBy { it.pickOrderLine?.id }
        
        //  NEW: 預先查出 basePickOrders 所有行對應的 suggestions，用來判斷是否用了 UNAVAILABLE lot
        val allSuggestionsForBaseOrders = if (allPickOrderLineIdsToCheck.isNotEmpty()) {
            suggestedPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIdsToCheck)
        } else {
            emptyList()
        }
        
        val suggestionsByPickOrderLineId = allSuggestionsForBaseOrders.groupBy { it.pickOrderLine?.id }
        val pickOrderIdsWithChanges = mutableSetOf<Long>()
        //  FIX: 重算條件 = 有 rejected 行，或者 有使用 UNAVAILABLE lot 的 suggestion
        val allPickOrdersToResuggest = basePickOrders.filter { po ->
            val hasRejected = po.pickOrderLines.any { pol ->
                val stockOutLines = stockOutLinesByPickOrderLineId[pol.id] ?: emptyList()
                val hasRejectedStockOutLine = stockOutLines.any {
                    it.status?.equals("rejected", ignoreCase = true) == true
                }
        
                if (hasRejectedStockOutLine) {
                    println("Pick Order ${po.code} has rejected stock out lines - will resuggest")
                    stockOutLines
                        .filter { it.status?.equals("rejected", ignoreCase = true) == true }
                        .forEach { sol ->
                            println("  - Rejected stock out line: ${sol.id} (lot: ${sol.inventoryLotLine?.id}, qty: ${sol.qty})")
                        }
                }
        
                hasRejectedStockOutLine
            }
        
            val hasUnavailableLot = po.pickOrderLines.any { pol ->
                val suggestions = suggestionsByPickOrderLineId[pol.id] ?: emptyList()
                val usedUnavailable = suggestions.any {
                    it.suggestedLotLine?.status == InventoryLotLineStatus.UNAVAILABLE
                }
                if (usedUnavailable) {
                    println("Pick Order ${po.code} has suggestions using UNAVAILABLE lots - will resuggest")
                }
                usedUnavailable
            }
        
            hasRejected || hasUnavailableLot
        }.distinctBy { it.id } 
        
        println("=== RESUGGEST DEBUG ===")
        println("Original pick orders: ${(listOf(pickOrder) + allCompetingPickOrders).size}")
        println("Filtered pick orders to resuggest: ${allPickOrdersToResuggest.size}")
        println("Pick orders being resuggested: ${allPickOrdersToResuggest.map { "${it.code}(${it.status})" }}")
        
        //  FIX: Only resuggest if there are actually pick orders with rejected lots
        if (allPickOrdersToResuggest.isEmpty()) {
            println("No pick orders need resuggesting - no rejected lots found")
            return MessageResponse(
                id = pickOrderId,
                name = "No resuggest needed",
                code = "SUCCESS",
                type = "resuggest",
                message = "No pick orders have rejected lots, no resuggest needed",
                errorPosition = null
            )
        }
        
        //  FIX: Get all pick order line IDs for the orders to resuggest
        val allPickOrderLineIds = allPickOrdersToResuggest
            .flatMap { it.pickOrderLines }
            .mapNotNull { it.id }
        
        println("All pick order line IDs to resuggest: $allPickOrderLineIds")
        
        //  FIX: Get all existing suggestions for these pick order lines
        val allSuggestions = suggestedPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIds)
        println("Found ${allSuggestions.size} existing suggestions")
        
        //  OPTIMIZATION 3: 批量查询所有相关的 stock out lines（用于检查 rejected 状态）
        val allSuggestionStockOutLines = if (allSuggestions.isNotEmpty()) {
            val suggestionLineIds = allSuggestions.mapNotNull { it.pickOrderLine?.id }.distinct()
            val suggestionLotIds = allSuggestions.mapNotNull { it.suggestedLotLine?.id }.distinct()
            
            //  OPTIMIZATION 3: 使用批量查询方法（一次查询代替 N*M 次查询）
            //  注意：返回 List<StockOutLine>（实体类）
            if (suggestionLineIds.isNotEmpty() && suggestionLotIds.isNotEmpty()) {
                stockOutLIneRepository.findAllByPickOrderLineIdInAndInventoryLotLineIdInAndDeletedFalse(
                    suggestionLineIds,
                    suggestionLotIds
                )
            } else {
                emptyList()
            }
        } else {
            emptyList()
        }
        
        println("Found ${allSuggestionStockOutLines.size} stock out lines for suggestions in batch query (1 query instead of ${allSuggestions.size} queries)")
        
        //  OPTIMIZATION 3: 按 (pickOrderLineId, inventoryLotLineId) 分组
        //  注意：StockOutLine 实体使用 pickOrderLine?.id 和 inventoryLotLine?.id
        val stockOutLinesByLineAndLot = allSuggestionStockOutLines
            .groupBy { 
                val lineId = it.pickOrderLine?.id
                val lotId = it.inventoryLotLine?.id
                lineId to lotId
            }
        
        //  FIX: Separate suggestions to keep (those WITHOUT rejected stock out lines) and delete
        val suggestionsToKeep = allSuggestions.filter { suggestion ->
            val pickOrderLineId = suggestion.pickOrderLine?.id
            val suggestedLotLine = suggestion.suggestedLotLine
            val suggestedLotLineId = suggestedLotLine?.id
        
            
            val isLotUnavailable = suggestedLotLine?.status == InventoryLotLineStatus.UNAVAILABLE
            if (isLotUnavailable) {
                println("⏭️ Suggestion ${suggestion.id} uses UNAVAILABLE lot ${suggestedLotLine?.id}, will be deleted")
                return@filter false
            }
        
            if (pickOrderLineId != null && suggestedLotLineId != null) {
                val stockOutLines = stockOutLinesByLineAndLot[pickOrderLineId to suggestedLotLineId] ?: emptyList()
                !stockOutLines.any { it.status?.equals("rejected", ignoreCase = true) == true }
            } else {
                true
            }
        }

        //  只删除有 rejected stock out lines 的 suggestions
        val suggestionsToDelete = allSuggestions.filter { suggestion ->
            val pickOrderLineId = suggestion.pickOrderLine?.id
            val suggestedLotLine = suggestion.suggestedLotLine
            val suggestedLotLineId = suggestedLotLine?.id
        
            val isLotUnavailable = suggestedLotLine?.status == InventoryLotLineStatus.UNAVAILABLE
        
            if (pickOrderLineId != null && suggestedLotLineId != null) {
                val stockOutLines = stockOutLinesByLineAndLot[pickOrderLineId to suggestedLotLineId] ?: emptyList()
                val hasRejected = stockOutLines.any { it.status?.equals("rejected", ignoreCase = true) == true }
        
                // ✅ 逻辑：只要 (lot UNAVAILABLE) 或 (有 rejected 的 stock_out_line)，就删掉这条 suggestion
                if (isLotUnavailable || hasRejected) {
                    println("🗑 Deleting suggestion ${suggestion.id} for lot $suggestedLotLineId, " +
                            "unavailable=$isLotUnavailable, hasRejected=$hasRejected")
                    true
                } else {
                    false
                }
            } else {
                // lotId == null 的老逻辑保留
                suggestedLotLineId == null
            }
        }
        
        println("Suggestions to keep: ${suggestionsToKeep.size}")
        println("Suggestions to delete: ${suggestionsToDelete.size}")
        
        // ✅ FIX: 更新使用 UNAVAILABLE lot 的 stockOutLine 状态为 rejected
        val unavailableLotIds = suggestionsToDelete
            .filter { it.suggestedLotLine?.status == InventoryLotLineStatus.UNAVAILABLE }
            .mapNotNull { it.suggestedLotLine?.id }
            .distinct()
        
        if (unavailableLotIds.isNotEmpty()) {
            println("=== Updating stock out lines for UNAVAILABLE lots ===")
            println("UNAVAILABLE lot IDs: $unavailableLotIds")
            
            // ✅ FIX: Find ALL stockOutLines using unavailable lots (not just those with suggestions)
            val stockOutLinesToReject = stockOutLIneRepository
                .findAllByInventoryLotLineIdInAndNotCompletedOrRejected(unavailableLotIds)
                .filter { sol ->
                    sol.status != StockOutLineStatus.REJECTED.status &&
                    sol.status != StockOutLineStatus.COMPLETE.status
                }
            
            println("Found ${stockOutLinesToReject.size} stock out lines to reject for UNAVAILABLE lots (across all pick orders)")
            
            stockOutLinesToReject.forEach { sol ->
                val oldStatus = sol.status
                sol.status = StockOutLineStatus.REJECTED.status
                sol.modified = LocalDateTime.now()
                sol.modifiedBy = "system"
                println("  - Updated stock out line ${sol.id} (lot: ${sol.inventoryLotLine?.id}, pickOrderLine: ${sol.pickOrderLine?.id}) status: $oldStatus -> rejected")
            }
            
            if (stockOutLinesToReject.isNotEmpty()) {
                stockOutLIneRepository.saveAll(stockOutLinesToReject)
                stockOutLIneRepository.flush()
                println("✅ Updated ${stockOutLinesToReject.size} stock out lines to REJECTED status")
            }
        }
        
        //  FIX: Clear holdQty ONLY for lots that have rejected stock out lines
        val rejectedLotIds = suggestionsToDelete.mapNotNull { it.suggestedLotLine?.id }.distinct()
        println("Rejected lot IDs (clearing holdQty only): $rejectedLotIds")
        
        //  OPTIMIZATION 3: 批量加载和更新 lots
        val rejectedLots = if (rejectedLotIds.isNotEmpty()) {
            inventoryLotLineRepository.findAllById(rejectedLotIds)
        } else {
            emptyList()
        }
        
        rejectedLots.forEach { lot ->
            val originalHoldQty = lot.holdQty ?: BigDecimal.ZERO
            lot.holdQty = BigDecimal.ZERO
            println("Cleared holdQty for rejected lot ${lot.id}: $originalHoldQty -> 0")
        }
        
        if (rejectedLots.isNotEmpty()) {
            inventoryLotLineRepository.saveAll(rejectedLots)
            println("Batch cleared holdQty for ${rejectedLots.size} rejected lots")
        }

        println("Keeping all suggestions (including rejected ones for display)")
        
        //  NEW: Build holdQtyMap with existing holdQty from other pick orders
        val existingHoldQtyMap = mutableMapOf<Long?, BigDecimal?>()
        
        // Get all lots that are being used by other pick orders (including those NOT being resuggested)
        val allOtherPickOrderLineIds = allCompetingPickOrders
            .flatMap { it.pickOrderLines }
            .mapNotNull { it.id }
        
        println("Other pick order line IDs: $allOtherPickOrderLineIds")
        
        if (allOtherPickOrderLineIds.isNotEmpty()) {
            val otherSuggestions = suggestedPickLotRepository.findAllByPickOrderLineIdIn(allOtherPickOrderLineIds)
            println("Found ${otherSuggestions.size} other suggestions")
            
            otherSuggestions.forEach { suggestion ->
                val lotId = suggestion.suggestedLotLine?.id
                val qty = suggestion.qty
                
                if (lotId != null && qty != null) {
                    val currentHoldQty = existingHoldQtyMap[lotId] ?: BigDecimal.ZERO
                    val newHoldQty = currentHoldQty.plus(qty)
                    existingHoldQtyMap[lotId] = newHoldQty
                }
            }
        }
        
        println("Final existing holdQtyMap: $existingHoldQtyMap")
        
        allPickOrdersToResuggest.forEach { pickOrderToResuggest ->
            //  获取需要重新建议的 pick order lines：
            //  1. 有 rejected stock out lines 的行
            //  2. 有使用 UNAVAILABLE lot 的 suggestion 的行
            val problematicPickOrderLines = pickOrderToResuggest.pickOrderLines.filter { pol ->
                // 情况1: 有 rejected stock out line
                val stockOutLines = stockOutLinesByPickOrderLineId[pol.id] ?: emptyList()
                val hasRejected = stockOutLines.any { it.status?.equals("rejected", ignoreCase = true) == true }
                
                // 情况2: 有使用 UNAVAILABLE lot 的 suggestion
                val suggestions = suggestionsByPickOrderLineId[pol.id] ?: emptyList()
                val hasUnavailableLot = suggestions.any { 
                    it.suggestedLotLine?.status == InventoryLotLineStatus.UNAVAILABLE 
                }
                
                hasRejected || hasUnavailableLot
            }
            
            if (problematicPickOrderLines.isNotEmpty()) {
                println("=== Creating new suggestions for pick order: ${pickOrderToResuggest.code} ===")
                println("  - Problematic lines: ${problematicPickOrderLines.map { it.id }}")
                
                //  调用 suggestionForPickOrderLines 生成新的 suggestions
                val request = SuggestedPickLotForPolRequest(
                    pickOrderLines = problematicPickOrderLines,
                    holdQtyMap = existingHoldQtyMap.toMutableMap()
                )
                val response = suggestionForPickOrderLines(request)
                
                println("Generated ${response.suggestedList.size} new suggestions")
                
                if (response.suggestedList.isNotEmpty()) {
                    println("Saving ${response.suggestedList.size} new suggestions")
                    pickOrderToResuggest.id?.let { pickOrderIdsWithChanges.add(it) }
                    //  获取现有的 pending/checked 状态的 suggestions（可以更新的）
                    val existingUpdatableSuggestions = suggestionsToKeep
                        .filter { it.suggestedLotLine?.id != null }
                        .groupBy { it.pickOrderLine?.id to it.suggestedLotLine?.id }
                        .mapValues { it.value.first() }  // 每个 (lineId, lotId) 只取第一个

                    //  处理新的 suggestions：更新现有的或创建新的
                    val suggestionsToSave = response.suggestedList.mapNotNull { newSugg ->
                        val key = newSugg.pickOrderLine?.id to newSugg.suggestedLotLine?.id
                        val lineId = newSugg.pickOrderLine?.id
                        val lotId = newSugg.suggestedLotLine?.id

                        if (lineId != null && lotId != null) {
                            //  检查这个 lot 是否已有 suggestion
                            val existingSugg = existingUpdatableSuggestions[key]
                            
                            if (existingSugg != null) {
                                //  OPTIMIZATION 3: 从预加载的 Map 中获取
                                val stockOutLines = stockOutLinesByLineAndLot[lineId to lotId] ?: emptyList()
                                
                                val canUpdate = stockOutLines.isEmpty() || stockOutLines.all { 
                                    val status = it.status?.lowercase()
                                    status == "pending" || status == "checked" || status == "partially_completed"
                                }
                                
                                if (canUpdate) {
                                    //  Case 1: 更新现有的 suggestion
                                    existingSugg.qty = newSugg.qty
                                    existingSugg.modified = LocalDateTime.now()
                                    existingSugg.modifiedBy = "system"
                                    println("⚠️ Updated existing suggestion ${existingSugg.id} for lot $lotId: new qty=${newSugg.qty}")
                                    existingSugg
                                } else {
                                    //  Case 2: 已完成/拒绝，跳过（不更新，也不创建新的）
                                    val firstStatus = stockOutLines.firstOrNull()?.status ?: "unknown"
                                    println("⏭️ Skipping lot $lotId - already $firstStatus")
                                    null
                                }
                            } else {
                                //  没有现有的 suggestion，创建新的
                                newSugg
                            }
                        } else if (lotId == null) {
                            //  lotId=null：检查是否已有 resuggest_issue
                            val existingResuggestIssues = pickExecutionIssueRepository
                                .findByPickOrderLineIdAndDeletedFalse(lineId ?: 0L)
                                .filter { it.issueCategory.name == "resuggest_issue" }
                            
                            if (existingResuggestIssues.isEmpty()) {
                                newSugg  // 创建新的 null suggestion（后续会创建 issue）
                            } else {
                                println("⏭️ Resuggest issue already exists for line $lineId, skipping null suggestion")
                                null  // 跳过，避免创建重复的 resuggest_issue
                            }
                        } else {
                            newSugg
                        }
                    }.filterNotNull()

                    val updatedSuggestions = suggestionsToSave.filter { it.id != null } // 有 id 的是更新的
                    val newSuggestions = suggestionsToSave.filter { it.id == null } // 没有 id 的是新创建的

                    val allSavedSuggestions = mutableListOf<SuggestedPickLot>()

                    // 保存更新的 suggestions
                    if (updatedSuggestions.isNotEmpty()) {
                        val savedUpdated = suggestedPickLotRepository.saveAllAndFlush(updatedSuggestions)
                        allSavedSuggestions.addAll(savedUpdated)
                        println(" Updated ${savedUpdated.size} existing suggestions")
                    }

                    // 保存新的 suggestions
                    if (newSuggestions.isNotEmpty()) {
                        val savedNew = suggestedPickLotRepository.saveAllAndFlush(newSuggestions)
                        allSavedSuggestions.addAll(savedNew)
                        println(" Created ${savedNew.size} new suggestions")
                    }

                    val savedSuggestions = allSavedSuggestions
                    println("Saved/Updated ${savedSuggestions.size} suggestions")
                                        
                    //  为每个新 suggestion 创建 stock out line 或 issue
                    savedSuggestions.forEach { suggestion ->
                        if (suggestion.suggestedLotLine != null) {
                            val isNewSuggestion = response.suggestedList.any { 
                                it.pickOrderLine?.id == suggestion.pickOrderLine?.id &&
                                it.suggestedLotLine?.id == suggestion.suggestedLotLine?.id &&
                                it.id == null  // 新的 suggestion 还没有 id
                            }
                            val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest)
                            if (stockOutLine != null) {
                                println(" Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}")
                            }
                        } else {
                            //  如果 lot 是 null，表示没有可用的 lot，创建 resuggest_issue
                            println("❌ No available lot for pick order line ${suggestion.pickOrderLine?.id}, creating resuggest_issue")
                            
                            val pickOrderLine = suggestion.pickOrderLine
                            if (pickOrderLine != null) {
                                //  OPTIMIZATION 3: 从预加载的 Map 中获取
                                val rejectedStockOutLines = stockOutLinesByPickOrderLineId[pickOrderLine.id]
                                    ?.filter { it.status?.equals("rejected", ignoreCase = true) == true } ?: emptyList()
                                println("Rejected stock out lines: ${rejectedStockOutLines.size}")
                                //  修复：只创建一个 resuggest_issue（如果有 rejected lines）
                                if (rejectedStockOutLines.isNotEmpty()) {
                                    println("Creating resuggest failure issue")
                                    //  注意：rejectedStockOutLines 是 List<StockOutLine>，需要转换为合适的类型
                                    val firstRejectedLine = rejectedStockOutLines.first()
                                    createResuggestFailureIssue(
                                        pickOrder = pickOrderToResuggest,
                                        pickOrderLine = pickOrderLine,
                                        rejectedStockOutLine = firstRejectedLine
                                    )
                                }
                            }
                        }
                    }
                    
                    // ✅ 优化：批量更新 holdQty（避免逐个保存）
                    val lotsToUpdate = mutableListOf<InventoryLotLine>()
                    response.holdQtyMap.forEach { (lotId, newHoldQty) ->
                        if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) {
                            val lot = inventoryLotLineRepository.findById(lotId).orElse(null)
                            lot?.let {
                                val currentHoldQty = it.holdQty ?: BigDecimal.ZERO
                                val existingHoldQty = existingHoldQtyMap[lotId] ?: BigDecimal.ZERO
                                val additionalHoldQty = newHoldQty.minus(existingHoldQty)
                                val finalHoldQty = currentHoldQty.plus(additionalHoldQty)
                                it.holdQty = finalHoldQty
                                lotsToUpdate.add(it)
                                existingHoldQtyMap[lotId] = newHoldQty
                            }
                        }
                    }
                    // 批量保存所有更新的 lots
                    if (lotsToUpdate.isNotEmpty()) {
                        inventoryLotLineRepository.saveAll(lotsToUpdate)
                        println("Batch updated holdQty for ${lotsToUpdate.size} lots")
                    }
                } else {
                    //  如果完全没有生成任何 suggestions
                    println("No suggestions generated at all for pick order: ${pickOrderToResuggest.code}")
                    
                    problematicPickOrderLines.forEach { pickOrderLine ->
                        //  OPTIMIZATION 3: 从预加载的 Map 中获取
                        val rejectedStockOutLines = stockOutLinesByPickOrderLineId[pickOrderLine.id]
                            ?.filter { it.status?.equals("rejected", ignoreCase = true) == true } ?: emptyList()
                        
                        if (rejectedStockOutLines.isNotEmpty()) {
                            rejectedStockOutLines.forEach { rejectedLine ->
                                createResuggestFailureIssue(
                                    pickOrder = pickOrderToResuggest,
                                    pickOrderLine = pickOrderLine,
                                    rejectedStockOutLine = rejectedLine
                                )
                            }
                        }
                    }
                }
            }
        }
        
        // ✅ 优化：批量更新 inventory table（收集所有 item IDs，只更新一次）
        val allItemIdsToUpdate = allPickOrdersToResuggest
            .flatMap { it.pickOrderLines }
            .mapNotNull { it.item?.id }
            .distinct()
        
        if (allItemIdsToUpdate.isNotEmpty()) {
            println("=== Batch updating inventory table for ${allItemIdsToUpdate.size} items ===")
            allItemIdsToUpdate.forEach { itemId ->
                // Calculate onHoldQty for ALL pick orders that use this item
                // ✅ FIX: Use enum directly, not .value
                val onHoldQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE)
                    .sumOf { it.holdQty ?: BigDecimal.ZERO }
                
                // ✅ FIX: Use enum directly, not .value
                val unavailableQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.UNAVAILABLE)
                    .sumOf { 
                        val inQty = it.inQty ?: BigDecimal.ZERO
                        val outQty = it.outQty ?: BigDecimal.ZERO
                        val remainingQty = inQty.minus(outQty)
                        remainingQty 
                    }
                
                val inventory = inventoryRepository.findByItemId(itemId).orElse(null)
                if (inventory != null) {
                    inventory.onHoldQty = onHoldQty
                    inventory.unavailableQty = unavailableQty
                    inventoryRepository.save(inventory)
                }
            }
            println("✅ Batch updated inventory table for ${allItemIdsToUpdate.size} items")
        }
        println("=== RESUGGEST DEBUG END ===")
        
        return MessageResponse(
            id = pickOrderId,
            name = "Pick order resuggested successfully",
            code = "SUCCESS",
            type = "resuggest",
            message = "Resuggested ${allPickOrdersToResuggest.size} pick orders",
            errorPosition = null
        )
    } catch (e: Exception) {
        println("Error in resuggestPickOrder: ${e.message}")
        e.printStackTrace()
        return MessageResponse(
            id = null,
            name = "Failed to resuggest pick order",
            code = "ERROR",
            type = "resuggest",
            message = "Error: ${e.message}",
            errorPosition = null
        )
    }
}

private fun createResuggestFailureIssue(
    pickOrder: PickOrder,
    pickOrderLine: PickOrderLine,
    rejectedStockOutLine: StockOutLine  //  改为 StockOutLine
) {
    try {
        val item = pickOrderLine.item
        
        //  从 StockOutLine 获取 inventoryLotLineId
        val inventoryLotLine = rejectedStockOutLine.inventoryLotLine
        val inventoryLotLineId = inventoryLotLine?.id
        
        val issue = PickExecutionIssue(
            id = null,
            pickOrderId = pickOrder.id!!,
            pickOrderCode = pickOrder.code!!,
            pickOrderCreateDate = pickOrder.created?.toLocalDate(),
            pickExecutionDate = LocalDate.now(),
            pickOrderLineId = pickOrderLine.id!!,
            issueNo = generateIssueNo(),
            joPickOrderId = pickOrder.jobOrder?.id,
            doPickOrderId = pickOrder.deliveryOrder?.id,
            issueCategory = IssueCategory.resuggest_issue,
            itemId = item?.id!!,
            itemCode = item.code,
            itemDescription = item.name,
            lotId = inventoryLotLineId,
            lotNo = inventoryLotLine?.inventoryLot?.lotNo,
            storeLocation = inventoryLotLine?.warehouse?.code,
            requiredQty = pickOrderLine.qty,
            actualPickQty = rejectedStockOutLine.qty?.let { BigDecimal.valueOf(it) } ?: BigDecimal.ZERO,
            missQty = (pickOrderLine.qty ?: BigDecimal.ZERO).minus(
                rejectedStockOutLine.qty?.let { BigDecimal.valueOf(it) } ?: BigDecimal.ZERO
            ),
            badItemQty = BigDecimal.ZERO,
            issueRemark = "Resuggest failed: No alternative lots available for rejected lot ${inventoryLotLine?.inventoryLot?.lotNo}",
            pickerName = null,
            handleStatus = HandleStatus.pending,
            handleDate = null,
            handledBy = null,
            created = LocalDateTime.now(),
            createdBy = "system",
            version = 0,
            modified = LocalDateTime.now(),
            modifiedBy = "system",
            deleted = false
        )
        
        val savedIssue = pickExecutionIssueRepository.save(issue)
        println(" Created resuggest_issue: ${savedIssue.issueNo} for pick order ${pickOrder.code}")
        
    } catch (e: Exception) {
        println("❌ Error creating resuggest_issue: ${e.message}")
        e.printStackTrace()
    }
}
    private fun generateIssueNo(): String {
        val now = LocalDateTime.now()
        val yearMonth = now.format(DateTimeFormatter.ofPattern("yyMM"))
        
        // 查询当月最新的 issueNo
        val latestIssueNo = pickExecutionIssueRepository.findLatestIssueNoByYearMonth(yearMonth)
        
        // 计算下一个序列号
        val nextSequence = if (latestIssueNo != null) {
            val parts = latestIssueNo.split("-")
            if (parts.size == 3) {
                val currentSequence = parts[2].toIntOrNull() ?: 0
                currentSequence + 1
            } else {
                1
            }
        } else {
            1
        }
        
        return "SKO-${yearMonth}-${nextSequence.toString().padStart(3, '0')}"
    }
private fun findAllSuggestionsForPickOrders(pickOrderIds: List<Long>): List<SuggestedPickLot> {
    val allPickOrderLines = mutableListOf<PickOrderLine>()
    
    pickOrderIds.forEach { pickOrderId ->
        val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null)
        pickOrder?.let { allPickOrderLines.addAll(it.pickOrderLines) }
    }
    
    val pickOrderLineIds = allPickOrderLines.mapNotNull { it.id }
    
    return if (pickOrderLineIds.isNotEmpty()) {
        suggestedPickLotRepository.findAllByPickOrderLineIdIn(pickOrderLineIds)
    } else {
        emptyList()
    }
}


private fun generateOptimalSuggestionsForAllPickOrders(
    pickOrders: List<PickOrder>,
    originalHoldQtyMap: Map<Long?, BigDecimal>
): List<SuggestedPickLot> {
    val suggestions = mutableListOf<SuggestedPickLot>()
    val zero = BigDecimal.ZERO
    val one = BigDecimal.ONE
    val today = LocalDate.now()
    
    // Group pick order lines by item
    val pickOrderLinesByItem = pickOrders.flatMap { it.pickOrderLines }
        .groupBy { it.item?.id }
        .filterKeys { it != null }
    
    pickOrderLinesByItem.forEach { (itemId, pickOrderLines) ->
        if (itemId == null) return@forEach
        
        // Calculate total demand for this item
        val totalDemand = pickOrderLines.sumOf { it.qty ?: zero }
        
        // Get available lots for this item (FEFO order)
        val availableLots = inventoryLotLineService
            .allInventoryLotLinesByItemIdIn(listOf(itemId))
            .filter { it.status == InventoryLotLineStatus.AVAILABLE.value }
            .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) }
            .sortedBy { it.expiryDate }
        
        //  FIX: Get fresh lot data and reset holdQty to 0 for calculation
        val lotEntities = inventoryLotLineRepository.findAllByIdIn(availableLots.mapNotNull { it.id })
        lotEntities.forEach { lot -> lot.holdQty = BigDecimal.ZERO }
        
        //  FIX: Calculate remaining quantity for each pick or  der line
                //  FIX: Calculate remaining quantity for each pick order line
                val remainingQtyPerLine = pickOrderLines.associate { pol ->
                    val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!)
                    
                    //  FIX: Count picked qty from ALL statuses except 'rejected'
                    // This includes 'completed', 'pending', 'checked', 'partially_completed'
                    val totalPickedQty = stockOutLines
                    .sumOf { it.qty ?: BigDecimal.ZERO }
                    
                    val requiredQty = pol.qty ?: zero
                    val remainingQty = requiredQty.minus(totalPickedQty)
                    
                    println("Pick Order Line ${pol.id}: required=${requiredQty}, picked=${totalPickedQty}, remaining=${remainingQty}")
                    println("Stock Out Lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}")
                    
                    pol.id to remainingQty
                }.toMutableMap()
        
        //  FIX: Filter out pick order lines that don't need more qty
        val remainingPickOrderLines = pickOrderLines.filter { pol ->
            val remainingQty = remainingQtyPerLine[pol.id] ?: zero
            remainingQty > zero
        }.toMutableList()
        
        val isDeliveryOrderContext = pickOrderLines.any { it.pickOrder?.type?.value == "do" }
        lotEntities.forEach { lot ->
            if (remainingPickOrderLines.isEmpty()) return@forEach
            val warehouseStoreId=lot.warehouse?.store_id
            if (warehouseStoreId == "3F" && isDeliveryOrderContext) {
                return@forEach
            }
            val totalQty = lot.inQty ?: zero
            val outQty = lot.outQty ?: zero
            val holdQty = lot.holdQty ?: zero // This should be 0 now
            val availableQty = totalQty.minus(outQty).minus(holdQty)
            
            if (availableQty <= zero) return@forEach
            
            var lotRemainingQty = availableQty
            
            // Allocate this lot to pick order lines in order (same UOM only)
            remainingPickOrderLines.removeAll { pickOrderLine ->
                val lineId = pickOrderLine.id
                val lineRemainingQty = remainingQtyPerLine[lineId] ?: zero
                val polUomId = pickOrderLine.uom?.id
                if (polUomId != null && lot.stockUom?.uom?.id != polUomId) {
                    return@removeAll false
                }

                if (lineRemainingQty <= zero || lotRemainingQty <= zero) return@removeAll false
                
                val assignQty = minOf(lotRemainingQty, lineRemainingQty)
                lotRemainingQty = lotRemainingQty.minus(assignQty)
                remainingQtyPerLine[lineId] = lineRemainingQty.minus(assignQty)
                
                if (assignQty > zero) {
                    suggestions.add(SuggestedPickLot().apply {
                        type = SuggestedPickLotType.PICK_ORDER
                        suggestedLotLine = lot
                        this.pickOrderLine = pickOrderLine
                        qty = assignQty
                    })
                }
                
                // Remove this line if fully satisfied
                remainingQtyPerLine[lineId]!! <= zero
            }
        }
        
        //  FIX: Create insufficient stock suggestions for remaining quantities
        remainingQtyPerLine.forEach { (lineId, remainingQty) ->
            if (remainingQty > zero) {
                val pickOrderLine = pickOrderLines.find { it.id == lineId }
                if (pickOrderLine != null) {
                    suggestions.add(SuggestedPickLot().apply {
                        type = SuggestedPickLotType.PICK_ORDER
                        suggestedLotLine = null
                        this.pickOrderLine = pickOrderLine
                        qty = remainingQty
                    })
                }
            }
        }
    }
    
    return suggestions
}

private fun updateInventoryTableAfterResuggest(pickOrder: PickOrder) {
    try {
        // Get all item IDs from the pick order
        val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct()
        
        itemIds.forEach { itemId ->
            //  FIX: Calculate onHoldQty for ALL pick orders that use this item, not just the current one
            val onHoldQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE)
                .sumOf { it.holdQty ?: BigDecimal.ZERO }
            
            //  FIX: Use enum method instead of string method
            val unavailableQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.UNAVAILABLE)
                .sumOf { 
                    val inQty = it.inQty ?: BigDecimal.ZERO
                    val outQty = it.outQty ?: BigDecimal.ZERO
                    val remainingQty = inQty.minus(outQty)
                    remainingQty 
                }
            
            // Update the inventory table
            val inventory = inventoryRepository.findByItemId(itemId).orElse(null)
            if (inventory != null) {
                inventory.onHoldQty = onHoldQty
                inventory.unavailableQty = unavailableQty
                inventoryRepository.save(inventory)
                
                println("Updated inventory for item $itemId: onHoldQty=$onHoldQty, unavailableQty=$unavailableQty")
            }
        }
    } catch (e: Exception) {
        println("Error updating inventory table after resuggest: ${e.message}")
        e.printStackTrace()
    }
}
    private fun findAllSuggestionsForPickOrder(pickOrderId: Long): List<SuggestedPickLot> {
        val pickOrderLines = pickOrderRepository.findById(pickOrderId)
            .orElseThrow()
            .pickOrderLines
        
        val pickOrderLineIds = pickOrderLines.mapNotNull { it.id }
        
        return suggestedPickLotRepository.findAllByPickOrderLineIdIn(pickOrderLineIds)
    }
    private fun generateSuggestionsForExcessQuantity(pickOrder: PickOrder, excessQtyInBaseUnits: BigDecimal): List<SuggestedPickLot> {
        val suggestions = mutableListOf<SuggestedPickLot>()
        val zero = BigDecimal.ZERO
        val one = BigDecimal.ONE
    
        pickOrder.pickOrderLines.forEach { orderLine ->
            val itemId = orderLine.item?.id ?: return@forEach
            
            // Get sales unit conversion ratio
            val salesUnit = itemUomService.findSalesUnitByItemId(itemId)
            val ratio = one
            
            // Convert excess quantity to sales units
            val excessQtyInSalesUnits = excessQtyInBaseUnits.divide(ratio, 0, RoundingMode.HALF_UP)
            
            if (excessQtyInSalesUnits <= zero) return@forEach
            
            val polUomId = orderLine.uom?.id
            // Get available inventory lots for this item (FEFO order, same UOM)
            val availableLots = inventoryLotLineService
                .allInventoryLotLinesByItemIdIn(listOf(itemId))
                .filter { it.status == InventoryLotLineStatus.AVAILABLE.value }
                .filter { polUomId == null || it.uomId == polUomId }
                .sortedBy { it.expiryDate }
    
            var remainingQtyInBaseUnits = excessQtyInBaseUnits
    
            availableLots.forEach { lotInfo ->
                if (remainingQtyInBaseUnits <= zero) return@forEach
    
                val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) }
                    ?: return@forEach
    
                val totalQty = lot.inQty ?: zero
                val outQty = lot.outQty ?: zero
                val holdQty = lot.holdQty ?: zero
                val availableQtyInBaseUnits = totalQty.minus(outQty).minus(holdQty)
    
                if (availableQtyInBaseUnits <= zero) return@forEach
    
                val assignQtyInBaseUnits = minOf(availableQtyInBaseUnits, remainingQtyInBaseUnits)
                remainingQtyInBaseUnits = remainingQtyInBaseUnits.minus(assignQtyInBaseUnits)
                
                val assignQtyInSalesUnits = assignQtyInBaseUnits.divide(ratio, 0, RoundingMode.DOWN)
    
                if (assignQtyInSalesUnits > zero) {
                    suggestions.add(SuggestedPickLot().apply {
                        type = SuggestedPickLotType.PICK_ORDER
                        suggestedLotLine = lot
                        pickOrderLine = orderLine
                        qty = assignQtyInSalesUnits
                    })
                }
            }
        }
    
        return suggestions
    }
   
   private fun findProblematicSuggestions(pickOrderId: Long): List<SuggestedPickLot> {
    val pickOrderLines = pickOrderRepository.findById(pickOrderId)
        .orElseThrow()
        .pickOrderLines
    
    val pickOrderLineIds = pickOrderLines.mapNotNull { it.id }
    
    println("=== DEBUG: findProblematicSuggestions ===")
    println("Pick Order ID: $pickOrderId")
    println("Pick Order Line IDs: $pickOrderLineIds")
    
    val allSuggestions = suggestedPickLotRepository.findAllByPickOrderLineIdIn(pickOrderLineIds)
    println("Total suggestions found: ${allSuggestions.size}")
    
    val problematicSuggestions = allSuggestions.filter { suggestion ->
        val pickOrderLine = pickOrderLines.find { it.id == suggestion.pickOrderLine?.id }
        val inventoryLotLine = suggestion.suggestedLotLine
        
        if (pickOrderLine == null || inventoryLotLine == null) {
            println("Suggestion ${suggestion.id}: Missing pickOrderLine or inventoryLotLine")
            return@filter true
        }
        
        // Get sales unit conversion ratio
        val salesUnit = pickOrderLine.item?.id?.let { itemUomService.findSalesUnitByItemId(it) }
        val ratio = BigDecimal.ONE
        
        val ratioCheck = (suggestion.qty ?: BigDecimal.ZERO).divide(pickOrderLine.qty ?: BigDecimal.ONE, 2, RoundingMode.HALF_UP)
        
        // Calculate available qty in base units
        val availableQtyInBaseUnits = (inventoryLotLine.inQty ?: BigDecimal.ZERO)
            .minus(inventoryLotLine.outQty ?: BigDecimal.ZERO)
            .minus(inventoryLotLine.holdQty ?: BigDecimal.ZERO)
        
        // Convert available qty to sales units for comparison
        val availableQtyInSalesUnits = availableQtyInBaseUnits.divide(ratio, 2, RoundingMode.HALF_UP)
        
        // Convert suggestion qty to base units for comparison
        val suggestionQtyInBaseUnits = (suggestion.qty ?: BigDecimal.ZERO).multiply(ratio)
        
        println("Suggestion ${suggestion.id}: qty=${suggestion.qty} (sales), requiredQty=${pickOrderLine.qty} (sales), ratio=$ratioCheck")
        println("  - availableQtyInBaseUnits=$availableQtyInBaseUnits, availableQtyInSalesUnits=$availableQtyInSalesUnits")
        println("  - suggestionQtyInBaseUnits=$suggestionQtyInBaseUnits")
        
        // Flag as problematic if:
        val exceedsRatio = ratioCheck > BigDecimal("10")
        val exceedsAvailable = suggestionQtyInBaseUnits > availableQtyInBaseUnits
        
        println("  - exceedsRatio=$exceedsRatio, exceedsAvailable=$exceedsAvailable")
        
        val isProblematic = exceedsRatio || exceedsAvailable
        if (isProblematic) {
            println("Suggestion ${suggestion.id}: PROBLEMATIC - ratio=$ratioCheck, exceedsAvailable=$exceedsAvailable")
        }
        isProblematic
    }
    
    println("Problematic suggestions found: ${problematicSuggestions.size}")
    return problematicSuggestions
}
private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): List<SuggestedPickLot> {
    val suggestions = mutableListOf<SuggestedPickLot>()
    val zero = BigDecimal.ZERO
    val one = BigDecimal.ONE
    val today = LocalDate.now()
    
    pickOrder.pickOrderLines.forEach { orderLine ->
        val itemId = orderLine.item?.id ?: return@forEach
        val requiredQty = orderLine.qty ?: zero // This is in sales units
        
        println("=== DEBUG: generateCorrectSuggestionsWithExistingHolds ===")
        println("Item ID: $itemId, Required Qty: $requiredQty")
        
        // Get sales unit conversion ratio
        val salesUnit = itemUomService.findSalesUnitByItemId(itemId)
        val ratio = one
        
        //  FIX: Get ALL inventory lots (both available and unavailable)
        val allLots = inventoryLotLineService
            .allInventoryLotLinesByItemIdIn(listOf(itemId))
            .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) }
            .sortedBy { it.expiryDate }
        
        //  FIX: Separate available and unavailable lots
        val availableLots = allLots.filter { it.status == InventoryLotLineStatus.AVAILABLE.value }
        val unavailableLots = allLots.filter { it.status == InventoryLotLineStatus.UNAVAILABLE.value }
        
        //  FIX: Calculate total quantity that was previously held by unavailable lots
        var totalUnavailableHoldQty = BigDecimal.ZERO
        val modifiedUnavailableLots = mutableListOf<InventoryLotLine>()
        
        unavailableLots.forEach { lotInfo ->
            val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) }
            lot?.let {
                totalUnavailableHoldQty = totalUnavailableHoldQty.plus(it.holdQty ?: zero)
                //  Reset holdQty for unavailable lots
                it.holdQty = BigDecimal.ZERO
                modifiedUnavailableLots.add(it) //  Keep reference to modified entity
            }
        }
        
        //  FIX: Save the modified entities (not fresh ones from database)
        if (modifiedUnavailableLots.isNotEmpty()) {
            inventoryLotLineRepository.saveAll(modifiedUnavailableLots)
            println("Reset holdQty for ${modifiedUnavailableLots.size} unavailable lots")
        }
        
        //  FIX: Add the unavailable hold quantity to the required quantity
        //val totalRequiredQty = requiredQty.plus(totalUnavailableHoldQty.divide(ratio, 2, RoundingMode.HALF_UP))
        val totalRequiredQty = requiredQty
        var remainingQtyInSalesUnits = totalRequiredQty
        
        println("Total required qty (including unavailable): $totalRequiredQty")
        println("Available lots: ${availableLots.size}, Unavailable lots: ${unavailableLots.size}")
        println("Total unavailable hold qty to redistribute: $totalUnavailableHoldQty")
        
        val polUomId = orderLine.uom?.id
        // Generate suggestions from available lots ONLY (same UOM as POL)
        availableLots.forEach { lotInfo ->
            if (remainingQtyInSalesUnits <= zero) return@forEach
            if (polUomId != null && lotInfo.uomId != polUomId) return@forEach

            val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) }
                ?: return@forEach
           
            val warehouseStoreId=lot.warehouse?.store_id
            // 3F filter only for delivery order
            if (warehouseStoreId == "3F" && pickOrder.type?.value == "do") {
                return@forEach
            }
            val totalQty = lot.inQty ?: zero
            val outQty = lot.outQty ?: zero
            val holdQty = lot.holdQty ?: zero
            val availableQtyInBaseUnits = totalQty.minus(outQty).minus(holdQty)
            
            val availableQtyInSalesUnits = availableQtyInBaseUnits.divide(ratio, 2, RoundingMode.HALF_UP)

            println("Available lot ${lot.id}: availableQtyInBaseUnits=$availableQtyInBaseUnits, availableQtyInSalesUnits=$availableQtyInSalesUnits")

            if (availableQtyInSalesUnits <= zero) return@forEach

            val assignQtyInSalesUnits = minOf(availableQtyInSalesUnits, remainingQtyInSalesUnits)
            remainingQtyInSalesUnits = remainingQtyInSalesUnits.minus(assignQtyInSalesUnits)

            println("Assigning $assignQtyInSalesUnits to lot ${lot.id}, remaining: $remainingQtyInSalesUnits")

            if (assignQtyInSalesUnits > zero) {
                suggestions.add(SuggestedPickLot().apply {
                    type = SuggestedPickLotType.PICK_ORDER
                    suggestedLotLine = lot
                    pickOrderLine = orderLine
                    qty = assignQtyInSalesUnits
                })
            }
        }
        
        // If we still have remaining qty, create a suggestion with null lot (insufficient stock)
        if (remainingQtyInSalesUnits > zero) {
            println("Creating insufficient stock suggestion for remaining qty: $remainingQtyInSalesUnits")
            suggestions.add(SuggestedPickLot().apply {
                type = SuggestedPickLotType.PICK_ORDER
                suggestedLotLine = null
                pickOrderLine = orderLine
                qty = remainingQtyInSalesUnits
            })
        }
    }

    return suggestions
}
private fun generateCorrectSuggestionsWithOriginalHolds(
    pickOrder: PickOrder, 
    originalHoldQtyMap: Map<Long?, BigDecimal>
): List<SuggestedPickLot> {
    val suggestions = mutableListOf<SuggestedPickLot>()
    val zero = BigDecimal.ZERO
    val one = BigDecimal.ONE
    val today = LocalDate.now()
    
    pickOrder.pickOrderLines.forEach { orderLine ->
        val itemId = orderLine.item?.id ?: return@forEach
        val requiredQty = orderLine.qty ?: zero
        
        println("=== DEBUG: generateCorrectSuggestionsWithOriginalHolds ===")
        println("Item ID: $itemId, Required Qty: $requiredQty")
        
        val salesUnit = itemUomService.findSalesUnitByItemId(itemId)
        val ratio = one
        val polUomId = orderLine.uom?.id
        
        // Get available inventory lots for this item (FEFO order, same UOM)
        val availableLots = inventoryLotLineService
            .allInventoryLotLinesByItemIdIn(listOf(itemId))
            .filter { it.status == InventoryLotLineStatus.AVAILABLE.value }
            .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) }
            .filter { polUomId == null || it.uomId == polUomId }
            .sortedBy { it.expiryDate }
        
        //  Calculate total quantity that needs to be redistributed
        var totalRedistributeQty = requiredQty
        originalHoldQtyMap.forEach { (lotId, originalHoldQty) ->
            if (originalHoldQty > zero) {
                // Check if this lot is now unavailable
                val lot = lotId?.let { inventoryLotLineRepository.findById(it).orElse(null) }
                if (lot?.status == InventoryLotLineStatus.UNAVAILABLE) {
                    totalRedistributeQty = totalRedistributeQty.plus(originalHoldQty.divide(ratio, 2, RoundingMode.HALF_UP))
                    println("Adding ${originalHoldQty.divide(ratio, 2, RoundingMode.HALF_UP)} from unavailable lot $lotId")
                }
            }
        }
        
        var remainingQtyInSalesUnits = totalRedistributeQty
        println("Total qty to redistribute: $totalRedistributeQty")
        
        // Generate suggestions from available lots
        availableLots.forEach { lotInfo ->
            if (remainingQtyInSalesUnits <= zero) return@forEach

            val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) }
                ?: return@forEach

            val totalQty = lot.inQty ?: zero
            val outQty = lot.outQty ?: zero
            val holdQty = lot.holdQty ?: zero
            val availableQtyInBaseUnits = totalQty.minus(outQty).minus(holdQty)
            
            val availableQtyInSalesUnits = availableQtyInBaseUnits.divide(ratio, 2, RoundingMode.HALF_UP)

            if (availableQtyInSalesUnits <= zero) return@forEach

            val assignQtyInSalesUnits = minOf(availableQtyInSalesUnits, remainingQtyInSalesUnits)
            remainingQtyInSalesUnits = remainingQtyInSalesUnits.minus(assignQtyInSalesUnits)

            if (assignQtyInSalesUnits > zero) {
                suggestions.add(SuggestedPickLot().apply {
                    type = SuggestedPickLotType.PICK_ORDER
                    suggestedLotLine = lot
                    pickOrderLine = orderLine
                    qty = assignQtyInSalesUnits
                })
            }
        }
        
        // If we still have remaining qty, create a suggestion with null lot (insufficient stock)
        if (remainingQtyInSalesUnits > zero) {
            suggestions.add(SuggestedPickLot().apply {
                type = SuggestedPickLotType.PICK_ORDER
                suggestedLotLine = null
                pickOrderLine = orderLine
                qty = remainingQtyInSalesUnits
            })
        }
    }

    return suggestions
}
// 在 SuggestedPickLotService.kt 的 recordFailInventoryLotLine 方法中修复：

// 在 SuggestedPickLotService.kt 中修改 recordFailInventoryLotLine 方法：

// 在 SuggestedPickLotService.kt 中修改 recordFailInventoryLotLine 方法：

@Transactional(rollbackFor = [Exception::class])
open fun recordFailInventoryLotLine(request: PickAnotherLotRequest): MessageResponse {
    try {
        // 获取相关的库存批次行
        val inventoryLotLine = inventoryLotLineRepository.findById(request.lotId).orElseThrow()
        
        // 获取相关的拣货订单行
        val pickOrderLine = pickOrderLineRepository.findById(request.pickOrderLineId).orElseThrow()
        
        // 获取相关的出库行（如果存在）
        val stockOutLine = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(request.pickOrderLineId)
            .find { it.inventoryLotLineId == request.lotId }
        
        // 创建失败记录
        val failRecord = FailInventoryLotLine().apply {
            this.Lot_id = inventoryLotLine.inventoryLot?.id?.toInt()
            this.stockOutLineId = stockOutLine?.id?.toInt()
            this.handlerId = request.handlerId?.toInt()
            this.type = request.type
            this.qty = request.qty
            this.recordDate = request.recordDate ?: LocalDate.now()
            this.category = request.category
            this.releasedBy = request.releasedBy?.toInt()
        }
        
        val savedRecord = failInventoryLotLineRepository.save(failRecord)
        
        // 根据失败类型执行不同的逻辑
        when (request.type) {
            "missing" -> {
                // 类型为 missing：减少库存批次行的可用数量
                handleMissingItem(inventoryLotLine, request.qty)
            }
            "item_broken" -> {
                // 类型为 item_broken：将批次标记为不可用，更新库存表
                handleBrokenItem(inventoryLotLine, request.qty)
            }
            else -> {
                // 其他类型：只记录失败，不执行特殊逻辑
                println("Unknown fail type: ${request.type}")
            }
        }
        
        // 重新建议拣货批次
        val pickOrder = pickOrderLine.pickOrder
        if (pickOrder != null) {
            resuggestPickOrder(pickOrder.id!!)
        }
        
        return MessageResponse(
            id = savedRecord.id,
            name = "Fail inventory lot line recorded successfully",
            code = "SUCCESS",
            type = "fail_inventory_lot_line",
            message = "Fail inventory lot line recorded and resuggested successfully",
            errorPosition = null
        )
        
    } catch (e: Exception) {
        return MessageResponse(
            id = null,
            name = "Failed to record fail inventory lot line",
            code = "ERROR",
            type = "fail_inventory_lot_line",
            message = "Error: ${e.message}",
            errorPosition = null
        )
    }
}

// 处理缺失物品的情况
private fun handleMissingItem(inventoryLotLine: InventoryLotLine, failQty: BigDecimal) {
    try {
        // 减少库存批次行的可用数量
        val currentInQty = inventoryLotLine.inQty ?: BigDecimal.ZERO
        val newInQty = currentInQty.minus(failQty)
        
        // 确保不会变成负数
        inventoryLotLine.inQty = if (newInQty < BigDecimal.ZERO) BigDecimal.ZERO else newInQty
        
        inventoryLotLineRepository.save(inventoryLotLine)
        
        println("Missing item handled: Reduced inQty by $failQty for lot line ${inventoryLotLine.id}")
        
    } catch (e: Exception) {
        println("Error handling missing item: ${e.message}")
        throw e
    }
}

// 处理损坏物品的情况
private fun handleBrokenItem(inventoryLotLine: InventoryLotLine, failQty: BigDecimal) {
    try {
        // 1. 将库存批次行标记为不可用
        inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE
        
        // 2. 更新库存表的不可用数量
        val itemId = inventoryLotLine.inventoryLot?.item?.id
        if (itemId != null) {
            val inventory = inventoryRepository.findByItemId(itemId).orElse(null)
            if (inventory != null) {
                val currentUnavailableQty = inventory.unavailableQty ?: BigDecimal.ZERO
                inventory.unavailableQty = currentUnavailableQty.plus(failQty)
                inventoryRepository.save(inventory)
                
                println("Broken item handled: Updated unavailableQty by $failQty for item $itemId")
            }
        }
        
        // 3. 保存库存批次行的状态变更
        inventoryLotLineRepository.save(inventoryLotLine)
        
        println("Broken item handled: Marked lot line ${inventoryLotLine.id} as unavailable")
        
    } catch (e: Exception) {
        println("Error handling broken item: ${e.message}")
        throw e
    }
}

// ... existing code ...

@Transactional(rollbackFor = [Exception::class])
open fun updateSuggestedLotLineId(suggestedPickLotId: Long, newLotLineId: Long): MessageResponse {
    try {
        println("=== DEBUG: updateSuggestedLotLineId ===")
        println("suggestedPickLotId: $suggestedPickLotId")
        println("newLotLineId: $newLotLineId")

        // Get the existing suggested pick lot
        val suggestedPickLot = suggestedPickLotRepository.findById(suggestedPickLotId).orElse(null)
        if (suggestedPickLot == null) {
            return MessageResponse(
                id = null,
                name = "Suggested pick lot not found",
                code = "ERROR",
                type = "suggested_pick_lot",
                message = "Suggested pick lot with ID $suggestedPickLotId not found",
                errorPosition = null
            )
        }

        // Get the new inventory lot line
        val newInventoryLotLine = inventoryLotLineRepository.findById(newLotLineId).orElse(null)
        if (newInventoryLotLine == null) {
            return MessageResponse(
                id = null,
                name = "Inventory lot line not found",
                code = "ERROR",
                type = "inventory_lot_line",
                message = "Inventory lot line with ID $newLotLineId not found",
                errorPosition = null
            )
        }

        // Validate that the new lot line has the same item as the pick order line
        val pickOrderLine = suggestedPickLot.pickOrderLine
        if (pickOrderLine?.item?.id != newInventoryLotLine.inventoryLot?.item?.id) {
            return MessageResponse(
                id = null,
                name = "Item mismatch",
                code = "ERROR",
                type = "validation",
                message = "The new lot line belongs to a different item than the pick order line",
                errorPosition = null
            )
        }

        // Get the old inventory lot line for hold quantity management
        val oldInventoryLotLine = suggestedPickLot.suggestedLotLine
        val suggestedQty = suggestedPickLot.qty ?: BigDecimal.ZERO

        // Update hold quantities
        if (oldInventoryLotLine != null) {
            // Reduce hold quantity from old lot
            val oldHoldQty = oldInventoryLotLine.holdQty ?: BigDecimal.ZERO
            val newOldHoldQty = oldHoldQty.minus(suggestedQty)
            oldInventoryLotLine.holdQty = if (newOldHoldQty < BigDecimal.ZERO) BigDecimal.ZERO else newOldHoldQty
            inventoryLotLineRepository.save(oldInventoryLotLine)
            println("Reduced holdQty for old lot ${oldInventoryLotLine.id}: $oldHoldQty -> ${oldInventoryLotLine.holdQty}")
        }

        // Add hold quantity to new lot
        val newHoldQty = (newInventoryLotLine.holdQty ?: BigDecimal.ZERO).plus(suggestedQty)
        newInventoryLotLine.holdQty = newHoldQty
        inventoryLotLineRepository.save(newInventoryLotLine)
        println("Added holdQty for new lot ${newInventoryLotLine.id}: ${newInventoryLotLine.holdQty}")

        // Update the suggested pick lot
        suggestedPickLot.suggestedLotLine = newInventoryLotLine
        val savedSuggestedPickLot = suggestedPickLotRepository.save(suggestedPickLot)

        println(" Successfully updated suggested pick lot ${suggestedPickLotId} to use lot line ${newLotLineId}")

        return MessageResponse(
            id = savedSuggestedPickLot.id,
            name = "Suggested pick lot updated successfully",
            code = "SUCCESS",
            type = "suggested_pick_lot",
            message = "Successfully updated suggested pick lot to use new lot line",
            errorPosition = null
        )

    } catch (e: Exception) {
        println("❌ Error in updateSuggestedLotLineId: ${e.message}")
        e.printStackTrace()
        return MessageResponse(
            id = null,
            name = "Failed to update suggested pick lot",
            code = "ERROR",
            type = "suggested_pick_lot",
            message = "Failed to update suggested pick lot: ${e.message}",
            errorPosition = null
        )
    }
}

private fun createInsufficientStockIssue(
    pickOrder: PickOrder,
    pickOrderLine: PickOrderLine,
    insufficientQty: BigDecimal
) {
    try {
        //  检查是否已存在相同的 issue（避免重复创建）
        val existingIssues = pickExecutionIssueRepository
            .findByPickOrderLineIdAndDeletedFalse(pickOrderLine.id ?: 0L)
            .filter { 
                it.issueCategory == IssueCategory.resuggest_issue && 
                it.handleStatus == HandleStatus.pending 
            }
        
        if (existingIssues.isNotEmpty()) {
            println("⏭️ Issue already exists for pick order line ${pickOrderLine.id}, skipping")
            return
        }
        
        val issue = PickExecutionIssue(
            id = null,
            pickOrderId = pickOrder.id!!,
            pickOrderCode = pickOrder.code ?: "",
            pickOrderCreateDate = pickOrder.created?.toLocalDate(),
            pickExecutionDate = LocalDate.now(),
            doPickOrderId = if (pickOrder.type?.value == "do") pickOrder.deliveryOrder?.id else null,
            joPickOrderId = pickOrder.jobOrder?.id,
            pickOrderLineId = pickOrderLine.id!!,
            issueNo = generateIssueNo(),
            issueCategory = IssueCategory.resuggest_issue,
            itemId = pickOrderLine.item?.id ?: 0L,
            itemCode = pickOrderLine.item?.code,
            itemDescription = pickOrderLine.item?.name,
            lotId = null,
            lotNo = null,
            storeLocation = null,
            requiredQty = pickOrderLine.qty,
            actualPickQty = BigDecimal.ZERO,
            missQty = insufficientQty,
            badItemQty = BigDecimal.ZERO,
            issueRemark = "No inventory available for suggestion (auto-created by system)",
            pickerName = null,
            handleStatus = HandleStatus.pending,
            handleDate = null,
            handledBy = null,
            created = LocalDateTime.now(),
            createdBy = "SYSTEM",
            version = 0,
            modified = LocalDateTime.now(),
            modifiedBy = "SYSTEM",
            deleted = false
        )
        
        pickExecutionIssueRepository.save(issue)
        println(" Auto-created issue ${issue.issueNo} for insufficient stock (line ${pickOrderLine.id}, qty ${insufficientQty})")
        
    } catch (e: Exception) {
        println("❌ Error creating insufficient stock issue: ${e.message}")
        e.printStackTrace()
    }
}

}


