package com.ffii.fpsms.modules.master.service

import com.ffii.fpsms.modules.master.entity.ItemUom
import com.ffii.fpsms.modules.master.entity.ItemUomRespository
import com.ffii.fpsms.modules.master.web.models.*
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.io.IOException
import java.math.BigDecimal
import java.math.RoundingMode
import kotlin.jvm.optionals.getOrNull
import org.slf4j.Logger
import org.slf4j.LoggerFactory

@Service
open class ItemUomService(
    val itemsService: ItemsService,
    val uomConversionService: UomConversionService,
    val itemUomRespository: ItemUomRespository,
    val currencyService: CurrencyService
) {
    val logger = org.slf4j.LoggerFactory.getLogger(this::class.java)

    open fun findAllByItemsId(itemId: Long): List<ItemUom> {
        return itemUomRespository.findAllByItemIdAndDeletedIsFalse(itemId);
    }

    open fun findById(id: Long): ItemUom? {
        return itemUomRespository.findByIdAndDeletedIsFalse(id);
    }

    open fun findByM18Id(m18Id: Long): ItemUom? {
        return itemUomRespository.findByM18IdAndDeletedIsFalse(m18Id);
    }

    open fun findPurchaseUnitByItemId(itemId: Long): ItemUom? {
        return itemUomRespository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(itemId)
    }

    open fun findSalesUnitByItemId(itemId: Long): ItemUom? {
        return itemUomRespository.findByItemIdAndSalesUnitIsTrueAndDeletedIsFalse(itemId)
    }
    open fun findStockUnitByItemId(itemId: Long): ItemUom? {
        return itemUomRespository.findByItemIdAndStockUnitIsTrueAndDeletedIsFalse(itemId)
    }

    open fun findFirstByItemIdAndUomId(itemId: Long, uomId: Long): ItemUom? {
        return itemUomRespository.findFirstByItemIdAndUomIdAndDeletedIsFalse(itemId, uomId)
    }

    open fun findByItemIdAndUomIdAndStockUnit(itemId: Long, uomId: Long, stockUnit: Boolean): ItemUom? {
        return itemUomRespository.findByItemIdAndUomIdAndStockUnitAndDeletedIsFalse(itemId, uomId, stockUnit)
    }

    open fun findPurchaseUnitByM18ItemId(m18ItemId: Long): ItemUom? {
        return itemUomRespository.findByItemM18IdAndPurchaseUnitIsTrueAndDeletedIsFalse(m18ItemId)
    }

    open fun findBaseUnitByItemId(itemId: Long): ItemUom? {
        return itemUomRespository.findByItemIdAndBaseUnitIsTrueAndDeletedIsFalse(itemId)
    }

    open fun findPickingUnitByItemId(itemId: Long): ItemUom? {
        return itemUomRespository.findByItemIdAndPickingUnitIsTrueAndDeletedIsFalse(itemId)
    }

    private fun findTargetItemUom(itemId: Long, targetUnit: String): ItemUom? {
        return when (targetUnit.lowercase()) {
            "baseunit", "base_unit" -> findBaseUnitByItemId(itemId)
            "stockunit", "stock_unit" -> findStockUnitByItemId(itemId)
            "purchaseunit", "purchase_unit" -> findPurchaseUnitByItemId(itemId)
            "salesunit", "sales_unit" -> findSalesUnitByItemId(itemId)
            "pickingunit", "picking_unit" -> findPickingUnitByItemId(itemId)
            else -> null
        }
    }

    open fun convertPurchaseQtyToStockQty(itemId: Long, purchaseQty: BigDecimal): BigDecimal {
        val purchaseUnit = findPurchaseUnitByItemId(itemId) ?: return purchaseQty;
        val stockUnit = findStockUnitByItemId(itemId) ?: return purchaseQty;
        val one = BigDecimal.ONE;

        val baseQty = purchaseQty.multiply(purchaseUnit.ratioN ?: one).divide(purchaseUnit.ratioD ?: one, 2, RoundingMode.UP)
        val stockQty = baseQty.multiply(stockUnit.ratioD ?: one).divide(stockUnit.ratioN ?: one, 2, RoundingMode.UP)

        return stockQty;
    }

    open fun convertQtyToStockQty(itemId: Long, uomId: Long, sourceQty: BigDecimal): BigDecimal {
        val itemUom = findFirstByItemIdAndUomId(itemId, uomId) ?: return sourceQty;

        val stockUnit = findStockUnitByItemId(itemId) ?: return BigDecimal.ZERO;
        val one = BigDecimal.ONE;

        val baseQty = sourceQty.multiply(itemUom?.ratioN ?: one).divide(itemUom?.ratioD ?: one, 2, RoundingMode.UP)
        val stockQty = baseQty.multiply(stockUnit.ratioD ?: one).divide(stockUnit.ratioN ?: one, 2, RoundingMode.UP)

        return stockQty;
    }

    // See if need to update the response
    open fun saveItemUom(request: ItemUomRequest): ItemUom {
        val itemUom = request.m18Id?.let { findByM18Id(it) } ?: request.id?.let { findById(it) } ?: ItemUom()

        //if (request.m18LastModifyDate == itemUom.m18LastModifyDate) {
        //    logger.info("Skipping update - m18LastModifyDate unchanged")
        //    return itemUom
        //}

        val item = request.itemId?.let { itemsService.find(it).getOrNull() }
        val uom = request.m18UomId?.let { uomConversionService.findByM18Id(it) } ?: request.uomId?.let { uomConversionService.find(it).getOrNull() }
        val currency = request.currencyId?.let { currencyService.findById(it) }

        itemUom.apply {
            this.uom = uom
            this.item = item
            this.currency = currency
            baseUnit = request.baseUnit
            stockUnit = request.stockUnit
            pickingUnit = request.pickingUnit
            salesUnit = request.salesUnit
            purchaseUnit = request.purchaseUnit
            price = request.price
            m18Id = request.m18Id
            m18LastModifyDate = request.m18LastModifyDate
            ratioD = request.ratioD
            ratioN = request.ratioN
            deleted = request.deleted  // ← EXPLICIT assignment (same as before)
        }

        val savedItemUom = itemUomRespository.saveAndFlush(itemUom)

        return savedItemUom
    }

    @Throws(IOException::class)
    @Transactional
    open fun deleteItemUoms(deleteIds: List<Long>) {
//        println(deleteIds)
        if (deleteIds.isNotEmpty()) {
            itemUomRespository.deleteAllByIdIn(deleteIds)
        }
    }


    open fun convertUomByItem(request: ConvertUomByItemRequest): ConvertUomByItemResponse {
        // Special case: Direct conversion from KG (784) to gram (4) or vice versa
        // This handles cases where ItemUom records might not exist
        if (request.uomId == 784L && request.targetUnit.lowercase() == "baseunit") {
            // Check if base unit is gram (id=4)
            val baseUnitItemUom = findBaseUnitByItemId(request.itemId)
            val baseUnitUomId = baseUnitItemUom?.uom?.id
            
            if (baseUnitUomId == 4L) {
                // Direct conversion: 1 KG = 1000 grams
                val convertedQty = request.qty.multiply(BigDecimal(1000))
                val gramUom = uomConversionService.findById(4L)
                    ?: throw IllegalArgumentException("UomConversion not found for id=4 (gram)")
                
                return ConvertUomByItemResponse(
                    newQty = convertedQty,
                    udfudesc = gramUom.udfudesc,
                    udfShortDesc = gramUom.udfShortDesc
                )
            }
        }
        if (request.uomId == 784L) {
            val targetItemUom = findTargetItemUom(request.itemId, request.targetUnit)
            val targetUomId = targetItemUom?.uom?.id
            
            if (targetUomId == 18L) {
                // Direct conversion: 1 KG = 1000 milliliters (assuming density = 1 g/ml for water-based products)
                // Note: This is a general conversion, actual conversion may vary by product density
                val convertedQty = request.qty.multiply(BigDecimal(1000))
                val milliliterUom = uomConversionService.findById(19L)
                    ?: throw IllegalArgumentException("UomConversion not found for id=19 (milliliter)")
                
                return ConvertUomByItemResponse(
                    newQty = convertedQty,
                    udfudesc = milliliterUom.udfudesc,
                    udfShortDesc = milliliterUom.udfShortDesc
                )
            }
        }
        // Find source ItemUom by itemId and uomId
        val sourceItemUom = itemUomRespository.findFirstByItemIdAndUomIdAndDeletedIsFalse(request.itemId, request.uomId)
            ?: throw IllegalArgumentException("Source ItemUom not found for itemId=${request.itemId}, uomId=${request.uomId}")
    
        // Find target ItemUom by itemId and targetUnit
        val targetItemUom = findTargetItemUom(request.itemId, request.targetUnit)
            ?: throw IllegalArgumentException("Target ItemUom not found for itemId=${request.itemId}, targetUnit=${request.targetUnit}")
    
        // Convert quantity using ratioN/ratioD via base unit
        val one = BigDecimal.ONE
        val sourceRatioN = sourceItemUom.ratioN ?: one
        val sourceRatioD = sourceItemUom.ratioD ?: one
        val targetRatioN = targetItemUom.ratioN ?: one
        val targetRatioD = targetItemUom.ratioD ?: one
    
        // Convert source qty to base: qty * ratioN / ratioD
        val baseQty = request.qty.multiply(sourceRatioN).divide(sourceRatioD, 2, RoundingMode.UP)
    
        // Convert base to target: baseQty * ratioD / ratioN
        val newQty = baseQty.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.UP)
    
        // Get UomConversion from target ItemUom
        val uomConversion = targetItemUom.uom
            ?: throw IllegalArgumentException("Target UomConversion not found for target ItemUom")
    
        return ConvertUomByItemResponse(
            newQty = newQty,
            udfudesc = uomConversion.udfudesc,
            udfShortDesc = uomConversion.udfShortDesc
        )
    }


    
}