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.web.models.MessageResponse
import com.ffii.fpsms.modules.stock.entity.StockInLine
import com.ffii.fpsms.modules.stock.entity.StockOutLine
import com.ffii.fpsms.modules.stock.entity.StockTransferRecord
import com.ffii.fpsms.modules.stock.entity.StockTransferRecordRepository
import com.ffii.fpsms.modules.stock.web.model.CreateStockTransferRequest
import com.ffii.fpsms.modules.stock.web.model.StockInRequest
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus


@Service
open class StockTransferRecordService(
    private val jdbcDao: JdbcDao,
    private val stockTransferRecordRepository: StockTransferRecordRepository,
    private val stockOutLineService: StockOutLineService,
    private val stockInLineService: StockInLineService,
    private val inventoryLotLineRepository: InventoryLotLineRepository
) : AbstractBaseEntityService<StockTransferRecord, Long, StockTransferRecordRepository>(
    jdbcDao,
    stockTransferRecordRepository
) {
    private enum class TransferStockInMode {
        MERGED_EXISTING_LOT,
        /** Merged into smallest-id AVAILABLE line among several with same expiry (same lot key at target). */
        MERGED_EXISTING_LOT_AMBIGUOUS,
        CREATED_NEW_LOT
    }

    @Transactional
    open fun createStockTransfer(request: CreateStockTransferRequest): MessageResponse {
        // Step 1: TRF outbound on source lot line (out-only; not pick/QR createStockOut)
        val stockOutLine = createSourceStockOutForTransfer(request)

        // Step 2: TRF inbound to target warehouse (merge or new lot line)
        val (stockInLine, stockInMode) = createStockIn(request)

        // Step 3: Persist transfer record linking in/out lines
        val stockTransferRecord = createStockTransferRecord(request, stockOutLine, stockInLine)

        return MessageResponse(
            id = stockTransferRecord.id,
            name = "Stock Transfer Record",
            code = when (stockInMode) {
                TransferStockInMode.MERGED_EXISTING_LOT -> "MERGED_EXISTING_LOT"
                TransferStockInMode.MERGED_EXISTING_LOT_AMBIGUOUS -> "MERGED_EXISTING_LOT_AMBIGUOUS"
                TransferStockInMode.CREATED_NEW_LOT -> "CREATED_NEW_LOT"
            },
            type = "success",
            message = when (stockInMode) {
                TransferStockInMode.MERGED_EXISTING_LOT ->
                    "Stock transfer completed successfully (merged into existing target lot)"
                TransferStockInMode.MERGED_EXISTING_LOT_AMBIGUOUS ->
                    "已併入較早建立的批次（同批號有多筆可用）"
                TransferStockInMode.CREATED_NEW_LOT ->
                    "Stock transfer completed successfully (created new target lot)"
            },
            errorPosition = null
        )
    }

    private fun createSourceStockOutForTransfer(request: CreateStockTransferRequest): StockOutLine {
        return stockOutLineService.createStockOutForStockTransfer(
            inventoryLotLineId = request.inventoryLotLineId,
            qty = request.transferredQty.toDouble(),
        )
    }

    private fun createStockIn(request: CreateStockTransferRequest): Pair<StockInLine, TransferStockInMode> {
        // Step 1: Get inventoryLotLine to extract item information
        val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId)
            .orElseThrow { IllegalArgumentException("InventoryLotLine not found with id: ${request.inventoryLotLineId}") }

        // Step 2: Extract itemId and itemCode from inventoryLotLine
        val inventoryLot = inventoryLotLine.inventoryLot
            ?: throw IllegalArgumentException("InventoryLotLine must have an associated InventoryLot")

        val item = inventoryLot.item
            ?: throw IllegalArgumentException("InventoryLotLine 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
            ?: throw IllegalArgumentException("InventoryLot must have a lotNo")

        val productLotNo = inventoryLot.stockInLine?.productLotNo
        val dnNo = inventoryLot.stockInLine?.dnNo

        // Step 3: Validate required fields
        if (request.warehouseId == null) {
            throw IllegalArgumentException("warehouseId is required for stock in")
        }

        val stockInRequest = StockInRequest(
            itemId = itemId,
            itemNo = itemCode,
            demandQty = request.transferredQty,
            acceptedQty = request.transferredQty,
            expiryDate = expiryDate,
            lotNo = lotNo,
            productLotNo = productLotNo,
            dnNo = dnNo,
            type = "TRF",
            warehouseId = request.warehouseId
        )

        val warehouseId = request.warehouseId
        val mergeCandidates = inventoryLotLineRepository.findAllByLotNoAndItemIdAndWarehouseIdAndStatus(
            lotNo = lotNo,
            itemId = itemId,
            warehouseId = warehouseId,
            status = InventoryLotLineStatus.AVAILABLE,
        ).filter { it.id != inventoryLotLine.id }

        // Same expiry as source required to merge; repository orders by ill.id ASC — first = earliest-created line
        val sameExpiryMergeable = mergeCandidates.filter {
            val targetExpiry = it.inventoryLot?.expiryDate
            targetExpiry != null && targetExpiry == expiryDate
        }

        return when {
            mergeCandidates.isEmpty() ->
                stockInLineService.createStockIn(stockInRequest) to TransferStockInMode.CREATED_NEW_LOT
            sameExpiryMergeable.isEmpty() ->
                stockInLineService.createStockIn(stockInRequest) to TransferStockInMode.CREATED_NEW_LOT
            sameExpiryMergeable.size == 1 ->
                stockInLineService.createStockInForExistingInventoryLotLine(
                    stockInRequest,
                    sameExpiryMergeable.single(),
                ) to TransferStockInMode.MERGED_EXISTING_LOT
            else -> {
                val targetLine = sameExpiryMergeable.first()
                stockInLineService.createStockInForExistingInventoryLotLine(
                    stockInRequest,
                    targetLine,
                ) to TransferStockInMode.MERGED_EXISTING_LOT_AMBIGUOUS
            }
        }
    }

    private fun createStockTransferRecord(
        request: CreateStockTransferRequest,
        stockOutLine: StockOutLine,
        stockInLine: StockInLine
    ): StockTransferRecord {
        // Get source location from stockOutLine's inventoryLotLine warehouse
        val startLocation = stockOutLine.inventoryLotLine?.warehouse?.code
            ?: stockOutLine.inventoryLotLine?.warehouse?.name

        // Get target location from stockInLine's inventoryLot warehouse
        val targetLocation = stockInLine.inventoryLotLine?.warehouse?.code
            ?: stockInLine.inventoryLotLine?.warehouse?.name

        // Get item information
        val item = stockInLine.item
            ?: throw IllegalArgumentException("StockInLine must have an associated item")

        // Create StockTransferRecord
        val stockTransferRecord = StockTransferRecord().apply {
            this.item = item
            this.itemCode = item.code
            this.itemName = item.name
            this.lotNo = stockInLine.lotNo
            this.transferredQty = request.transferredQty
            this.startLocation = startLocation
            this.targetLocation = targetLocation
            this.stockInLine = stockInLine
            this.stockOutLine = stockOutLine
        }
        val savedStockTransferRecord = stockTransferRecordRepository.save(stockTransferRecord)

        // Link back to StockInLine and StockOutLine
        stockInLine.stockTransferRecord = savedStockTransferRecord
        stockOutLine.stockTransferRecord = savedStockTransferRecord

        // Save the updated lines
        stockInLineService.saveAndFlush(stockInLine)
        stockOutLineService.saveAndFlush(stockOutLine)

        return savedStockTransferRecord
    }
}