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

import com.ffii.core.support.AbstractBaseEntityService
import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.master.entity.ItemUomRespository
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus
import com.ffii.fpsms.modules.stock.entity.*
import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo
import com.ffii.fpsms.modules.stock.web.model.*
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.io.IOException
import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus

@Service
open class StockOutLineService(
    private val jdbcDao: JdbcDao,
    private val pickOrderLineRepository: PickOrderLineRepository,
    private val stockOutRepository: StockOutRepository,
    private val stockOutLineRepository: StockOutLIneRepository,
    private val itemRepository: ItemsRepository,
    private val inventoryRepository: InventoryRepository,
    private val itemUomRespository: ItemUomRespository,
    private val inventoryLotLineRepository: InventoryLotLineRepository,
    private val suggestedPickLotService: SuggestedPickLotService
    ): AbstractBaseEntityService<StockOutLine, Long, StockOutLIneRepository>(jdbcDao, stockOutLineRepository)  {
    @Throws(IOException::class)
    @Transactional
    open fun findAllByStockOutId(stockOutId: Long): List<StockOutLine> {
        return stockOutLineRepository.findAllByStockOutIdAndDeletedFalse(stockOutId)
    }
    @Throws(IOException::class)
    @Transactional
    open fun deleteById(id: Long): String {
        val deleteStockOutline = stockOutLineRepository.findById(id).orElseThrow().apply {
            deleted = true
        }
        val updateItem = stockOutLineRepository.save(deleteStockOutline)
        val response = "mark deleted item: ${updateItem.id}"
        return response
    }
    @Throws(IOException::class)
    @Transactional
    open fun deleteByStockOutId(id: Long): String {
        val deleteStockOutline = stockOutLineRepository.findAllByStockOutIdAndDeletedFalse(id).map {sol ->
            sol.apply {
                deleted = true
            }
        }
        val updateItemIds = stockOutLineRepository.saveAllAndFlush(deleteStockOutline).map { it.id }
        val response = "mark deleted items: $updateItemIds"
        return response
    }

    @Transactional
    open fun getAllStockOutLineByPickOrderLineId(pickOrderLineId: Long): List<StockOutLineInfo> {
        return stockOutLineRepository.findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId)
    }

    @Transactional
    open fun create(request: CreateStockOutLineRequest): MessageResponse {
        // pick flow step 1
        println("=== DEBUG: create StockOutLine ===")
        println("Request consoCode: ${request.consoCode}")
        println("Request pickOrderLineId: ${request.pickOrderLineId}")
        println("Request inventoryLotLineId: ${request.inventoryLotLineId}")
        println("Request qty: ${request.qty}")
//        println(request.pickOrderLineId)
val existingStockOutLine = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
            request.pickOrderLineId, 
            request.inventoryLotLineId
        )
        
        if (existingStockOutLine.isNotEmpty()) {
            // ✅ 如果已存在，返回 null 表示不需要创建
            return MessageResponse(
                id = null,
                name = "Stock out line already exists",
                code = "EXISTS",
                type = "stock_out_line",
                message = "Stock out line already exists for this pick order line and inventory lot line",
                errorPosition = null,
                entity = null,
            )
        }
        val allStockOuts = stockOutRepository.findAll()
        println("=== DEBUG: All StockOut records ===")
        allStockOuts.forEach { stockOut ->
            println("StockOut ID: ${stockOut.id}, consoPickOrderCode: ${stockOut.consoPickOrderCode}")
        }
        val stockOut = stockOutRepository.findByConsoPickOrderCode(request.consoCode).orElseThrow()
        println("Found stockOut: ${stockOut.id} with consoCode: ${stockOut.consoPickOrderCode}")
        val pickOrderLine = pickOrderLineRepository.saveAndFlush(
            pickOrderLineRepository.findById(request.pickOrderLineId).orElseThrow()
                .apply {
                    this.status = PickOrderLineStatus.PICKING
                }
        )
        val item = itemRepository.findById(pickOrderLine.item!!.id!!).orElseThrow()
        val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow()
        val stockOutLine = StockOutLine()
            .apply {
                this.item = item
                this.qty = request.qty
                this.stockOut = stockOut
                this.inventoryLotLine = inventoryLotLine
                this.pickOrderLine = pickOrderLine
                this.status = StockOutLineStatus.PENDING.status
            }
        val savedStockOutLine = saveAndFlush(stockOutLine)
        val mappedSavedStockOutLine = stockOutLineRepository.findStockOutLineInfoById(savedStockOutLine.id!!)
//        println("triggering")
        return MessageResponse(
            id = savedStockOutLine.id,
            name = savedStockOutLine.inventoryLotLine!!.inventoryLot!!.lotNo,
            code = savedStockOutLine.stockOut!!.consoPickOrderCode,
            type = savedStockOutLine.status,
            message = "success",
            errorPosition = null,
            entity = mappedSavedStockOutLine,
        )
    }
    @Transactional
fun handleQc(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): List<StockOutLine?> {
    var newStockOutLine: StockOutLine? = null
    if (request.qty < stockOutLine.qty!!) {
        newStockOutLine = StockOutLine().apply {
            this.pickOrderLine = stockOutLine.pickOrderLine
            this.stockOut = stockOutLine.stockOut
            this.item = stockOutLine.item
            this.qty = stockOutLine.qty!! - request.qty
            this.status = StockOutLineStatus.DETERMINE1.status // escalated
        }
    }
    val inventoryLotLine = if (request.inventoryLotLineId != null)
        inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow()
    else null
    stockOutLine.apply {
        this.inventoryLotLine = inventoryLotLine ?: stockOutLine.inventoryLotLine
        this.qty = request.qty
        this.status = StockOutLineStatus.COMPLETE.status // complete
    }
    // update inventory lot line
    val zero = BigDecimal.ZERO
    val one = BigDecimal.ONE
    val targetLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId!!).orElseThrow()
    val salesUnit = inventoryLotLine?.inventoryLot?.item?.id?.let {_itemId -> itemUomRespository.findByItemIdAndSalesUnitIsTrueAndDeletedIsFalse(_itemId) }
    val ratio = 1.0 //(salesUnit?.ratioN ?: zero).divide(salesUnit?.ratioD ?: one).toDouble()
    val targetLotLineEntry = targetLotLine.apply {

        this.outQty = (this.outQty?: BigDecimal.ZERO) + (request.qty.div(ratio)).toBigDecimal()
        this.holdQty = this.holdQty!!.minus((request.qty.div(ratio)).toBigDecimal())
    }
    inventoryLotLineRepository.save(targetLotLineEntry)
    // update inventory
    val inventory = inventoryRepository.findByItemId(request.itemId).orElseThrow()
    val inventoryEntry = inventory.apply {
        this.onHandQty = this.onHandQty!!.minus((request.qty.div(ratio)).toBigDecimal())
        this.onHoldQty = this.onHoldQty!!.minus((request.qty.div(ratio)).toBigDecimal())
    }
    inventoryRepository.save(inventoryEntry)
    return listOf(stockOutLine, newStockOutLine)
}
    @Transactional
open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): MessageResponse {
    try {
        // ✅ Get stockOutId from pickOrderLineId with detailed error
        val stockOutId = getStockOutIdFromPickOrderLine(request.pickOrderLineId)
        println("Found stockOutId: $stockOutId for pickOrderLineId: ${request.pickOrderLineId}")
        
        val stockOut = stockOutRepository.findById(stockOutId).orElseThrow {
            IllegalArgumentException("StockOut not found with ID: $stockOutId")
        }
        println("Found stockOut: ${stockOut.id} with consoCode: ${stockOut.consoPickOrderCode}")
        
        val pickOrderLine = pickOrderLineRepository.findById(request.pickOrderLineId).orElseThrow {
            IllegalArgumentException("PickOrderLine not found with ID: ${request.pickOrderLineId}")
        }
        println("Found pickOrderLine: ${pickOrderLine.id}")
        
        val updatedPickOrderLine = pickOrderLineRepository.saveAndFlush(
            pickOrderLine.apply {
                this.status = PickOrderLineStatus.PICKING
            }
        )
        println("Updated pickOrderLine status to: ${updatedPickOrderLine.status}")
        
        val item = itemRepository.findById(updatedPickOrderLine.item!!.id!!).orElseThrow {
            IllegalArgumentException("Item not found with ID: ${updatedPickOrderLine.item!!.id}")
        }
        println("Found item: ${item.id} - ${item.code}")
        
        val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow {
            IllegalArgumentException("InventoryLotLine not found with ID: ${request.inventoryLotLineId}")
        }
        println("Found inventoryLotLine: ${inventoryLotLine.id}")
        
        val stockOutLine = StockOutLine()
            .apply {
                this.item = item
                this.qty = request.qty
                this.stockOut = stockOut
                this.inventoryLotLine = inventoryLotLine
                this.pickOrderLine = updatedPickOrderLine
                this.status = StockOutLineStatus.PENDING.status
            }
        println("Created stockOutLine with qty: ${request.qty}")
        
        val savedStockOutLine = saveAndFlush(stockOutLine)
        println("Saved stockOutLine with ID: ${savedStockOutLine.id}")
        
        val mappedSavedStockOutLine = stockOutLineRepository.findStockOutLineInfoById(savedStockOutLine.id!!)
        println("Mapped stockOutLine info: $mappedSavedStockOutLine")
        
        return MessageResponse(
            id = savedStockOutLine.id,
            name = savedStockOutLine.inventoryLotLine!!.inventoryLot!!.lotNo,
            code = savedStockOutLine.stockOut!!.consoPickOrderCode,
            type = savedStockOutLine.status,
            message = "success",
            errorPosition = null,
            entity = mappedSavedStockOutLine,
        )
    } catch (e: Exception) {
        println("Error in createWithoutConso: ${e.message}")
        e.printStackTrace()
        throw e
    }
}

// ✅ Update helper method with detailed error messages
private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long {
    println("Getting stockOutId for pickOrderLineId: $pickOrderLineId")
    
    // ✅ Fixed: Use poId instead of pick_order_id
    val sql = """
        SELECT so.id as stockOutId, so.consoPickOrderCode, po.consoCode
        FROM stock_out so
        JOIN pick_order po ON po.consoCode = so.consoPickOrderCode
        JOIN pick_order_line pol ON pol.poId = po.id  -- ✅ Fixed: Use poId
        WHERE pol.id = :pickOrderLineId
    """.trimIndent()
    
    val result = jdbcDao.queryForList(sql, mapOf("pickOrderLineId" to pickOrderLineId))
    println("SQL result: $result")
    
    if (result.isEmpty()) {
        throw IllegalArgumentException("No StockOut found for pickOrderLineId: $pickOrderLineId. Check if pick order line exists and has associated stock out.")
    }
    
    val stockOutId = result[0]["stockOutId"] as? Long
    val consoPickOrderCode = result[0]["consoPickOrderCode"] as? String
    val consoCode = result[0]["consoCode"] as? String
    
    println("Found stockOutId: $stockOutId, consoPickOrderCode: $consoPickOrderCode, consoCode: $consoCode")
    
    if (stockOutId == null) {
        throw IllegalArgumentException("StockOut ID is null for pickOrderLineId: $pickOrderLineId. ConsoCode: $consoCode, ConsoPickOrderCode: $consoPickOrderCode")
    }
    
    return stockOutId
}
    @Transactional
    fun handleLotChangeApprovalOrReject(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): List<StockOutLine?> {
        /**
         * @param request.qty
         *  qty that requires lot change
         */
        var newStockOutLine: StockOutLine? = null

        if (request.qty < stockOutLine.qty!!) {
            newStockOutLine = StockOutLine().apply {
                this.pickOrderLine = stockOutLine.pickOrderLine
                this.stockOut = stockOutLine.stockOut
                this.item = stockOutLine.item
                this.qty = stockOutLine.qty!! - request.qty
                this.status = StockOutLineStatus.REJECTED.status // original status
            }
        }
        stockOutLine.apply {
            this.qty = request.qty
            this.status = StockOutLineStatus.LOT_CHANGE_APPROVAL.status // require new lot no
        }
        return listOf(stockOutLine, newStockOutLine)
    }

//    @Transactional
//    fun assignNewLotLine(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): List<StockOutLine?> {
//        val newInventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow()
//        return listOf(stockOutLine.apply {
//            this.inventoryLotLine = newInventoryLotLine
//        })
//    }
    @Transactional
    fun checkIsStockOutLineCompleted(pickOrderLineId: Long) {
        val unfinishedLine = stockOutLineRepository
            .findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId)
            .filter {
                it.status != StockOutLineStatus.COMPLETE.status
                        &&  it.status != StockOutLineStatus.REJECTED.status
            }
        if (unfinishedLine.isEmpty()) {
            // set pick order line status to complete
            val pol = pickOrderLineRepository.findById(pickOrderLineId).orElseThrow()
            pickOrderLineRepository.save(
                pol.apply {
                    this.status = PickOrderLineStatus.COMPLETED
                }
            )
        } else {
            // return unfinished ids to frontend
            println(unfinishedLine.map {
                Pair(
                    it.id,
                    it.status
                )
            })
        }
    }
    @Transactional
    open fun update(request: UpdateStockOutLineRequest): MessageResponse {
        val stockOutLine = stockOutLineRepository.findById(request.id).orElseThrow()
        var stockOutLineEntries: List<StockOutLine?> = listOf()
        // pick flow step 2
        if (request.status == StockOutLineStatus.COMPLETE.status) { // doing qc
            //println("doing sth")
            if (request.qty <= 0) {
                return MessageResponse(
                    id = null,
                    code = null,
                    name = null,
                    type = "qty == 0",
                    message = "qty cannot be 0",
                    errorPosition = "request.qty",
                )
            }
            stockOutLineEntries = handleQc(stockOutLine, request)
        }
        if (request.status == StockOutLineStatus.LOT_CHANGE_APPROVAL.status) {
            if (request.qty < 0) {
                return MessageResponse(
                    id = null,
                    code = null,
                    name = null,
                    type = "qty < 0",
                    message = "qty cannot be smaller than 0",
                    errorPosition = "request.qty",
                )
            }
            stockOutLineEntries = handleLotChangeApprovalOrReject(stockOutLine, request)
        }

        val savedEntries = stockOutLineRepository.saveAllAndFlush(stockOutLineEntries.filterNotNull())
        val lineInfoList = stockOutLineRepository.findAllByIdIn(savedEntries.map { it.id!! })

        checkIsStockOutLineCompleted(pickOrderLineId = stockOutLine.pickOrderLine!!.id!!)

        return MessageResponse(
            id = 0,
            name = "id: ${lineInfoList.map { it.id }.joinToString(separator = ",")}",
            code = stockOutLine.stockOut!!.consoPickOrderCode,
            type = lineInfoList.joinToString(separator = ",") { it.status },
            message = "success",
            errorPosition = null,
            entity = lineInfoList,
        )
    }

    @Transactional
open fun updateStatus(request: UpdateStockOutLineStatusRequest): MessageResponse {
    try {
        val stockOutLine = stockOutLineRepository.findById(request.id).orElseThrow {
            IllegalArgumentException("StockOutLine not found with ID: ${request.id}")
        }
        
        println("Updating StockOutLine ID: ${request.id}")
        println("Current status: ${stockOutLine.status}")
        println("New status: ${request.status}")
        
        // Update status
        stockOutLine.status = request.status
        
        // Update quantity if provided
        if (request.qty != null) {
            val currentQty = stockOutLine.qty?.toDouble() ?: 0.0
            val newQty = currentQty + request.qty
            stockOutLine.qty = (newQty)
        }

        val savedStockOutLine = stockOutLineRepository.saveAndFlush(stockOutLine)
        println("Updated StockOutLine: ${savedStockOutLine.id} with status: ${savedStockOutLine.status}")
        
        // ✅ FIX: Only call once and add debugging
        if (request.status == "rejected" || request.status == "REJECTED") {
            println("=== TRIGGERING LOT REJECTION LOGIC ===")
            handleLotRejectionFromStockOutLine(savedStockOutLine)
        }
        
        val mappedSavedStockOutLine = stockOutLineRepository.findStockOutLineInfoById(savedStockOutLine.id!!)
        
        return MessageResponse(
            id = savedStockOutLine.id,
            name = savedStockOutLine.inventoryLotLine!!.inventoryLot!!.lotNo,
            code = savedStockOutLine.stockOut!!.consoPickOrderCode,
            type = savedStockOutLine.status,
            message = "Stock out line status updated successfully",
            errorPosition = null,
            entity = mappedSavedStockOutLine,
        )
    } catch (e: Exception) {
        println("Error updating stock out line status: ${e.message}")
        e.printStackTrace()
        throw e
    }
}
   // ✅ ADD THIS: Handle lot rejection when stock out line is rejected
   private fun handleLotRejectionFromStockOutLine(stockOutLine: StockOutLine) {
    try {
        println("=== HANDLING LOT REJECTION FROM STOCK OUT LINE ===")
        println("StockOutLine ID: ${stockOutLine.id}")
        println("StockOutLine Status: ${stockOutLine.status}")
        
        val inventoryLotLine = stockOutLine.inventoryLotLine
        if (inventoryLotLine != null) {
            println("InventoryLotLine ID: ${inventoryLotLine.id}")
            println("Current lot status: ${inventoryLotLine.status}")
            println("Current holdQty: ${inventoryLotLine.holdQty}")
            
            // Step 2: Update inventory lot line
            inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE
            inventoryLotLine.holdQty = BigDecimal.ZERO
            
            println("Setting lot status to UNAVAILABLE and holdQty to 0")
            val savedLotLine = inventoryLotLineRepository.save(inventoryLotLine)
            println("Saved lot line: ${savedLotLine.id}, status: ${savedLotLine.status}, holdQty: ${savedLotLine.holdQty}")
            
            // Step 3: Update inventory table
            println("Updating inventory table...")
            updateInventoryTableAfterLotRejection(inventoryLotLine)
            
            // Step 4: Trigger resuggest
            val pickOrderLine = stockOutLine.pickOrderLine
            if (pickOrderLine?.pickOrder?.id != null) {
                println("Triggering resuggest for pick order ID: ${pickOrderLine.pickOrder!!.id}")
                suggestedPickLotService.resuggestPickOrder(pickOrderLine.pickOrder!!.id!!)
                println("Resuggest completed")
            } else {
                println("No pick order found for resuggest")
            }
        } else {
            println("No inventory lot line found in stock out line")
        }
    } catch (e: Exception) {
        println("Error handling lot rejection from stock out line: ${e.message}")
        e.printStackTrace()
    }
}

// ✅ ADD THIS: Update inventory table after lot rejection
private fun updateInventoryTableAfterLotRejection(inventoryLotLine: InventoryLotLine) {
    try {
        println("=== UPDATING INVENTORY TABLE ===")
        val itemId = inventoryLotLine.inventoryLot?.item?.id
        println("Item ID: $itemId")
        
        if (itemId != null) {
            // Calculate onHoldQty (sum of holdQty from available lots only)
            val onHoldQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE.value)
                .sumOf { it.holdQty ?: BigDecimal.ZERO }
            
            // Calculate unavailableQty (sum of inQty from unavailable lots only)
            val unavailableQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId,InventoryLotLineStatus.UNAVAILABLE.value)
            .sumOf { 
                val inQty = it.inQty ?: BigDecimal.ZERO
                val outQty = it.outQty ?: BigDecimal.ZERO
                val remainingQty = inQty.minus(outQty)
                remainingQty
            }
            
            println("Calculated onHoldQty: $onHoldQty")
            println("Calculated unavailableQty: $unavailableQty")
            
            // Update the inventory table
            val inventory = inventoryRepository.findByItemId(itemId).orElse(null)
            if (inventory != null) {
                println("Found inventory record: ${inventory.id}")
                println("Current inventory - onHoldQty: ${inventory.onHoldQty}, unavailableQty: ${inventory.unavailableQty}")
                
                inventory.onHoldQty = onHoldQty
                inventory.unavailableQty = unavailableQty
                val savedInventory = inventoryRepository.save(inventory)
                
                println("Updated inventory - onHoldQty: ${savedInventory.onHoldQty}, unavailableQty: ${savedInventory.unavailableQty}")
            } else {
                println("No inventory record found for item $itemId")
            }
        }
    } catch (e: Exception) {
        println("Error updating inventory table after lot rejection: ${e.message}")
        e.printStackTrace()
    }
}
    }