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

import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.web.model.StockAdjustmentRequest
import com.ffii.fpsms.modules.stock.web.model.StockInRequest
import com.ffii.fpsms.modules.stock.web.model.StockOutRequest
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.math.BigDecimal
import java.time.LocalDate

import com.ffii.fpsms.modules.stock.entity.StockAdjustmentRecord
import com.ffii.fpsms.modules.stock.entity.StockAdjustmentRecordRepository
import com.ffii.fpsms.modules.stock.entity.StockInLine
import com.ffii.fpsms.modules.stock.entity.StockOutLine

@Service
open class StockAdjustmentService(
    private val stockInLineService: StockInLineService,
    private val stockOutLineService: StockOutLineService,
    private val inventoryLotLineRepository: InventoryLotLineRepository,
    private val stockAdjustmentRecordRepository: StockAdjustmentRecordRepository
) {

    @Transactional
    open fun submit(request: StockAdjustmentRequest): MessageResponse {
        val originalById = request.originalLines.filter { it.id > 0 }.associateBy { it.id }
        val currentIds = request.currentLines.map { it.id }.toSet()

        // Branch 4: Removed entries — stock out full qty and mark unavailable
        val removed = request.originalLines.filter { it.id > 0 && it.id !in currentIds }
        for (line in removed) {
            val stockOutLine = stockOutLineService.createStockOutForRemoval(
                inventoryLotLineId = line.id,
                type = "ADJ"
            )
            saveAdjustmentRecordForStockOut(stockOutLine)
        }
        for (current in request.currentLines) {
            // Branch 2: New entry — createStockIn (OPEN or ADJ)
            if (current.isNew) {
                val stockType = if (current.isOpeningInventory) "OPEN" else "ADJ"
                val stockInLine = stockInLineService.createStockIn(
                    StockInRequest(
                        itemId = current.itemId,
                        itemNo = current.itemNo,
                        demandQty = null,
                        acceptedQty = current.adjustedQty,
                        expiryDate = LocalDate.parse(current.expiryDate),
                        lotNo = current.lotNo?.takeIf { it.isNotBlank() },
                        productLotNo = current.productlotNo?.takeIf { it.isNotBlank() },
                        dnNo = current.dnNo?.takeIf { it.isNotBlank() },
                        type = stockType,
                        warehouseId = current.warehouseId
                    )
                )
                saveAdjustmentRecordForStockIn(stockInLine)
                continue
            }

            // Branch 1 & 3: Existing line — compare qty
            val original = originalById[current.id] ?: continue
            val diff = current.adjustedQty.subtract(original.adjustedQty)
            if (diff.compareTo(BigDecimal.ZERO) == 0) continue // Branch 1: no change

            if (diff.compareTo(BigDecimal.ZERO) > 0) {
                // Branch 2 (qty up): increase inQty on the same lot line; new StockIn/StockInLine for audit only
                val inventoryLotLine = inventoryLotLineRepository.findById(current.id)
                    .orElseThrow { IllegalArgumentException("InventoryLotLine not found: ${current.id}") }
                val stockInRequest = buildStockInRequestFromExistingLotLine(inventoryLotLine, diff)
                val stockInLine = stockInLineService.createStockInForExistingInventoryLotLine(
                    stockInRequest,
                    inventoryLotLine
                )
                saveAdjustmentRecordForStockIn(stockInLine)
            } else {
                // Branch 3 (qty down): adjustment outbound only (not pick createStockOut)
                val stockOutLine = stockOutLineService.createStockOutForAdjustment(
                    StockOutRequest(
                        inventoryLotLineId = current.id,
                        qty = diff.abs().toDouble(),
                        type = "ADJ"
                    )
                )
                saveAdjustmentRecordForStockOut(stockOutLine)
            }
        }

        return MessageResponse(
            id = null,
            name = "Stock Adjustment",
            code = "STOCK_ADJUSTMENT_SUBMITTED",
            type = "success",
            message = "Stock adjustment completed successfully",
            errorPosition = null
        )
    }

    private fun buildStockInRequestFromExistingLotLine(
        inventoryLotLine: InventoryLotLine,
        acceptedQty: BigDecimal
    ): StockInRequest {
        val inventoryLot = inventoryLotLine.inventoryLot
            ?: throw IllegalArgumentException("InventoryLotLine must have an associated InventoryLot")
        val item = inventoryLot.item
            ?: throw IllegalArgumentException("InventoryLot must have an associated item")
        val itemId = item.id
            ?: throw IllegalArgumentException("Item must have an id")
        val itemCode = item.code
            ?: throw IllegalArgumentException("Item must have a code")
        val expiryDate = inventoryLot.expiryDate
            ?: throw IllegalArgumentException("InventoryLot must have an expiryDate")
        val lotNo = inventoryLot.lotNo
        val productLotNo = inventoryLot.stockInLine?.productLotNo
        val dnNo = inventoryLot.stockInLine?.dnNo
        val warehouseId = inventoryLotLine.warehouse?.id
            ?: throw IllegalArgumentException("InventoryLotLine must have a warehouse")

        return StockInRequest(
            itemId = itemId,
            itemNo = itemCode,
            demandQty = null,
            acceptedQty = acceptedQty,
            expiryDate = expiryDate,
            lotNo = lotNo,
            productLotNo = productLotNo,
            dnNo = dnNo,
            type = "ADJ",
            warehouseId = warehouseId
        )
    }
    private fun saveAdjustmentRecordForStockIn(stockInLine: StockInLine) {
        val item = stockInLine.item ?: return
        val record = StockAdjustmentRecord().apply {
            this.item = item
            this.itemCode = stockInLine.itemNo ?: item.code
            this.itemName = item.name
            this.lotNo = stockInLine.lotNo
            this.inQty = stockInLine.acceptedQty
            this.outQty = null
            this.stockInLine = stockInLine
            this.stockOutLine = null
        }
        stockAdjustmentRecordRepository.save(record)
    }

    private fun saveAdjustmentRecordForStockOut(stockOutLine: StockOutLine) {
        val item = stockOutLine.item ?: return
        val lotNo = stockOutLine.inventoryLotLine?.inventoryLot?.lotNo
        val record = StockAdjustmentRecord().apply {
            this.item = item
            this.itemCode = item.code
            this.itemName = item.name
            this.lotNo = lotNo
            this.inQty = null
            this.outQty = BigDecimal.valueOf(stockOutLine.qty ?: 0.0)
            this.stockInLine = null
            this.stockOutLine = stockOutLine
        }
        stockAdjustmentRecordRepository.save(record)
    }
}