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

import com.ffii.core.utils.PdfUtils
import com.ffii.core.utils.ZebraPrinterUtil
import com.ffii.fpsms.m18.entity.M18DataLogRepository
import com.ffii.fpsms.m18.service.M18DataLogService
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrder
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository
import com.ffii.fpsms.modules.deliveryOrder.entity.models.DeliveryOrderInfo
import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus
import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderRequest
import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderResponse
import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderStatusRequest
import com.ffii.fpsms.modules.master.service.CurrencyService
import com.ffii.fpsms.modules.master.service.ShopService
import com.ffii.fpsms.modules.user.entity.UserRepository
import com.ffii.fpsms.modules.user.service.UserService
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.io.IOException
import kotlin.jvm.optionals.getOrDefault
import kotlin.jvm.optionals.getOrNull
import com.ffii.fpsms.modules.deliveryOrder.web.models.DoDetailResponse
import com.ffii.fpsms.modules.deliveryOrder.web.models.DoDetailLineResponse
import com.ffii.fpsms.modules.deliveryOrder.web.models.ReleaseDoRequest
import com.ffii.fpsms.modules.pickOrder.web.models.SavePickOrderRequest
import com.ffii.fpsms.modules.pickOrder.web.models.SavePickOrderLineRequest
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderType
import com.ffii.fpsms.modules.pickOrder.service.PickOrderService
import java.time.LocalDate
import java.math.BigDecimal
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import java.time.LocalDateTime
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrder
import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus
import com.ffii.fpsms.modules.deliveryOrder.web.models.ExportDeliveryNoteRequest
import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintDeliveryNoteRequest
import com.ffii.fpsms.modules.master.service.PrinterService
import java.time.format.DateTimeFormatter
import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository
import java.time.LocalTime
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.StockOutRepository
import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository
import com.ffii.fpsms.modules.stock.entity.StockOut
import com.ffii.fpsms.modules.stock.entity.StockOutLine
import com.ffii.fpsms.modules.stock.web.model.StockOutStatus
import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPolRequest
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
import net.sf.jasperreports.engine.JasperCompileManager
import net.sf.jasperreports.engine.JasperExportManager
import net.sf.jasperreports.engine.JasperPrint
import org.springframework.core.io.ClassPathResource
import java.io.File
import java.io.FileNotFoundException

import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecord
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository
import com.ffii.fpsms.modules.deliveryOrder.web.models.ExportDNLabelsRequest
import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintDNLabelsRequest
import com.ffii.fpsms.modules.pickOrder.entity.RouterRepository
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository
import com.ffii.fpsms.modules.stock.service.InventoryLotService
import com.ffii.fpsms.modules.pickOrder.entity.Router
import net.sf.jasperreports.engine.JasperPrintManager
import net.sf.jasperreports.engine.JRPrintPage
import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository

@Service
open class DeliveryOrderService(
    private val deliveryOrderRepository: DeliveryOrderRepository,
    private val shopService: ShopService,
    private val currencyService: CurrencyService,
    private val m18DataLogService: M18DataLogService,
    private val m18DataLogRepository: M18DataLogRepository,
    private val userService: UserService,
    private val userRepository: UserRepository,
    private val pickOrderService: PickOrderService,
    private val doPickOrderService: DoPickOrderService,
    private val truckRepository: TruckRepository,
    private val pickOrderRepository: PickOrderRepository,
    private val suggestedPickLotService: SuggestedPickLotService,
    private val inventoryLotLineRepository: InventoryLotLineRepository,
    private val stockOutRepository: StockOutRepository,
    private val stockOutLineRepository: StockOutLIneRepository,
    private val pickOrderLineRepository: PickOrderLineRepository,
    private val printerService: PrinterService,
    private val doPickOrderRecordRepository: DoPickOrderRecordRepository,
    private val routerRepository: RouterRepository,
    private val purchaseOrderRepository: PurchaseOrderRepository,
    private val inventoryLotService: InventoryLotService,
    private val suggestedPickLotRepository: SuggestPickLotRepository,
    private val inventoryLotRepository: InventoryLotRepository,
) {

    open fun findByM18DataLogId(m18DataLogId: Long): DeliveryOrder? {
        return deliveryOrderRepository.findTopByM18DataLogIdAndDeletedIsFalseOrderByModifiedDesc(m18DataLogId)
    }

    open fun getDoList(): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findDeliveryOrderInfoByDeletedIsFalse();
    }

    /*
    open fun getDetailedDo(id: Long) : DeliveryOrder? {
        return deliveryOrderRepository.findByIdAndDeletedIsFalse(id);
    }
    */
    open fun getDetailedDo(id: Long): DoDetailResponse? {
        val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(id) ?: return null

        return DoDetailResponse(
            id = deliveryOrder.id!!,
            code = deliveryOrder.code ?: "",
            supplierCode = deliveryOrder.supplier?.code,
            shopCode = deliveryOrder.shop?.code,
            currencyCode = deliveryOrder.currency?.code,
            orderDate = deliveryOrder.orderDate,
            estimatedArrivalDate = deliveryOrder.estimatedArrivalDate,
            completeDate = deliveryOrder.completeDate,
            status = deliveryOrder.status?.value,
            deliveryOrderLines = deliveryOrder.deliveryOrderLines.map { line ->
                DoDetailLineResponse(
                    id = line.id!!,
                    itemNo = line.itemNo,
                    qty = line.qty,
                    price = line.price,
                    status = line.status?.value,
                    itemName = line.item?.name,
                    uomCode = line.uom?.code
                )
            }
        )
    }

    open fun searchByCode(code: String?): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndDeletedIsFalse(code);
    }

    open fun searchByShopName(shopName: String): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByShopNameContainsAndDeletedIsFalse(shopName);
    }

    open fun searchByStatus(status: DeliveryOrderStatus): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByStatusAndDeletedIsFalse(status);
    }

    open fun searchBetweenOrderDate(start: LocalDateTime?, end: LocalDateTime?): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findByOrderDateBetweenAndDeletedIsFalse(start, end);
    }


    open fun getFullList(): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllBy();
    }

    open fun searchAll(
        code: String?,
        shopName: String?,
        status: DeliveryOrderStatus?,
        orderStartDate: LocalDateTime?,
        orderEndDate: LocalDateTime?,
        estArrStartDate: LocalDateTime?,
        estArrEndDate: LocalDateTime?
    ): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndShopNameContainsAndStatusAndOrderDateBetweenAndEstimatedArrivalDateBetweenAndDeletedIsFalse(
            code,
            shopName,
            status,
            orderStartDate,
            orderEndDate,
            estArrStartDate,
            estArrEndDate
        );
    }

    open fun searchWithoutStatus(
        code: String?,
        shopName: String?,
        orderStartDate: LocalDateTime?,
        orderEndDate: LocalDateTime?,
        estArrStartDate: LocalDateTime?,
        estArrEndDate: LocalDateTime?
    ): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndShopNameContainsAndOrderDateBetweenAndEstimatedArrivalDateBetweenAndDeletedIsFalse(
            code,
            shopName,
            orderStartDate,
            orderEndDate,
            estArrStartDate,
            estArrEndDate
        );
    }

    open fun searchWithoutEstArrDate(
        code: String?,
        shopName: String?,
        status: DeliveryOrderStatus?,
        orderStartDate: LocalDateTime?,
        orderEndDate: LocalDateTime?
    ): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndShopNameContainsAndStatusAndOrderDateBetweenAndDeletedIsFalse(
            code,
            shopName,
            status,
            orderStartDate,
            orderEndDate
        );
    }

    open fun searchWithoutEstArrDateAndStatus(
        code: String?,
        shopName: String?,
        orderStartDate: LocalDateTime?,
        orderEndDate: LocalDateTime?
    ): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndShopNameContainsAndOrderDateBetweenAndDeletedIsFalse(
            code,
            shopName,
            orderStartDate,
            orderEndDate
        );
    }

    open fun searchWithoutOrderDate(
        code: String?,
        shopName: String?,
        status: DeliveryOrderStatus?,
        estArrStartDate: LocalDateTime?,
        estArrEndDate: LocalDateTime?
    ): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndShopNameContainsAndStatusAndEstimatedArrivalDateBetweenAndDeletedIsFalse(
            code,
            shopName,
            status,
            estArrStartDate,
            estArrEndDate
        );
    }

    open fun searchWithoutOrderDateAndStatus(
        code: String?,
        shopName: String?,
        estArrStartDate: LocalDateTime?,
        estArrEndDate: LocalDateTime?
    ): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndShopNameContainsAndEstimatedArrivalDateBetweenAndDeletedIsFalse(
            code,
            shopName,
            estArrStartDate,
            estArrEndDate
        );
    }

    open fun searchWithoutDate(
        code: String?,
        shopName: String?,
        status: DeliveryOrderStatus?
    ): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndShopNameContainsAndStatusAndDeletedIsFalse(
            code,
            shopName,
            status
        );
    }

    open fun searchCodeAndShopName(code: String?, shopName: String?): List<DeliveryOrderInfo> {
        return deliveryOrderRepository.findAllByCodeContainsAndShopNameContainsAndDeletedIsFalse(code, shopName);
    }


    open fun updateDeliveryOrderStatus(request: SaveDeliveryOrderStatusRequest): SaveDeliveryOrderResponse {
        val deliveryOrder = checkNotNull(
            request.id?.let { deliveryOrderRepository.findById(it).getOrNull() }
        ) { "ID must be set" }

        val status = checkNotNull(
            request.status?.let { status -> DeliveryOrderStatus.entries.find { it.value == status } }
        ) { "Status must be set" }

        deliveryOrder.apply {
            this.status = status
        }

        val savedDeliveryOrder = deliveryOrderRepository.save(deliveryOrder).let {
            SaveDeliveryOrderResponse(
                id = it.id,
                code = it.code,
                supplierCode = it.supplier?.code,
                shopCode = it.shop?.code,
                currencyCode = it.currency?.code,
                orderDate = it.orderDate,
                estimatedArrivalDate = it.estimatedArrivalDate,
                completeDate = it.completeDate,
                status = it.status?.value
            )
        }

        return savedDeliveryOrder
    }

    @Throws(IOException::class)
    @Transactional
    open fun saveDeliveryOrder(request: SaveDeliveryOrderRequest): SaveDeliveryOrderResponse {
        val deliveryOrder =
            request.id?.let { deliveryOrderRepository.findById(it).getOrDefault(DeliveryOrder()) } ?: DeliveryOrder()
        val supplier = request.m18SupplierId?.let { shopService.findVendorByM18Id(it) }
            ?: request.supplierId?.let { shopService.findById(it) }
        val shop = request.m18ShopId?.let { shopService.findShopByM18Id(it) }
            ?: request.shopId?.let { shopService.findById(it) }
        val currency = request.m18CurrencyId?.let { currencyService.findByM18Id(it) }
            ?: request.currencyId?.let { currencyService.findById(it) }
        val status = request.status?.let { status -> DeliveryOrderStatus.entries.find { it.value == status } }
        val m18DataLog = request.m18DataLogId?.let { m18DataLogRepository.findById(it).getOrNull() }
        val handler = request.handlerId?.let { userRepository.findById(it).getOrNull() }

        //Need check duplicate?
        deliveryOrder.apply {
            code = request.code
            this.supplier = supplier
            this.shop = shop
            this.currency = currency
            orderDate = request.orderDate
            estimatedArrivalDate = request.estimatedArrivalDate
            completeDate = request.completeDate
            this.status = status
            this.m18DataLog = m18DataLog
            this.handler = handler
            m18BeId = request.m18BeId
        }

        val savedDeliveryOrder = deliveryOrderRepository.saveAndFlush(deliveryOrder).let {
            SaveDeliveryOrderResponse(
                id = it.id,
                code = it.code,
                supplierCode = it.supplier?.code,
                shopCode = it.shop?.code,
                currencyCode = it.currency?.code,
                orderDate = it.orderDate,
                estimatedArrivalDate = it.estimatedArrivalDate,
                completeDate = it.completeDate,
                status = it.status?.value
            )
        }

        return savedDeliveryOrder
    }

    @Transactional(rollbackFor = [Exception::class])
    open fun releaseDeliveryOrder(request: ReleaseDoRequest): MessageResponse {
        println("�� DEBUG: Starting releaseDeliveryOrder for DO ID: ${request.id}, User ID: ${request.userId}")
        
        val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.id) 
            ?: throw NoSuchElementException("Delivery Order not found")
        
        println("�� DEBUG: Found delivery order - ID: ${deliveryOrder.id}, Shop: ${deliveryOrder.shop?.code}, Status: ${deliveryOrder.status}")
        
        deliveryOrder.apply {
            status = DeliveryOrderStatus.PENDING
        }
        deliveryOrderRepository.save(deliveryOrder)

        val pols = deliveryOrder.deliveryOrderLines.map {
            SavePickOrderLineRequest(
                itemId = it.item?.id,
                qty = it.qty ?: BigDecimal.ZERO,
                uomId = it.uom?.id,
            )
        }
        val po = SavePickOrderRequest(
            doId = deliveryOrder.id,
            type = PickOrderType.DELIVERY_ORDER,
            targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now(),
            pickOrderLine = pols
        )

        val createdPickOrder = pickOrderService.create(po)
        println("🔍 DEBUG: Created pick order - ID: ${createdPickOrder.id}")
        
        val consoCode = pickOrderService.assignConsoCode()
        val pickOrderEntity = pickOrderRepository.findById(createdPickOrder.id!!).orElse(null)
        
        if (pickOrderEntity != null) {
            pickOrderEntity.consoCode = consoCode
            pickOrderEntity.status = com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED
            pickOrderRepository.saveAndFlush(pickOrderEntity)
            println("�� DEBUG: Assigned consoCode $consoCode to pick order ${createdPickOrder.id}")
            
            // ✅ Debug: Check pick order lines
            println("�� DEBUG: Pick order has ${pickOrderEntity.pickOrderLines?.size ?: 0} pick order lines")
            pickOrderEntity.pickOrderLines?.forEach { line ->
                println("🔍 DEBUG: Pick order line - Item ID: ${line.item?.id}, Qty: ${line.qty}")
            }
            val lines = pickOrderLineRepository.findAllByPickOrderId(pickOrderEntity.id!!)
            println("🔍 DEBUG: Loaded ${lines.size} pick order lines from DB")
            if (lines.isEmpty()) {
                println("⚠️ No pick order lines found; suggestions will be empty")
            }
            // ✅ Create suggested pick lots and hold inventory (like normal release)
            println("🔍 DEBUG: About to call suggestionForPickOrderLines for pick order ${pickOrderEntity.id}")
            val suggestions = suggestedPickLotService.suggestionForPickOrderLines(
                SuggestedPickLotForPolRequest(pickOrderLines = lines)
            )
            println("🔍 DEBUG: Got ${suggestions.suggestedList.size} suggested pick lots")
            
            if (suggestions.suggestedList.isEmpty()) {
                println("⚠️ WARNING: No suggested pick lots generated - this might be due to no inventory available")
            }
            
            val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList)
            println("🔍 DEBUG: Saved ${saveSuggestedPickLots.size} suggested pick lots")
            
            
            // ✅ Hold inventory quantities
            val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn(
                saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id }
            )
            
            saveSuggestedPickLots.forEach { lot ->
                if (lot.suggestedLotLine != null && lot.suggestedLotLine?.id != null && lot.suggestedLotLine!!.id!! > 0) {
                    val lineIndex = inventoryLotLines.indexOf(lot.suggestedLotLine)
                    if (lineIndex >= 0) {
                        inventoryLotLines[lineIndex].holdQty = 
                            (inventoryLotLines[lineIndex].holdQty ?: BigDecimal.ZERO).plus(lot.qty ?: BigDecimal.ZERO)
                    }
                }
            }
            inventoryLotLineRepository.saveAll(inventoryLotLines)
            
            // ✅ Create stock out record and pre-create stock out lines
            val stockOut = StockOut().apply {
                this.type = "job"
                this.consoPickOrderCode = consoCode
                this.status = StockOutStatus.PENDING.status
                this.handler = request.userId
            }
            val savedStockOut = stockOutRepository.saveAndFlush(stockOut)
            
            // ✅ Pre-create stock out lines for suggested lots
            saveSuggestedPickLots.forEach { lot ->
                val polId = lot.pickOrderLine?.id
                val illId = lot.suggestedLotLine?.id
                if (polId != null && illId != null) {
                    val existingLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(polId, illId)
                    if (existingLines.isEmpty()) {
                        val pickOrderLine = pickOrderLineRepository.findById(polId).orElse(null)
                        val inventoryLotLine = inventoryLotLineRepository.findById(illId).orElse(null)
                        
                        if (pickOrderLine != null && inventoryLotLine != null) {
                            val line = StockOutLine().apply {
                                this.stockOut = savedStockOut
                                this.pickOrderLine = pickOrderLine
                                this.inventoryLotLine = inventoryLotLine
                                this.item = pickOrderLine.item
                                this.status = StockOutLineStatus.PENDING.status
                                this.qty = 0.0
                            }
                            stockOutLineRepository.save(line)
                        }
                    }
                }
            }
        }

        // ✅ CREATE do_pick_order_record entries
        val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now()
        val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("ddMMyy"))
        
        println("�� DEBUG: Target date: $targetDate, Date prefix: $datePrefix")

        val truck = deliveryOrder.shop?.id?.let { shopId ->
            println("🔍 DEBUG: Looking for truck with shop ID: $shopId")
            val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId)
            println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId")
            trucks.forEach { t ->
                println("🔍 DEBUG: Truck ID: ${t.id}, DepartureTime: ${t.departureTime}")
            }
            val selectedTruck = trucks.minByOrNull { it.departureTime ?: LocalTime.MAX }
            println("🔍 DEBUG: Selected truck: ID=${selectedTruck?.id}, DepartureTime=${selectedTruck?.departureTime}")
            selectedTruck
        }
        
        println("🔍 DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines")
        
        // ✅ Group lines by store ID to get unique ticket numbers per store
        val linesByStore = deliveryOrder.deliveryOrderLines.groupBy { line ->
            if (deliveryOrder.supplier?.code == "P06B") "4/F" else "2/F"
        }

        linesByStore.forEach { (storeId, lines) ->
            // ✅ Get ticket number for this specific store
            val nextTicketNumber = doPickOrderService.getNextTicketNumber(datePrefix, storeId)
            println("🔍 DEBUG: Next ticket number for store $storeId: $nextTicketNumber")

            lines.forEach { line ->
                println("�� DEBUG: Processing line - Item ID: ${line.item?.id}, Store ID: $storeId")

                val doPickOrderRecord = DoPickOrderRecord(
                    storeId = storeId,
                    ticketNo = nextTicketNumber,
                    ticketStatus = DoPickOrderStatus.pending,
                    truckId = truck?.id,
                    pickOrderId = createdPickOrder.id,
                    truckDepartureTime = truck?.departureTime,
                    itemId = line.item?.id,
                    shopId = deliveryOrder.shop?.id,
                    shopPoSupplierId = deliveryOrder.shop?.id,
                    handledBy = null
                )

                println("�� DEBUG: Creating DoPickOrderRecord - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}")

                val savedDoPickOrderRecord = doPickOrderRecordRepository.save(doPickOrderRecord)
                println("🔍 DEBUG: Saved DoPickOrderRecord - ID: ${savedDoPickOrderRecord.id}")
            }
            
            // ✅ Also create DoPickOrder records for this store
            lines.forEach { line ->
                println("�� DEBUG: Creating DoPickOrder - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}")

                val doPickOrder = DoPickOrder(
                    storeId = storeId,
                    ticketNo = nextTicketNumber,
                    ticketStatus = DoPickOrderStatus.pending,
                    truckId = truck?.id,
                    pickOrderId = createdPickOrder.id,
                    truckDepartureTime = truck?.departureTime,
                    itemId = line.item?.id,
                    shopId = deliveryOrder.shop?.id,
                    shopPoSupplierId = deliveryOrder.shop?.id,
                    handledBy = null
                )

                val savedDoPickOrder = doPickOrderService.save(doPickOrder)
                println("🔍 DEBUG: Saved DoPickOrder - ID: ${savedDoPickOrder.id}")
            }
        }
        return MessageResponse(
            id = deliveryOrder.id,
            code = deliveryOrder.code,
            name = deliveryOrder.shop?.name,
            type = null,
            message = null,
            errorPosition = null,
            entity = mapOf("status" to deliveryOrder.status?.value)
        )
    }

    open fun getRouteAndIndexByInventoryLotId(inventoryLotId: Int): List<Router> {
        return routerRepository.findByInventoryLotIdAndDeletedFalse(inventoryLotId)
            .sortedBy { it.index }
    }

    open fun getRoutesByInventoryLotId(inventoryLotId: Int): List<String> {
        return getRouteAndIndexByInventoryLotId(inventoryLotId)
            .mapNotNull { it.route }
    }

    open fun getRouteByItemId(itemId: Long): String? {
        val inventoryLots = inventoryLotService.findByItemId(itemId)
        if (inventoryLots.isNotEmpty()){
            val inventoryLotId = inventoryLots.first().id?.toInt()
            return inventoryLotId?.let { lotId ->
                getRoutesByInventoryLotId(lotId).firstOrNull()
            }
        }
        return null
    }

    open fun getRouterIndexByItemId(itemId: Long): Int? {
        val inventoryLots = inventoryLotService.findByItemId(itemId)
        if( inventoryLots.isNotEmpty()){
            val inventoryLotId = inventoryLots.first().id?.toInt()
            return inventoryLotId?.let { lotId ->
                getRouteAndIndexByInventoryLotId(lotId).firstOrNull()?.index
            }
        }
        return null
    }

    open fun getLotNumbersForPickOrderByItemId(itemId: Long, pickOrderId: Long): String {
        try {
            val pickOrderLines = pickOrderLineRepository.findAllByPickOrderId(pickOrderId)
            val pickOrderLineIds = pickOrderLines.mapNotNull { it.id }
            val suggestedPickLots = suggestedPickLotRepository.findAllByPickOrderLineIdIn(pickOrderLineIds)

            val lotNumbers = suggestedPickLots
                .filter { it.pickOrderLine?.item?.id == itemId }
                .mapNotNull { it.suggestedLotLine?.inventoryLot?.lotNo }
                .distinct()

            return lotNumbers.joinToString(", ")
        } catch (e: Exception) {
            println("Error getting lot numbers for item $itemId in pick order $pickOrderId: ${e.message}")
            return ""
        }
    }


    //Delivery Note
    @Throws(IOException::class)
    @Transactional
    open fun exportDeliveryNote(request: ExportDeliveryNoteRequest): Map<String, Any> {
        val DELIVERYNOTE_PDF = "DeliveryNote/DeliveryNotePDF.jrxml"
        val resource = ClassPathResource(DELIVERYNOTE_PDF)
        if(!resource.exists()){
            throw FileNotFoundException("Report file not fount: $DELIVERYNOTE_PDF")
        }
        val inputStream = resource.inputStream
        val deliveryNote = JasperCompileManager.compileReport(inputStream)
        val deliveryNoteInfo = deliveryOrderRepository.findDeliveryOrderInfoById(request.deliveryOrderIds).toMutableList()

        val fields = mutableListOf<MutableMap<String, Any>>()
        val params = mutableMapOf<String, Any>()

        val deliveryOrderEntity = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.deliveryOrderIds)
        val selectedTruckNo = deliveryOrderEntity?.shop?.id?.let { shopId ->
            val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId)
            trucks.firstOrNull()?.truckNo
        } ?: ""

        val selectedPickOrder = pickOrderRepository.findById(request.pickOrderIds).orElse(null)


        for (info in deliveryNoteInfo) {
            val sortedLines = info.deliveryOrderLines.sortedBy { line ->
                line.itemId?.let { itemId ->
                    getRouterIndexByItemId(itemId)
                } ?: Int.MAX_VALUE
            }

            sortedLines.forEachIndexed { index, line ->

                val field = mutableMapOf<String, Any>()

                field["sequenceNumber"] = (index + 1).toString()
                field["itemNo"] = line.itemNo
                field["itemName"] = line.itemName ?:""
                field["uom"] = line.uom ?:""
                field["qty"] = line.qty.toString()
                field["shortName"] = line.uomShortDesc ?:""

                val route = line.itemId?.let { itemId ->
                    getRouteByItemId(itemId)
                } ?: ""
                field["route"] = route

                val lotNo = line.itemId?.let { itemId ->
                    getLotNumbersForPickOrderByItemId(itemId, request.pickOrderIds)
                } ?: ""
                field["lotNo"] = lotNo

                fields.add(field)
            }
        }

        if(request.isDraft){
            params["dnTitle"] = "送貨單(初稿)"
            params["colQty"] = "所需數量"
            params["totalCartonTitle"] = ""
        }
        else{
            params["dnTitle"] = "送貨單"
            params["colQty"] = "數量"
            params["totalCartonTitle"] = "總箱數："
        }

        params["numOfCarton"] = request.numOfCarton.toString()
        if(params["numOfCarton"] == "0"){
            params["numOfCarton"] = ""
        }

        params["deliveryOrderCode"] = deliveryNoteInfo[0].code
        params["shopName"] = deliveryNoteInfo[0].shopName ?: ""
        params["shopAddress"] = deliveryNoteInfo[0].shopAddress ?: ""
        params["deliveryDate"] = deliveryNoteInfo[0].estimatedArrivalDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: ""
        params["truckNo"] = selectedTruckNo
        params["ShopPurchaseOrderNo"] = deliveryNoteInfo[0].code
        params["FGPickOrderNo"] = selectedPickOrder?.code ?: ""


            return mapOf(
            "report" to PdfUtils.fillReport(deliveryNote, fields, params),
            "filename" to deliveryNoteInfo[0].code
        )
    }


    //Print Delivery Note
    @Transactional
    open fun printDeliveryNote(request: PrintDeliveryNoteRequest){
        //val printer = printerService.findById(request.printerId) ?: throw java.util.NoSuchElementException("No such printer")

        val pdf = exportDeliveryNote(
            ExportDeliveryNoteRequest(
                deliveryOrderIds = request.deliveryOrderId,
                numOfCarton = request.numOfCarton,
                isDraft = request.isDraft,
                pickOrderIds = request.pickOrderId
            )
        )

        val jasperPrint = pdf["report"] as JasperPrint

        val tempPdfFile = File.createTempFile("print_job_",".pdf")

        try{
            JasperExportManager.exportReportToPdfFile(jasperPrint,tempPdfFile.absolutePath)

        //    val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty
        //    printer.ip?.let { ip -> printer.port?.let { port ->
        //        ZebraPrinterUtil.printPdfToZebra(tempPdfFile, ip, port, printQty, ZebraPrinterUtil.PrintDirection.ROTATED)
         //   }}
        } finally {
        //    tempPdfFile.delete()
        }

    }

    //Carton Labels
    open fun exportDNLabels(request: ExportDNLabelsRequest): Map<String, Any>{
        val DNLABELS_PDF = "DeliveryNote/DeliveryNoteCartonLabelsPDF.jrxml"
        val resource = ClassPathResource(DNLABELS_PDF)
        if(!resource.exists()){
            throw FileNotFoundException("Label file not found: $DNLABELS_PDF")
        }
        val inputStream = resource.inputStream
        val cartonLabel = JasperCompileManager.compileReport(inputStream)
        val cartonLabelInfo = deliveryOrderRepository.findDeliveryOrderInfoById(request.deliveryOrderIds).toMutableList()
        val params = mutableMapOf<String, Any>()
        val fields = mutableListOf<MutableMap<String ,Any>>()
        for (info in cartonLabelInfo) {
            val field = mutableMapOf<String, Any>()
        }

        params["shopPurchaseOrderNo"] = cartonLabelInfo[0].code
        params["deliveryOrderCode"] = cartonLabelInfo[0].code
        params["shopAddress"] = cartonLabelInfo[0].shopAddress?: ""
        params["shopName"] = cartonLabelInfo[0].shopName?: ""

        for(cartonNumber in 1..request.numOfCarton){
            val field = mutableMapOf<String, Any>()
            fields.add(field)
        }

        return mapOf(
            "report" to PdfUtils.fillReport(cartonLabel, fields, params),
            "filename" to "${cartonLabelInfo[0].code}_carton_labels"
        )


    }


    //Print Carton Labels
    @Transactional
    open fun printDNLabels(request: PrintDNLabelsRequest){
        //val printer = printerService.findById(request.printerId) ?: throw java.util.NoSuchElementException("No such printer")

        val pdf = exportDNLabels(
            ExportDNLabelsRequest(
                deliveryOrderIds = request.deliveryOrderId,
                numOfCarton = request.numOfCarton
            )
        )

        val jasperPrint = pdf["report"] as JasperPrint

        val tempPdfFile = File.createTempFile("print_job_",".pdf")

        try{
            JasperExportManager.exportReportToPdfFile(jasperPrint,tempPdfFile.absolutePath)


            //val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty
            //printer.ip?.let { ip -> printer.port?.let { port ->
            //     ZebraPrinterUtil.printPdfToZebra(tempPdfFile, ip, port, printQty, ZebraPrinterUtil.PrintDirection.ROTATED)
             //}}

            println("Test PDF saved to: ${tempPdfFile.absolutePath}")

        } finally {
            //tempPdfFile.delete()
        }

    }

}