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

@Service
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 logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::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 } // 添加此行
    
        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 {
        logger.info("--------- Start - Import Stock Take Excel -------");

        if (workbook == null) {
            logger.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)
        logger.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))
//                logger.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) {
                logger.error("Import Error (Warehouse Error): ${e.message}")
                null
            } ?: continue

            // Item
            val item = try {
//                logger.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) {
                logger.error("Import Error (Item Code Error): ${e.message}")
                null
            } ?: continue

            // Stock Take Line (First Create)
            val qty = try {
//                logger.info("Qty Type: ${row.getCell(COLUMN_QTY_INDEX).cellType}")
                row.getCell(COLUMN_QTY_INDEX).numericCellValue.toBigDecimal()
            } catch (e: Exception) {
                logger.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)
            logger.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)
        logger.info("--------- End - Import Stock Take Excel -------")
        return "Import Excel success";
    }




    fun createStockTakeForSections(): Map<String, String> {
        logger.info("--------- Start - Create Stock Take for Sections -------")
        
        val result = mutableMapOf<String, String>()
        
        // 1. 获取所有不同的 stockTakeSection（从 warehouse 表）
        val allWarehouses = warehouseRepository.findAllByDeletedIsFalse()
        val distinctSections = allWarehouses
            .mapNotNull { it.stockTakeSection }
            .distinct()
            .filter { !it.isBlank() }
        
        // 2. 获取所有 stock_take 记录（按 stockTakeSection 分组）
        val allStockTakes = stockTakeRepository.findAll()
            .filter { !it.deleted }
            .groupBy { it.stockTakeSection }
        
        // 3. 为每个 stockTakeSection 检查并创建
        distinctSections.forEach { section ->
            val stockTakesForSection = allStockTakes[section] ?: emptyList()
            
            // 检查：如果该 section 的所有记录都是 COMPLETED，才创建新的
            val allCompleted = stockTakesForSection.isEmpty() || 
                stockTakesForSection.all { it.status == StockTakeStatus.COMPLETED }
            
            if (allCompleted) {
                try {
                    val now = LocalDateTime.now()
                    val code = assignStockTakeNo()
                    
                    val saveStockTakeReq = SaveStockTakeRequest(
                        code = code,
                        planStart = now,
                        planEnd = now.plusDays(1),
                        actualStart = null,
                        actualEnd = null,
                        status = StockTakeStatus.PENDING.value,
                        remarks = null,
                        stockTakeSection = section
                    )
                    
                    val savedStockTake = saveStockTake(saveStockTakeReq)
                    result[section] = "Created: ${savedStockTake.code}"
                    logger.info("Created stock take for section $section: ${savedStockTake.code}")
                } catch (e: Exception) {
                    result[section] = "Error: ${e.message}"
                    logger.error("Error creating stock take for section $section: ${e.message}")
                }
            } else {
                result[section] = "Skipped: Has non-completed records"
                logger.info("Skipped section $section: Has non-completed records")
            }
        }
        
        // 移除 null section 处理逻辑，因为 warehouse 表中没有 null 的 stockTakeSection
        
        logger.info("--------- End - Create Stock Take for Sections -------")
        return result
    }
}