package com.ffii.fpsms.modules.common.internalSetup

import com.ffii.core.utils.ExcelUtils
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.springframework.stereotype.Component
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.transaction.annotation.Transactional
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.entity.WarehouseRepository
import com.ffii.fpsms.modules.master.service.ItemUomService
import com.ffii.fpsms.modules.stock.service.StockInLineService
import com.ffii.fpsms.modules.stock.web.model.StockInRequest
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.web.model.PrintQrCodeForSilRequest

@Component
open class InventorySetup {

    @Autowired
    private lateinit var itemsRepository: ItemsRepository

    @Autowired
    private lateinit var warehouseRepository: WarehouseRepository

    @Autowired
    private lateinit var stockInLineService: StockInLineService

    @Autowired
    private lateinit var itemUomService: ItemUomService

    private val DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd")

    @Transactional(rollbackFor = [Exception::class])
    open fun importExcelFromLocal(filePath: String): Int {
        val file = File(filePath)
        if (!file.exists()) {
            throw IOException("File not found: $filePath")
        }

        FileInputStream(file).use { inputStream ->
            val workbook: Workbook = XSSFWorkbook(inputStream)

            try {
                val inventoryDataList = mutableListOf<InventoryData>()

                // New template: B=store_id, C=warehouse, D=area, E=slot, F=item code, J=productionLotNo, K=quantity (sales unit), O=expiry date
                val COLUMN_STORE_ID_INDEX = 1   // B
                val COLUMN_WAREHOUSE_INDEX = 2   // C
                val COLUMN_AREA_INDEX = 3        // D
                val COLUMN_SLOT_INDEX = 4        // E
                val COLUMN_ITEM_CODE_INDEX = 5   // F
                val COLUMN_PRODUCT_LOT_NO_INDEX = 9  // J
                val COLUMN_QUANTITY_INDEX = 10   // K (sales unit)
                val COLUMN_EXPIRY_DATE_INDEX = 14    // O

                val START_ROW_INDEX = 6  // Start from Excel row 7 (0-based: index 6)

                // ────────────────────────────────────────────────
                // Process all sheets, data rows from row 7
                // ────────────────────────────────────────────────
                for (sheetIndex in 0 until workbook.numberOfSheets) {
                    val sheet: Sheet = workbook.getSheetAt(sheetIndex)
                    val sheetName = sheet.sheetName
                    for (rowIndex in START_ROW_INDEX..sheet.lastRowNum) {
                        val row = sheet.getRow(rowIndex) ?: continue

                        val itemNo = ExcelUtils.getStringValue(row.getCell(COLUMN_ITEM_CODE_INDEX))?.trim() ?: ""

                        if (itemNo.isEmpty()) {
                            println("SKIP sheet '$sheetName' row ${rowIndex + 1} - Missing item code")
                            continue
                        }

                        val store_id = ExcelUtils.getStringValue(row.getCell(COLUMN_STORE_ID_INDEX))?.trim() ?: ""
                        val warehouse = ExcelUtils.getStringValue(row.getCell(COLUMN_WAREHOUSE_INDEX))?.trim() ?: ""
                        val area = ExcelUtils.getStringValue(row.getCell(COLUMN_AREA_INDEX))?.trim() ?: ""
                        val slot = ExcelUtils.getStringValue(row.getCell(COLUMN_SLOT_INDEX))?.trim() ?: ""

                        val locationCode = if (store_id.isNotEmpty() && warehouse.isNotEmpty() &&
                            area.isNotEmpty() && slot.isNotEmpty()) {
                            "$store_id-$warehouse-$area-$slot"
                        } else {
                            ""
                        }

                        if (locationCode.isEmpty()) {
                            println("SKIP sheet '$sheetName' row ${rowIndex + 1} - No location (store_id/warehouse/area/slot required)")
                            continue
                        }

                        val quantityStr = ExcelUtils.getStringValue(row.getCell(COLUMN_QUANTITY_INDEX))?.trim()
                        val salesQuantity = quantityStr?.toBigDecimalOrNull()

                        if (salesQuantity == null || salesQuantity <= BigDecimal.ZERO) {
                            println("SKIP sheet '$sheetName' row ${rowIndex + 1} - Invalid or zero quantity: $quantityStr")
                            continue
                        }

                        val productLotNo = ExcelUtils.getStringValue(row.getCell(COLUMN_PRODUCT_LOT_NO_INDEX))?.trim()

                        val expiryDateStr = ExcelUtils.getStringValue(row.getCell(COLUMN_EXPIRY_DATE_INDEX))?.trim()
                        val expiryDate = expiryDateStr?.let {
                            try {
                                LocalDate.parse(it, DATE_FORMAT)
                            } catch (e: Exception) {
                                println("WARNING sheet '$sheetName' row ${rowIndex + 1} - Invalid expiry date format: $it (expected yyyy/MM/dd), using default: 1 month later")
                                null
                            }
                        } ?: LocalDate.now().plusMonths(1)

                        val finalExpiryDate = expiryDate ?: LocalDate.now().plusMonths(1)

                        inventoryDataList.add(
                            InventoryData(
                                itemNo = itemNo,
                                locationCode = locationCode,
                                quantitySalesUnit = salesQuantity,
                                productLotNo = productLotNo,
                                expiryDate = finalExpiryDate
                            )
                        )
                    }
                }

                println("Total valid rows collected: ${inventoryDataList.size}")

                // ────────────────────────────────────────────────
                // Create stock in (convert sales qty -> stock qty per item)
                // ────────────────────────────────────────────────
                var createdCount = 0
                var notFoundCount = 0
                var warehouseSkippedCount = 0

                for (data in inventoryDataList) {
                    try {
                        val item = itemsRepository.findFirstByCodeAndDeletedFalse(data.itemNo)

                        if (item == null) {
                            println("NOT FOUND - Item with code '${data.itemNo}' does not exist")
                            notFoundCount++
                            continue
                        }

                        if (item.id == null) {
                            println("ERROR - Item '${data.itemNo}' has no ID")
                            continue
                        }

                        val warehouse = warehouseRepository.findByCodeAndDeletedIsFalse(data.locationCode)
                        if (warehouse == null) {
                            println("SKIP - No warehouse for location '${data.locationCode}' (no default)")
                            warehouseSkippedCount++
                            continue
                        }

                        if (warehouse.id == null) {
                            println("ERROR - Warehouse '${warehouse.code}' has no ID")
                            continue
                        }

                        val stockQuantityRaw = itemUomService.findSalesUnitByItemId(item.id!!)?.uom?.id?.let { salesUomId ->
                            itemUomService.convertQtyToStockQty(item.id!!, salesUomId, data.quantitySalesUnit)
                        } ?: data.quantitySalesUnit
                        val stockQuantity = stockQuantityRaw.setScale(0, RoundingMode.FLOOR)

                        if (stockQuantity != stockQuantityRaw) {
                            println("FLOOR applied for item ${data.itemNo}: $stockQuantityRaw -> $stockQuantity (sales qty: ${data.quantitySalesUnit})")
                        }

                        if (stockQuantity <= BigDecimal.ZERO) {
                            if (stockQuantityRaw > BigDecimal.ZERO) {
                                println("SKIP - Stock quantity became 0 after floor for item ${data.itemNo} (sales qty: ${data.quantitySalesUnit}, converted before floor: $stockQuantityRaw)")
                            } else {
                                println("SKIP - Stock quantity <= 0 for item ${data.itemNo} (sales qty: ${data.quantitySalesUnit})")
                            }
                            continue
                        }

                        val today = LocalDate.now()
                        val dnNo = "LT-${today.format(DateTimeFormatter.ofPattern("yyyyMMdd"))}-${data.itemNo}"

                        val stockInRequest = StockInRequest(
                            itemId = item.id!!,
                            itemNo = data.itemNo,
                            demandQty = stockQuantity,
                            acceptedQty = stockQuantity,
                            expiryDate = data.expiryDate,
                            lotNo = null,
                            productLotNo = data.productLotNo,
                            dnNo = dnNo,
                            type = "OPEN",
                            warehouseId = warehouse.id!!
                        )

                        stockInLineService.createStockIn(stockInRequest)
                        createdCount++
                        println("CREATED stock in for item: ${data.itemNo} (sales qty: ${data.quantitySalesUnit} -> stock qty: $stockQuantity, warehouse: ${warehouse.code})")
                    } catch (e: Exception) {
                        println("Error processing inventory row (itemNo: ${data.itemNo}): ${e.message}")
                        e.printStackTrace()
                        continue
                    }
                }

                println("Import finished. Created $createdCount stock in records, Not found: $notFoundCount items, Skipped (no location): $warehouseSkippedCount.")
                return createdCount

            } finally {
                workbook.close()
            }
        }
    }

    data class InventoryData(
        val itemNo: String,
        val locationCode: String,
        val quantitySalesUnit: BigDecimal,
        val productLotNo: String?,
        val expiryDate: LocalDate
    )

    @Autowired
    private lateinit var inventoryLotLineRepository: InventoryLotLineRepository
    @Transactional(rollbackFor = [Exception::class])
    open fun printAllLotStockInLabels(
        printerId: Long,
        printQty: Int = 1,
        fromIndex: Int? = null,
        toIndex: Int? = null
    ): Int {
        println("=== Starting to print stock in labels for all lots ===")

        // Get all inventory lot lines that are not deleted and have stock in lines
        val allInventoryLotLines = try {
            inventoryLotLineRepository.findAllByDeletedIsFalseAndHasStockInLine()
        } catch (e: Exception) {
            println("Error fetching inventory lot lines: ${e.message}")
            e.printStackTrace()
            return 0
        }

        if (allInventoryLotLines.isEmpty()) {
            println("No inventory lot lines found to print")
            return 0
        }

        // Apply range filter if provided
        val inventoryLotLinesToPrint = if (fromIndex != null && toIndex != null) {
            val startIndex = fromIndex.coerceAtLeast(0)
            val endIndex = toIndex.coerceAtMost(allInventoryLotLines.size - 1)
            if (startIndex > endIndex || startIndex >= allInventoryLotLines.size) {
                println("Invalid range: fromIndex=$fromIndex, toIndex=$toIndex, total items=${allInventoryLotLines.size}")
                return 0
            }
            println("Printing range: index $startIndex to $endIndex (out of ${allInventoryLotLines.size} total items)")
            allInventoryLotLines.subList(startIndex, endIndex + 1)
        } else {
            println("Printing all ${allInventoryLotLines.size} inventory lot lines")
            allInventoryLotLines
        }

        println("Found ${inventoryLotLinesToPrint.size} inventory lot lines to print")

        var printedCount = 0
        var errorCount = 0

        // Loop through each inventory lot line
        for ((index, inventoryLotLine) in inventoryLotLinesToPrint.withIndex()) {
            try {
                // Get the associated stock in line
                val stockInLine = inventoryLotLine.inventoryLot?.stockInLine
                    ?: throw IllegalArgumentException("No stock in line associated with inventory lot line ID: ${inventoryLotLine.id}")

                val stockInLineId = stockInLine.id
                    ?: throw IllegalArgumentException("Stock in line has no ID")

                val actualIndex = if (fromIndex != null) fromIndex + index else index
                println("Processing lot ${actualIndex + 1}/${allInventoryLotLines.size}: Lot No: ${inventoryLotLine.inventoryLot?.lotNo}, StockInLineId: $stockInLineId")

                // Create print request
                val printRequest = PrintQrCodeForSilRequest(
                    stockInLineId = stockInLineId,
                    printerId = printerId,
                    printQty = printQty
                )

                // Print the label
                stockInLineService.printQrCode(printRequest)
                printedCount++

                println("✓ Successfully printed label for lot: ${inventoryLotLine.inventoryLot?.lotNo}")

            } catch (e: Exception) {
                errorCount++
                println("✗ Error printing label for inventory lot line ID ${inventoryLotLine.id}: ${e.message}")
                e.printStackTrace()
                // Continue with next lot instead of stopping
                continue
            }
        }

        println("=== Printing finished ===")
        println("Total processed: ${inventoryLotLinesToPrint.size}")
        println("Successfully printed: $printedCount")
        println("Errors: $errorCount")

        return printedCount
    }

    @Transactional(rollbackFor = [Exception::class])
    open fun printLotStockInLabelsByItemIds(
        printerId: Long,
        itemIds: List<Long>,
        printQty: Int = 1,
        fromIndex: Int? = null,
        toIndex: Int? = null
    ): Int {
        if (itemIds.isEmpty()) return 0

        val allInventoryLotLines = inventoryLotLineRepository
            .findAllByItemIdIn(itemIds)
            .filter { it.deleted == false && it.inventoryLot?.stockInLine != null }

        if (allInventoryLotLines.isEmpty()) return 0

        val inventoryLotLinesToPrint = if (fromIndex != null && toIndex != null) {
            val startIndex = fromIndex.coerceAtLeast(0)
            val endIndex = toIndex.coerceAtMost(allInventoryLotLines.size - 1)
            if (startIndex > endIndex || startIndex >= allInventoryLotLines.size) return 0
            allInventoryLotLines.subList(startIndex, endIndex + 1)
        } else {
            allInventoryLotLines
        }

        var printedCount = 0
        for ((index, inventoryLotLine) in inventoryLotLinesToPrint.withIndex()) {
            val stockInLineId = inventoryLotLine.inventoryLot?.stockInLine?.id
                ?: throw IllegalArgumentException("Stock in line missing")
            val actualIndex = if (fromIndex != null) fromIndex + index else index
            println("Processing lot ${actualIndex + 1}/${allInventoryLotLines.size}: Lot No: ${inventoryLotLine.inventoryLot?.lotNo}, StockInLineId: $stockInLineId")
            stockInLineService.printQrCode(
                PrintQrCodeForSilRequest(
                    stockInLineId = stockInLineId,
                    printerId = printerId,
                    printQty = printQty
                )
            )
            printedCount++
        }
        return printedCount
    }
    data class PrintProgressResult(
    val success: Boolean,
    val lastIndex: Int,
    val totalLots: Int,
    val errorMessage: String? = null
)
    fun printLotStockInLabelsByItemIdsV2(
        printerId: Long,
        itemIds: List<Long>,
        printQty: Int = 1,
        fromIndex: Int? = null,
        toIndex: Int? = null
    ): PrintProgressResult {
        if (itemIds.isEmpty()) {
            println("No itemIds provided, nothing to print")
            return PrintProgressResult(
                success = false,
                lastIndex = -1,
                totalLots = 0,
                errorMessage = "itemIds is empty"
            )
        }
    
        // 1. 查出并过滤 lot（可以按你 v2 的逻辑过滤 inQty-outQty=0）
        val allInventoryLotLines = inventoryLotLineRepository
            .findAllByItemIdIn(itemIds)
            .filter { it.deleted == false && it.inventoryLot?.stockInLine != null }
    
        if (allInventoryLotLines.isEmpty()) {
            println("No inventory lot lines found for itemIds=$itemIds")
            return PrintProgressResult(
                success = false,
                lastIndex = -1,
                totalLots = 0,
                errorMessage = "no lots found for given itemIds"
            )
        }
    
        val totalLots = allInventoryLotLines.size
    
        // 如果要过滤 inQty-outQty = 0，就在这里再 filter 一次（你已经写过类似逻辑了）
        val lotsToUse = allInventoryLotLines  // 或者 nonZeroLots
        val effectiveTotal = lotsToUse.size   // 这就是你的 totalLots（按你的业务决定）
    
        // 2. 计算本次要打的范围
        val startIndex = (fromIndex ?: 0).coerceAtLeast(0)
        val endIndex = (toIndex ?: (effectiveTotal - 1)).coerceAtMost(effectiveTotal - 1)
    
        if (startIndex > endIndex || startIndex >= effectiveTotal) {
            println("Invalid range: fromIndex=$fromIndex, toIndex=$toIndex, totalLots=$effectiveTotal")
            return PrintProgressResult(
                success = false,
                lastIndex = startIndex - 1,
                totalLots = effectiveTotal,
                errorMessage = "invalid index range"
            )
        }
    
        println("Printing range: $startIndex to $endIndex (out of $effectiveTotal)")
    
        // 3. 循环打印，实时更新 lastIndex，出现打印错误时立即返回
        var lastIndex = startIndex - 1  // 还没成功打印任何一张时是 startIndex-1
    
        for (globalIndex in startIndex..endIndex) {
            val inventoryLotLine = lotsToUse[globalIndex]
            val stockInLineId = inventoryLotLine.inventoryLot?.stockInLine?.id
                ?: return PrintProgressResult(
                    success = false,
                    lastIndex = lastIndex,
                    totalLots = effectiveTotal,
                    errorMessage = "Stock in line missing for inventoryLotLineId=${inventoryLotLine.id}"
                )
    
            try {
                println("Processing lot ${globalIndex + 1}/$effectiveTotal: lotNo=${inventoryLotLine.inventoryLot?.lotNo}, stockInLineId=$stockInLineId")
                stockInLineService.printQrCode(
                    PrintQrCodeForSilRequest(
                        stockInLineId = stockInLineId,
                        printerId = printerId,
                        printQty = printQty
                    )
                )
                lastIndex = globalIndex
                println("✓ Printed label for lotNo=${inventoryLotLine.inventoryLot?.lotNo}")
            } catch (e: Exception) {
                // 打印异常（最常见：打印机没纸 / 打印机错误）
                println("✗ Printer error at index=$globalIndex, lotLineId=${inventoryLotLine.id}: ${e.message}")
                e.printStackTrace()
    
                val msg = when {
                    e.message?.contains("paper", ignoreCase = true) == true ->
                        "Printer error (maybe out of paper): ${e.message}"
                    else ->
                        "Printer error: ${e.message}"
                }
    
                return PrintProgressResult(
                    success = false,
                    lastIndex = lastIndex,   // 最后成功打印到哪一张
                    totalLots = effectiveTotal,
                    errorMessage = msg
                )
            }
        }
    
        // 能循环走完，说明这段全部打完
        return PrintProgressResult(
            success = true,
            lastIndex = lastIndex,    // 成功场景下，这里会等于 endIndex
            totalLots = effectiveTotal,
            errorMessage = null
        )
    }
    @Transactional(rollbackFor = [Exception::class])
open fun printLotStockInLabelsByStoreIdsV1(
    printerId: Long,
    storeIds: List<String>,
    printQty: Int = 1,
    fromIndex: Int? = null,
    toIndex: Int? = null
): PrintProgressResult {

    if (storeIds.isEmpty()) {
        return PrintProgressResult(
            success = false,
            lastIndex = -1,
            totalLots = 0,
            errorMessage = "storeIds is empty"
        )
    }

    // 1. 查出对应 store_id 的 lot line
    val allInventoryLotLines = inventoryLotLineRepository
        .findAllByStoreIdsAndHasStockInLine(storeIds)

    if (allInventoryLotLines.isEmpty()) {
        return PrintProgressResult(
            success = false,
            lastIndex = -1,
            totalLots = 0,
            errorMessage = "no lots found for given storeIds"
        )
    }

    val effectiveTotal = allInventoryLotLines.size

    // 2. 计算要打印的 index 范围
    val startIndex = (fromIndex ?: 0).coerceAtLeast(0)
    val endIndex = (toIndex ?: (effectiveTotal - 1)).coerceAtMost(effectiveTotal - 1)

    if (startIndex > endIndex || startIndex >= effectiveTotal) {
        return PrintProgressResult(
            success = false,
            lastIndex = startIndex - 1,
            totalLots = effectiveTotal,
            errorMessage = "invalid index range"
        )
    }

    var lastIndex = startIndex - 1

    // 3. 逐张打印，遇到打印机错误就立即返回，支持断点续打
    for (globalIndex in startIndex..endIndex) {
        val inventoryLotLine = allInventoryLotLines[globalIndex]
        val stockInLineId = inventoryLotLine.inventoryLot?.stockInLine?.id
            ?: return PrintProgressResult(
                success = false,
                lastIndex = lastIndex,
                totalLots = effectiveTotal,
                errorMessage = "Stock in line missing for inventoryLotLineId=${inventoryLotLine.id}"
            )

        try {
            stockInLineService.printQrCode(
                PrintQrCodeForSilRequest(
                    stockInLineId = stockInLineId,
                    printerId = printerId,
                    printQty = printQty
                )
            )
            lastIndex = globalIndex
        } catch (e: Exception) {
            val msg = when {
                e.message?.contains("paper", ignoreCase = true) == true ->
                    "Printer error (maybe out of paper): ${e.message}"
                else ->
                    "Printer error: ${e.message}"
            }

            return PrintProgressResult(
                success = false,
                lastIndex = lastIndex,
                totalLots = effectiveTotal,
                errorMessage = msg
            )
        }
    }

    // 全部打印成功
    return PrintProgressResult(
        success = true,
        lastIndex = lastIndex,
        totalLots = effectiveTotal,
        errorMessage = null
    )
}
}