package com.ffii.fpsms.modules.deliveryOrder.service

import com.ffii.fpsms.modules.deliveryOrder.web.models.ReleaseDoRequest
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import org.springframework.stereotype.Service
import java.time.Instant
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.min
import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.deliveryOrder.web.models.ReleaseDoResult
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrder
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLine
import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRepository
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository
import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRepository
import com.ffii.fpsms.modules.pickOrder.service.PickExecutionIssueService
data class BatchReleaseJobStatus(
    val jobId: String,
    val total: Int,
    val success: AtomicInteger = AtomicInteger(0),
    val failed: MutableList<Pair<Long, String>> = mutableListOf(),
    @Volatile var running: Boolean = true,
    val startedAt: Long = Instant.now().toEpochMilli(),
    @Volatile var finishedAt: Long? = null
)

@Service
class DoReleaseCoordinatorService(
    private val deliveryOrderService: DeliveryOrderService,
    private val jdbcDao: JdbcDao,
    private val doPickOrderLineRepository: DoPickOrderLineRepository,
    private val deliveryOrderRepository: DeliveryOrderRepository,
    private val doPickOrderRepository: DoPickOrderRepository,
    private val pickExecutionIssueService: PickExecutionIssueService,
) {
    private val poolSize = Runtime.getRuntime().availableProcessors()
    private val executor = Executors.newFixedThreadPool(min(poolSize, 4))
    private val jobs = ConcurrentHashMap<String, BatchReleaseJobStatus>()
    private fun updateBatchTicketNumbers() {
        try {
            val updateSql = """
                UPDATE fpsmsdb.do_pick_order dpo
                INNER JOIN (
                    WITH DoFloorCounts AS (
                        SELECT 
                            dol.deliveryOrderId,
                            w.store_id,
                            COUNT(DISTINCT dol.itemId) AS item_count
                        FROM fpsmsdb.delivery_order_line dol
                        INNER JOIN fpsmsdb.inventory i ON i.itemId = dol.itemId 
                            AND i.deleted = 0 
                            AND i.onHandQty > 0
                        INNER JOIN fpsmsdb.inventory_lot il ON il.itemId = i.itemId 
                            AND il.deleted = 0
                        INNER JOIN fpsmsdb.inventory_lot_line ill ON ill.inventoryLotId = il.id 
                            AND ill.deleted = 0
                        INNER JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId 
                            AND w.deleted = 0
                            AND w.store_id IN ('2F', '4F')
                        WHERE dol.deleted = 0
                        GROUP BY dol.deliveryOrderId, w.store_id
                    ),
                    DoFloorSummary AS (
                        SELECT 
                            do.id AS deliveryOrderId,
                            COALESCE(SUM(CASE WHEN dfc.store_id = '2F' THEN dfc.item_count ELSE 0 END), 0) AS count_2f,
                            COALESCE(SUM(CASE WHEN dfc.store_id = '4F' THEN dfc.item_count ELSE 0 END), 0) AS count_4f
                        FROM fpsmsdb.delivery_order do
                        LEFT JOIN DoFloorCounts dfc ON dfc.deliveryOrderId = do.id
                        WHERE do.deleted = 0
                        GROUP BY do.id
                    ),
                    PreferredFloor AS (
                        SELECT 
                            deliveryOrderId,
                            CASE 
                                WHEN count_2f > count_4f THEN '2F'
                                WHEN count_4f > count_2f THEN '4F'
                                ELSE '2F'
                            END AS preferred_floor,
                            CASE 
                                WHEN count_2f > count_4f THEN 2
                                WHEN count_4f > count_2f THEN 4
                                ELSE 2
                            END AS preferred_store_id
                        FROM DoFloorSummary
                    ),
                    TruckSelection AS (
                        SELECT 
                            do.id AS delivery_order_id,
                            do.estimatedArrivalDate,
                            pf.preferred_floor,
                            CASE 
                                WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
                                    COALESCE(
                                        (SELECT t.DepartureTime FROM fpsmsdb.truck t 
                                         WHERE t.shopId = do.shopId AND t.deleted = 0 
                                           AND t.Store_id = pf.preferred_store_id
                                         ORDER BY t.DepartureTime ASC LIMIT 1),
                                        (SELECT t.DepartureTime FROM fpsmsdb.truck t 
                                         WHERE t.shopId = do.shopId AND t.deleted = 0
                                         ORDER BY t.DepartureTime ASC LIMIT 1),
                                        '23:59:59'
                                    )
                                ELSE 
                                    COALESCE(
                                        (SELECT t.DepartureTime FROM fpsmsdb.truck t 
                                         WHERE t.shopId = do.shopId AND t.deleted = 0
                                         ORDER BY t.DepartureTime ASC LIMIT 1),
                                        '23:59:59'
                                    )
                            END AS selected_departure_time,
                            CASE 
                                WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
                                    COALESCE(
                                        (SELECT t.TruckLanceCode FROM fpsmsdb.truck t 
                                         WHERE t.shopId = do.shopId AND t.deleted = 0 
                                           AND t.Store_id = pf.preferred_store_id
                                         ORDER BY t.DepartureTime ASC LIMIT 1),
                                        (SELECT t.TruckLanceCode FROM fpsmsdb.truck t 
                                         WHERE t.shopId = do.shopId AND t.deleted = 0
                                         ORDER BY t.DepartureTime ASC LIMIT 1),
                                        'ZZ'
                                    )
                                ELSE 
                                    COALESCE(
                                        (SELECT t.TruckLanceCode FROM fpsmsdb.truck t 
                                         WHERE t.shopId = do.shopId AND t.deleted = 0
                                         ORDER BY t.DepartureTime ASC LIMIT 1),
                                        'ZZ'
                                    )
                            END AS selected_truck_lance,
                            COALESCE(
                                (SELECT t.LoadingSequence FROM fpsmsdb.truck t 
                                 WHERE t.shopId = do.shopId AND t.deleted = 0 
                                   AND t.Store_id = pf.preferred_store_id
                                 ORDER BY t.DepartureTime ASC LIMIT 1),
                                (SELECT t.LoadingSequence FROM fpsmsdb.truck t 
                                 WHERE t.shopId = do.shopId AND t.deleted = 0
                                 ORDER BY t.DepartureTime ASC LIMIT 1),
                                999
                            ) AS loading_sequence
                        FROM fpsmsdb.delivery_order do
                        LEFT JOIN PreferredFloor pf ON pf.deliveryOrderId = do.id
                        WHERE do.id IN (
                            SELECT DISTINCT do_order_id 
                            FROM fpsmsdb.do_pick_order 
                            WHERE ticket_no LIKE 'TEMP-%' 
                            AND deleted = 0
                        )
                        AND do.deleted = 0
                    )
                    SELECT 
                        dpo2.id,
                        CONCAT('TI-', 
                               DATE_FORMAT(dpo2.RequiredDeliveryDate, '%Y%m%d'), 
                               '-', 
                               COALESCE(ts.preferred_floor, '2F'),
                               '-',
                               LPAD(
                                   ROW_NUMBER() OVER (
                                       PARTITION BY DATE(dpo2.RequiredDeliveryDate), COALESCE(ts.preferred_floor, '2F')
                                       ORDER BY COALESCE(ts.selected_departure_time, '23:59:59'),
                                                COALESCE(ts.selected_truck_lance, 'ZZ'),
                                                COALESCE(ts.loading_sequence, 999),
                                                dpo2.do_order_id
                                   ),
                                   3, '0'
                               )
                        ) AS new_ticket_no
                    FROM fpsmsdb.do_pick_order dpo2
                    LEFT JOIN TruckSelection ts ON ts.delivery_order_id = dpo2.do_order_id
                    WHERE dpo2.ticket_no LIKE 'TEMP-%'
                      AND dpo2.deleted = 0
                ) AS ticket_mapping ON dpo.id = ticket_mapping.id
                SET dpo.ticket_no = ticket_mapping.new_ticket_no
            """.trimIndent()
            
            val rowsUpdated = jdbcDao.executeUpdate(updateSql, emptyMap<String, Any>())
            println("✅ Updated $rowsUpdated ticket numbers")
        } catch (e: Exception) {
            println("❌ Error updating ticket numbers: ${e.message}")
            e.printStackTrace()
        }
    }
    private fun getOrderedDeliveryOrderIds(ids: List<Long>): List<Long> {
        try {
            println("🔍 DEBUG: Getting ordered IDs for ${ids.size} orders")
            println("🔍 DEBUG: First 5 IDs: ${ids.take(5)}")
            val sql = """
                WITH DoFloorCounts AS (
                    SELECT 
                        dol.deliveryOrderId,
                        w.store_id,
                        COUNT(DISTINCT dol.itemId) AS item_count
                    FROM fpsmsdb.delivery_order_line dol
                    INNER JOIN fpsmsdb.inventory i ON i.itemId = dol.itemId 
                        AND i.deleted = 0 
                        AND i.onHandQty > 0
                    INNER JOIN fpsmsdb.inventory_lot il ON il.itemId = i.itemId 
                        AND il.deleted = 0
                    INNER JOIN fpsmsdb.inventory_lot_line ill ON ill.inventoryLotId = il.id 
                        AND ill.deleted = 0
                    INNER JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId 
                        AND w.deleted = 0
                        AND w.store_id IN ('2F', '4F')
                    WHERE dol.deleted = 0
                      AND dol.deliveryOrderId IN (${ids.joinToString(",")})
                    GROUP BY dol.deliveryOrderId, w.store_id
                ),
                DoFloorSummary AS (
                    SELECT 
                        do.id AS deliveryOrderId,
                        COALESCE(SUM(CASE WHEN dfc.store_id = '2F' THEN dfc.item_count ELSE 0 END), 0) AS count_2f,
                        COALESCE(SUM(CASE WHEN dfc.store_id = '4F' THEN dfc.item_count ELSE 0 END), 0) AS count_4f
                    FROM fpsmsdb.delivery_order do
                    LEFT JOIN DoFloorCounts dfc ON dfc.deliveryOrderId = do.id
                    WHERE do.id IN (${ids.joinToString(",")})
                      AND do.deleted = 0
                    GROUP BY do.id
                ),
                PreferredFloor AS (
                    SELECT 
                        deliveryOrderId,
                        count_2f,
                        count_4f,
                        CASE 
                            WHEN count_2f > count_4f THEN '2F'
                            WHEN count_4f > count_2f THEN '4F'
                            ELSE '2F'
                        END AS preferred_floor,
                        CASE 
                            WHEN count_2f > count_4f THEN 2
                            WHEN count_4f > count_2f THEN 4
                            ELSE 2
                        END AS preferred_store_id
                    FROM DoFloorSummary
                ),
                TruckSelection AS (
                        SELECT 
                        do.id AS delivery_order_id,
                        do.shopId,
                        do.estimatedArrivalDate,
                        pf.preferred_floor,
                        pf.preferred_store_id,
                        CASE 
                            WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
                                COALESCE(
                                    (SELECT t.DepartureTime 
                                     FROM fpsmsdb.truck t 
                                     WHERE t.shopId = do.shopId AND t.deleted = 0 
                                       AND t.Store_id = pf.preferred_store_id
                                     ORDER BY t.DepartureTime ASC
                                     LIMIT 1),
                                    (SELECT t.DepartureTime 
                                     FROM fpsmsdb.truck t 
                                     WHERE t.shopId = do.shopId AND t.deleted = 0
                                     ORDER BY t.DepartureTime ASC
                                     LIMIT 1),
                                    '23:59:59'
                                )
                            ELSE 
                                COALESCE(
                                    (SELECT t.DepartureTime 
                                     FROM fpsmsdb.truck t 
                                     WHERE t.shopId = do.shopId AND t.deleted = 0
                                     ORDER BY t.DepartureTime ASC
                                     LIMIT 1),
                                    '23:59:59'
                                )
                        END AS selected_departure_time,
                        CASE 
                            WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
                                COALESCE(
                                    (SELECT t.TruckLanceCode 
                                     FROM fpsmsdb.truck t 
                                     WHERE t.shopId = do.shopId AND t.deleted = 0 
                                       AND t.Store_id = pf.preferred_store_id
                                     ORDER BY t.DepartureTime ASC
                                     LIMIT 1),
                                    (SELECT t.TruckLanceCode 
                                     FROM fpsmsdb.truck t 
                                     WHERE t.shopId = do.shopId AND t.deleted = 0
                                     ORDER BY t.DepartureTime ASC
                                     LIMIT 1),
                                    'ZZ'
                                )
                            ELSE 
                                COALESCE(
                                    (SELECT t.TruckLanceCode 
                                     FROM fpsmsdb.truck t 
                                     WHERE t.shopId = do.shopId AND t.deleted = 0
                                     ORDER BY t.DepartureTime ASC
                                     LIMIT 1),
                                    'ZZ'
                                )
                        END AS selected_truck_lance,
                        COALESCE(
    (SELECT t.LoadingSequence 
     FROM fpsmsdb.truck t 
     WHERE t.shopId = do.shopId AND t.deleted = 0 
       AND t.Store_id = pf.preferred_store_id
     ORDER BY t.DepartureTime ASC LIMIT 1),
    (SELECT t.LoadingSequence 
     FROM fpsmsdb.truck t 
     WHERE t.shopId = do.shopId AND t.deleted = 0
     ORDER BY t.DepartureTime ASC LIMIT 1),
    999
) AS loading_sequence
                    FROM fpsmsdb.delivery_order do
                    LEFT JOIN PreferredFloor pf ON pf.deliveryOrderId = do.id
                WHERE do.id IN (${ids.joinToString(",")})
                      AND do.deleted = 0
                )
                SELECT delivery_order_id AS id
                FROM TruckSelection
                ORDER BY 
                    DATE(estimatedArrivalDate),
                    preferred_floor,
                    selected_departure_time,
                    selected_truck_lance,
                    loading_sequence ASC,
                    delivery_order_id ASC
                
            """.trimIndent()
            
                   
        println("🔍 DEBUG: SQL length: ${sql.length} characters")  // ✅ 添加这行
        println("🔍 DEBUG: SQL first 500 chars: ${sql.take(500)}")  // ✅ 添加这行
            
            val results = jdbcDao.queryForList(sql)
        println("🔍 DEBUG: Results type: ${results.javaClass.name}")  // ✅ 添加这行
        println("🔍 DEBUG: Results size: ${results.size}")  // ✅ 添加这行
        
        if (results.isNotEmpty()) {
            println("🔍 DEBUG: First result keys: ${results[0].keys}")  // ✅ 添加这行
            println("🔍 DEBUG: First result: ${results[0]}")  // ✅ 添加这行
        }
        
        val sortedIds = results.mapNotNull { row ->
            (row["id"] as? Number)?.toLong()
        }
        
        println("🔍 DEBUG: Query returned ${sortedIds.size} sorted IDs")
        println("🔍 DEBUG: First 10 sorted IDs: ${sortedIds.take(10)}")
        
        return if (sortedIds.isEmpty()) {
            println("⚠️ WARNING: No sorted IDs, using original order")
            ids
        } else {
            sortedIds
        }
        } catch (e: Exception) {
        println("❌ ERROR: ${e.message}")
        println("❌ ERROR Stack Trace:")  // ✅ 添加这行
            e.printStackTrace()
            return ids
        }
    }
    fun startBatchReleaseAsync(ids: List<Long>, userId: Long): MessageResponse {
        if (ids.isEmpty()) {
            return MessageResponse(
                id = null, code = "NO_IDS", name = null, type = null,
                message = "No IDs provided", errorPosition = null, entity = null
            )
        }

        val jobId = UUID.randomUUID().toString()
        val status = BatchReleaseJobStatus(jobId = jobId, total = ids.size)
        jobs[jobId] = status

        executor.submit {
            try {
                println("📦 Starting batch release for ${ids.size} orders")
                val sortedIds = getOrderedDeliveryOrderIds(ids)
                println("🔍 DEBUG: Got ${sortedIds.size} sorted orders")

                val releaseResults = mutableListOf<ReleaseDoResult>()

                // 第一步：发布所有 DO（创建 pick orders，但不创建 DoPickOrder）
              // 第一步：发布所有 DO（创建 pick orders，但不创建 DoPickOrder）
                sortedIds.forEach { id ->
                    try {
                        val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(id)
                        if (deliveryOrder?.status == DeliveryOrderStatus.COMPLETED ||
                            deliveryOrder?.status == DeliveryOrderStatus.RECEIVING
                        ) {
                            println("⏭️ DO $id is already ${deliveryOrder.status?.value}, skipping")
                            return@forEach
                        }
                        val result = deliveryOrderService.releaseDeliveryOrderWithoutTicket(
                            ReleaseDoRequest(id = id, userId = userId)
                        )
                        releaseResults.add(result)
                        status.success.incrementAndGet()
                        println("🔍 DO $id -> Success")
                    } catch (e: Exception) {
                        synchronized(status.failed) {
                            status.failed.add(id to (e.message ?: "Exception"))
                        }
                        println("❌ DO $id skipped: ${e.message}")

                        // ✅ 调用 PickExecutionIssueService 创建 issue 记录
                        try {
                            val issueCategory = when {
                                e.message?.contains("Unable to find") == true &&
                                        e.message?.contains("Warehouse") == true ->
                                    com.ffii.fpsms.modules.pickOrder.entity.IssueCategory.warehouse_issue

                                e.message?.contains("No matching truck") == true ->
                                    com.ffii.fpsms.modules.pickOrder.entity.IssueCategory.truck_issue

                                else ->
                                    com.ffii.fpsms.modules.pickOrder.entity.IssueCategory.match_issue
                            }

                            pickExecutionIssueService.createBatchReleaseIssue(
                                deliveryOrderId = id,
                                errorMessage = e.message ?: "Unknown error",
                                issueCategory = issueCategory
                            )
                        } catch (issueException: Exception) {
                            println("⚠️ Failed to create issue for DO $id: ${issueException.message}")
                        }
                    }
                }  // ✅ forEach 循环在这里结束

                // ✅ 第二步：按日期、楼层、店铺分组（在 forEach 之后）
                val sortedResults = releaseResults.sortedWith(
                    compareBy(
                        { it.estimatedArrivalDate },
                        { it.preferredFloor },
                        { it.truckDepartureTime },
                        { it.truckLanceCode },
                        { it.loadingSequence },
                        { it.shopId }
                    )
                )

                // ✅ 然后按正确的顺序分组（保持排序后的顺序）
                val grouped = sortedResults.groupBy {
                    Triple(it.estimatedArrivalDate, it.preferredFloor, it.shopId)
                }

                println("🔍 DEBUG: Grouped into ${grouped.size} DoPickOrders")

                // 第三步：为每组创建一个 DoPickOrder 和多条 DoPickOrderLine
                grouped.forEach { (key, group) ->
                    try {
                        createMergedDoPickOrder(group)
                        println("🔍 DEBUG: Created DoPickOrder for ${group.size} DOs")
                    } catch (e: Exception) {
                        println("❌ Error creating DoPickOrder: ${e.message}")
                        e.printStackTrace()
                    }
                }

                // 第四步：更新 ticket numbers
                if (status.success.get() > 0) {
                    println("🎫 Updating ticket numbers...")
                    updateBatchTicketNumbers()
                }

                println("✅ Batch completed: ${status.success.get()} success, ${status.failed.size} failed")

                            } catch (e: Exception) {
                                println("❌ Batch release exception: ${e.javaClass.simpleName} - ${e.message}")
                                e.printStackTrace()
                            } finally {
                                status.running = false
                                status.finishedAt = Instant.now().toEpochMilli()
                            }
                        }

                        return MessageResponse(
                            id = null, code = "STARTED", name = null, type = null,
                            message = "Batch release started", errorPosition = null,
                            entity = mapOf("jobId" to jobId, "total" to ids.size)
                        )
                    }

    private fun createMergedDoPickOrder(results: List<ReleaseDoResult>) {
        val first = results.first()
        
        val storeId = when (first.preferredFloor) {
            "2F" -> "2/F"
            "4F" -> "4/F"
            else -> "2/F"
        }
        
        val doPickOrder = DoPickOrder(
            storeId = storeId,
            ticketNo = "TEMP-${System.currentTimeMillis()}",
            ticketStatus = DoPickOrderStatus.pending,
            truckId = first.truckId,
            truckDepartureTime = first.truckDepartureTime,
            shopId = first.shopId,
            handledBy = null,
            loadingSequence = first.loadingSequence ?: 999,
            ticketReleaseTime = null,
            truckLanceCode = first.truckLanceCode,
            shopCode = first.shopCode,
            shopName = first.shopName,
            requiredDeliveryDate = first.estimatedArrivalDate
        )
        
        // ✅ 直接使用 doPickOrderRepository.save() 而不是 doPickOrderService.save()
        val saved = doPickOrderRepository.save(doPickOrder)
        println("🔍 DEBUG: Saved DoPickOrder - ID: ${saved.id}, Ticket: ${saved.ticketNo}")
        
        
        // 创建多条 DoPickOrderLine（每个 DO 一条）
        results.forEach { result ->
            val existingLines = doPickOrderLineRepository.findByPickOrderIdAndDeletedFalse(result.pickOrderId)
            if (existingLines.isNotEmpty()) {
                println("⚠️ WARNING: pick_order ${result.pickOrderId} already in do_pick_order_line, skipping")
                return@forEach  // 跳过这个
            }
            
            // ✅ 先创建 DoPickOrderLine，然后检查库存问题
            val line = DoPickOrderLine().apply {
                doPickOrderId = saved.id
                pickOrderId = result.pickOrderId
                doOrderId = result.deliveryOrderId
                pickOrderCode = result.pickOrderCode
                deliveryOrderCode = result.deliveryOrderCode
                status = "pending"  // 初始状态
            }
            doPickOrderLineRepository.save(line)
            println("🔍 DEBUG: Created DoPickOrderLine for pick order ${result.pickOrderId}")
        }
        
        // ✅ 现在检查整个 DoPickOrder 是否有库存问题
        val hasStockIssues = checkPickOrderHasStockIssues(saved.id!!)
        if (hasStockIssues) {
            // 更新所有相关的 DoPickOrderLine 状态为 "issue"
            val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(saved.id!!)
            doPickOrderLines.forEach { line ->
                line.status = "issue"
            }
            doPickOrderLineRepository.saveAll(doPickOrderLines)
            println("🔍 DEBUG: Updated ${doPickOrderLines.size} DoPickOrderLine records to 'issue' status")
        }
        
        println("🔍 DEBUG: Created ${results.size} DoPickOrderLine records")
    }
    private fun checkPickOrderHasStockIssues(doPickOrderId: Long): Boolean {
        try {
            // 1. 获取 do_pick_order 的所有 pick orders 数量
            val totalPickOrdersSql = """
                SELECT COUNT(*) as total_pick_orders
                FROM fpsmsdb.do_pick_order_line dpol
                WHERE dpol.do_pick_order_id = :doPickOrderId 
                AND dpol.deleted = 0
            """.trimIndent()
            
            val totalPickOrdersResult = jdbcDao.queryForList(totalPickOrdersSql, mapOf("doPickOrderId" to doPickOrderId))
            val totalPickOrders = (totalPickOrdersResult.firstOrNull()?.get("total_pick_orders") as? Number)?.toInt() ?: 0
            
            if (totalPickOrders == 0) {
                return false  // 没有 pick orders，不算 issue
            }
            
            // 2. 获取有库存问题的 pick orders 数量（通过检查 pick_execution_issue 表）
            val issuePickOrdersSql = """
                SELECT COUNT(DISTINCT dpol.pick_order_id) as issue_pick_orders
                FROM fpsmsdb.do_pick_order_line dpol
                INNER JOIN fpsmsdb.pick_execution_issue pei ON pei.pick_order_id = dpol.pick_order_id
                WHERE dpol.do_pick_order_id = :doPickOrderId 
                AND pei.deleted = 0
                AND dpol.deleted = 0
            """.trimIndent()
            
            val issuePickOrdersResult = jdbcDao.queryForList(issuePickOrdersSql, mapOf("doPickOrderId" to doPickOrderId))
            val issuePickOrders = (issuePickOrdersResult.firstOrNull()?.get("issue_pick_orders") as? Number)?.toInt() ?: 0
            
            // 3. 只有当所有 pick orders 都有问题时才算 issue
            val hasAllPickOrdersIssues = (totalPickOrders > 0) && (issuePickOrders == totalPickOrders)
            
            println("🔍 DEBUG: DoPickOrder $doPickOrderId - Total pick orders: $totalPickOrders, Issue pick orders: $issuePickOrders, All pick orders have issues: $hasAllPickOrdersIssues")
            
            return hasAllPickOrdersIssues
            
        } catch (e: Exception) {
            println("❌ Error checking stock issues for do pick order $doPickOrderId: ${e.message}")
            return false
        }
    }
    fun getBatchReleaseProgress(jobId: String): MessageResponse {
        val s = jobs[jobId] ?: return MessageResponse(
            id = null, code = "NOT_FOUND", name = null, type = null,
            message = "Job not found", errorPosition = null, entity = null
        )
        val finished = s.success.get() + s.failed.size
        val progress = (finished.toDouble() / s.total).coerceIn(0.0, 1.0)

        return MessageResponse(
            id = null, code = if (s.running) "RUNNING" else "FINISHED", name = null, type = null,
            message = null, errorPosition = null, entity = mapOf(
                "jobId" to s.jobId,
                "total" to s.total,
                "finished" to finished,
                "success" to s.success.get(),
                "failedCount" to s.failed.size,
                "failed" to s.failed.take(50),
                "running" to s.running,
                "progress" to progress,
                "startedAt" to s.startedAt,
                "finishedAt" to s.finishedAt
            )
        )
    }
}