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 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.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
) {

    @Transactional
    open fun createStockTransfer(request: CreateStockTransferRequest): MessageResponse {
        // Step 1: Stock Out - using generic function
        val stockOutLine = createStockOut(request)

        // Step 2: Stock In - using generic function
        val stockInLine = createStockIn(request)

        // Step 3: Create Stock Transfer Record
        val stockTransferRecord = createStockTransferRecord(request, stockOutLine, stockInLine)

        return MessageResponse(
            id = stockTransferRecord.id,
            name = "Stock Transfer Record",
            code = "STOCK_TRANSFER_CREATED",
            type = "success",
            message = "Stock transfer completed successfully",
            errorPosition = null
        )
    }

    private fun createStockOut(request: CreateStockTransferRequest): StockOutLine {

        val stockOutRequest = StockOutRequest(
            inventoryLotLineId = request.inventoryLotLineId,
            qty = request.transferredQty.toDouble(),
            type = "TRF"
        )

        return stockOutLineService.createStockOut(stockOutRequest)
    }

    private fun createStockIn(request: CreateStockTransferRequest): StockInLine {
        // 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")
        }

        // Step 4: Check if existing InventoryLotLine with same lot and warehouse exists
        val existingInventoryLotLine = inventoryLotLineRepository.findByLotNoAndItemIdAndWarehouseId(
            lotNo = lotNo,
            itemId = itemId,
            warehouseId = request.warehouseId
        )

        // Step 5: Map to StockInRequest
        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
        )

        return if (existingInventoryLotLine != null) {
            // Update existing InventoryLotLine's inQty
            existingInventoryLotLine.apply {
                val currentInQty = this.inQty ?: BigDecimal.ZERO
                this.inQty = currentInQty + request.transferredQty

                // Update status if it was UNAVAILABLE and now has stock
                if (this.status == InventoryLotLineStatus.UNAVAILABLE) {
                    this.status = InventoryLotLineStatus.AVAILABLE
                }
            }
            inventoryLotLineRepository.saveAndFlush(existingInventoryLotLine)

            // Create StockIn and StockInLine for record keeping, linking to existing InventoryLotLine
            stockInLineService.createStockInForExistingInventoryLotLine(stockInRequest, existingInventoryLotLine)
        } else {
            // Normal flow: create new InventoryLotLine
            stockInLineService.createStockIn(stockInRequest)
        }
    }

    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
    }
}