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

import com.ffii.core.utils.JwtTokenUtil
import com.ffii.fpsms.modules.common.CodeGenerator
import com.ffii.fpsms.modules.master.entity.WarehouseRepository
import com.ffii.fpsms.modules.master.service.ItemUomService
import com.ffii.fpsms.modules.master.service.ItemsService
import com.ffii.fpsms.modules.master.service.WarehouseService
import com.ffii.fpsms.modules.master.web.models.SaveWarehouseRequest
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.StockTake
import com.ffii.fpsms.modules.stock.entity.StockTakeRepository
import com.ffii.fpsms.modules.stock.enums.StockTakeLineStatus
import com.ffii.fpsms.modules.stock.enums.StockTakeStatus
import com.ffii.fpsms.modules.stock.web.model.*
import org.apache.poi.ss.usermodel.Cell
import org.apache.poi.ss.usermodel.CellType
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import java.math.BigDecimal
import java.time.LocalDateTime
import org.springframework.context.annotation.Lazy
import org.springframework.transaction.annotation.Transactional

@Service
open class StockTakeService(
    val stockTakeRepository: StockTakeRepository,
    val warehouseRepository: WarehouseRepository,
    val warehouseService: WarehouseService,
    val stockInService: StockInService,
    val stockInLineService: StockInLineService,
    @Lazy val itemsService: ItemsService,
    @Lazy val itemUomService: ItemUomService,
    val stockTakeLineService: StockTakeLineService,
    val inventoryLotLineRepository: InventoryLotLineRepository,
    val stockTakeRecordService: StockTakeRecordService,
) {
    companion object {
        private val log = LoggerFactory.getLogger(StockTakeService::class.java)
    }
    fun assignStockTakeNo(): String {
        val prefix = "ST"
        val midfix = CodeGenerator.DEFAULT_MIDFIX
        val latestCode = stockTakeRepository.findLatestCodeByPrefix("${prefix}-${midfix}")
        return CodeGenerator.generateNo(prefix = prefix, latestCode = latestCode)
    }

    fun saveStockTake(request: SaveStockTakeRequest): StockTake {
        val stockTake = request.id?.let { stockTakeRepository.findByIdAndDeletedIsFalse(it) } ?: StockTake();
        val status = request.status?.let { _status -> StockTakeStatus.entries.find { it.value == _status } }
    
        request.code?.let { stockTake.code = it }
        if (stockTake.code == null) {
            stockTake.code = assignStockTakeNo()
        }
        request.planStart?.let { stockTake.planStart = it }
        request.planEnd?.let { stockTake.planEnd = it }
        request.actualStart?.let { stockTake.actualStart = it }
        request.actualEnd?.let { stockTake.actualEnd = it }
        status?.let { stockTake.status = it }
        request.remarks?.let { stockTake.remarks = it }
        request.stockTakeSection?.let { stockTake.stockTakeSection = it } // 添加此行
        request.stockTakeRoundId?.let { stockTake.stockTakeRoundId = it }
        request.stockTakeRoundName?.let { stockTake.stockTakeRoundName = it }

        return stockTakeRepository.save(stockTake)
    }

    // ---------------------------------------------- Import Excel ---------------------------------------------- //
    fun getCellStringValue(cell: Cell): String {
        return cell.let {
            when (it.cellType) {
                CellType.STRING -> it.stringCellValue
                CellType.NUMERIC -> it.numericCellValue
                else -> ""
            }
        }.toString()
    }
    fun importExcel(workbook: Workbook?): String {
        log.info("--------- Start - Import Stock Take Excel -------");

        if (workbook == null) {
            log.error("No Excel Import");
            return "Import Excel failure";
        }

        val sheet: Sheet = workbook.getSheetAt(0);

        // Columns
        val COLUMN_ITEM_CODE_INDEX = 6;
        val COLUMN_FLOOR_INDEX = 11;
        val COLUMN_PLACE_INDEX = 12;
        val COLUMN_WAREHOSE_INDEX = 13;
        val COLUMN_ZONE_INDEX = 14;
        val COLUMN_SLOT_INDEX = 15;
        val COLUMN_QTY_INDEX = 18;

        val START_ROW_INDEX = 2;

        // Start Import
        val startTime = LocalDateTime.now();
        val stCode = assignStockTakeNo()
        val saveStockTakeReq = SaveStockTakeRequest(
            code = stCode,
            planStart = startTime,
            planEnd = startTime,
            actualStart = startTime,
            actualEnd = null,
            status = StockTakeStatus.PENDING.value,
            remarks = null
        )
        val savedStockTake = saveStockTake(saveStockTakeReq);

        val saveStockInReq = SaveStockInRequest(
            code = stCode,
            stockTakeId = savedStockTake.id
        )
        val savedStockIn = stockInService.create(saveStockInReq)
        log.info("Last row: ${sheet.lastRowNum}");
        for (i in START_ROW_INDEX ..< sheet.lastRowNum) {
            val row = sheet.getRow(i)

            // Warehouse
            val warehouse = try {
                val floor = getCellStringValue(row.getCell(COLUMN_FLOOR_INDEX))
                val place = getCellStringValue(row.getCell(COLUMN_PLACE_INDEX))
                val code = getCellStringValue(row.getCell(COLUMN_WAREHOSE_INDEX))
                val zone = getCellStringValue(row.getCell(COLUMN_ZONE_INDEX))
                val slot = getCellStringValue(row.getCell(COLUMN_SLOT_INDEX))
//                log.info("Warehouse code - zone - slot: ${row.getCell(COLUMN_WAREHOSE_INDEX).cellType} - ${row.getCell(COLUMN_ZONE_INDEX).cellType} - ${row.getCell(COLUMN_SLOT_INDEX).cellType}")

                val defaultCapacity = BigDecimal(10000)
                val warehouseCode = "$code-$zone-$slot"
                val warehouseName = "$floor-$place"
                val existingWarehouse = warehouseService.findByCode(warehouseCode)

                if (existingWarehouse != null) {
                    existingWarehouse
                } else {
                    val warehouseRequest = SaveWarehouseRequest(
                        code = warehouseCode,
                        name = warehouseName,
                        description = warehouseName,
                        capacity = defaultCapacity
                    )
                    warehouseService.saveWarehouse(warehouseRequest)
                }
            } catch (e: Exception) {
                log.error("Import Error (Warehouse Error): ${e.message}")
                null
            } ?: continue

            // Item
            val item = try {
//                log.info("Item Type: ${row.getCell(COLUMN_ITEM_CODE_INDEX).cellType}")
                val itemCode = row.getCell(COLUMN_ITEM_CODE_INDEX).stringCellValue;
                itemsService.findByCode(itemCode)
            } catch (e: Exception) {
                log.error("Import Error (Item Code Error): ${e.message}")
                null
            } ?: continue

            // Stock Take Line (First Create)
            val qty = try {
//                log.info("Qty Type: ${row.getCell(COLUMN_QTY_INDEX).cellType}")
                row.getCell(COLUMN_QTY_INDEX).numericCellValue.toBigDecimal()
            } catch (e: Exception) {
                log.error("Import Error (Qty Error): ${e.message}")
                null
            } ?: continue

            val uom = itemUomService.findStockUnitByItemId(item.id!!)?.uom
            val saveStockTakeLineReq = SaveStockTakeLineRequest(
                stockTakeId = savedStockTake.id,
                initialQty = qty,
                finalQty = qty,
                uomId = uom?.id,
                status = StockTakeLineStatus.PENDING.value,
                remarks = null,
            )
            val savedStockTakeLine = stockTakeLineService.saveStockTakeLine(saveStockTakeLineReq)

            // Stock In Line
            val expiryDate = LocalDateTime.now().plusMonths(6) // TODO: Add expiry date from excel
            val saveStockInLineReq = SaveStockInLineRequest(
                stockInId = savedStockIn.id,
                itemId = item.id!!,
                acceptedQty = qty,
                acceptQty = qty,
                expiryDate = expiryDate.toLocalDate(),
                warehouseId = warehouse.id,
                stockTakeLineId = savedStockTakeLine.id,
                qcAccept = true,
                status = StockInLineStatus.PENDING.status
            )

            val savedStockInLine = stockInLineService.create(saveStockInLineReq)
            saveStockInLineReq.apply {
                id = savedStockInLine.id
            }
            stockInLineService.update(saveStockInLineReq)

            val inventoryLotLines = SaveInventoryLotLineForSil(
                qty = qty,
                warehouseId = warehouse.id
            )
            saveStockInLineReq.apply {
                status = StockInLineStatus.RECEIVED.status
                this.inventoryLotLines = if (qty > BigDecimal.ZERO) mutableListOf(inventoryLotLines) else null
            }
            val finalStockInLine = stockInLineService.update(saveStockInLineReq)

            // Stock Take Line (Second Completed)
            val inventoryLotLine = inventoryLotLineRepository.findByInventoryLotStockInLineIdAndWarehouseId(inventoryLotStockInLineId =  finalStockInLine.id!!, warehouseId = warehouse.id!!)
            saveStockTakeLineReq.apply {
                id = savedStockTakeLine.id
                status = StockTakeLineStatus.COMPLETED.value
                completeDate = LocalDateTime.now()
                inventoryLotLineId = inventoryLotLine?.id
            }
            stockTakeLineService.saveStockTakeLine(saveStockTakeLineReq)
            log.info("[Stock Take]: Saved item '${item.name}' to warehouse '${warehouse.code}'")
        }

        // End of Import
        val endTime = LocalDateTime.now();
        saveStockTakeReq.apply {
            id = savedStockTake.id
            actualEnd = endTime
            status = StockTakeStatus.COMPLETED.value
        }
        saveStockTake(saveStockTakeReq)
        log.info("--------- End - Import Stock Take Excel -------")
        return "Import Excel success";
    }




    fun createStockTakeForSections(
        sectionsFilter: List<String>? = null,
        stockTakeRoundName: String? = null,
        planStart: LocalDateTime? = null,
    ): Map<String, String> {
        log.info("--------- Start - Create Stock Take for Sections -------")
        
        val result = mutableMapOf<String, String>()
        
        // 1. 获取所有不同的 stockTakeSection（从 warehouse 表）
        val allWarehouses = warehouseRepository.findAllByDeletedIsFalse()
        var distinctSections = allWarehouses
            .mapNotNull { it.stockTakeSection }
            .distinct()
            .filter { !it.isBlank() }

        if (sectionsFilter != null) {
            if (sectionsFilter.isEmpty()) {
                log.warn("Create stock take: empty section list in request")
                return mapOf("_" to "Error: No stock take sections selected")
            }
            val wanted = sectionsFilter.map { it.trim() }.filter { it.isNotBlank() }.toSet()
            distinctSections = distinctSections.filter { section ->
                wanted.any { w -> section.equals(w, ignoreCase = true) }
            }
            if (distinctSections.isEmpty()) {
                log.warn("Create stock take: no warehouse sections matched filter")
                return mapOf("_" to "Error: No matching warehouse stock take sections")
            }
        }

        // 移除 null section 处理逻辑，因为 warehouse 表中没有 null 的 stockTakeSection
        val batchPlanStart = planStart ?: LocalDateTime.now()
        val batchPlanEnd = batchPlanStart.plusDays(1)
        // 輪次序號：每批次共用同一個遞增 roundId，與各筆 stock_take 主鍵脫鉤（避免第二輪變成 4,4,4）
        val roundId = stockTakeRepository.findMaxStockTakeRoundId() + 1
        val roundName = stockTakeRoundName?.trim()?.takeIf { it.isNotBlank() }
        var placeholderStockTakerId: Long? = null
        distinctSections.forEach { section ->
            try {
                val code = assignStockTakeNo()
                val saveStockTakeReq = SaveStockTakeRequest(
                    code = code,
                    planStart = batchPlanStart,
                    planEnd = batchPlanEnd,
                    actualStart = null,
                    actualEnd = null,
                    status = StockTakeStatus.PENDING.value,
                    remarks = null,
                    stockTakeSection = section,
                    stockTakeRoundId = roundId,
                    stockTakeRoundName = roundName,
                )
                val savedStockTake = saveStockTake(saveStockTakeReq)
                if (placeholderStockTakerId == null) {
                    placeholderStockTakerId = stockTakeRecordService.resolvePlaceholderStockTakerId()
                }
                stockTakeRecordService.createPlaceholderStockTakeRecordsForStockTake(
                    savedStockTake,
                    placeholderStockTakerId!!
                )
                result[section] = "Created: ${savedStockTake.code}"
                log.info("Created stock take for section $section: ${savedStockTake.code}, roundId=$roundId")
            } catch (e: Exception) {
                result[section] = "Error: ${e.message}"
                log.error("Error creating stock take for section $section: ${e.message}")
            }
        }
        log.info("--------- End - Create Stock Take for Sections -------")
        return result
    }
}