package com.ffii.fpsms.modules.purchaseOrder.service

import com.ffii.core.support.AbstractBaseEntityService
import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.m18.entity.M18DataLogRepository
import com.ffii.fpsms.modules.master.entity.ShopRepository
import com.ffii.fpsms.modules.master.service.CurrencyService
import com.ffii.fpsms.modules.master.service.ShopService
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLineRepository
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository
import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PoLineWithStockInLine
import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderInfo
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderRequest
import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderResponse
import com.ffii.fpsms.modules.stock.entity.StockInLine
import com.ffii.fpsms.modules.stock.entity.StockInLineRepository
import com.ffii.fpsms.modules.stock.entity.StockInRepository
import com.ffii.fpsms.modules.stock.web.model.StockInLineStatus
import org.springframework.stereotype.Service
import java.util.HashMap
import java.util.Objects
import kotlin.jvm.optionals.getOrDefault
import kotlin.jvm.optionals.getOrNull

@Service
open class PurchaseOrderService(
    private val jdbcDao: JdbcDao,
    private val purchaseOrderRepository: PurchaseOrderRepository,
    private val purchaseOrderLineRepository: PurchaseOrderLineRepository,
    private val polRepository: PurchaseOrderLineRepository,
    private val shopRepository: ShopRepository,
    private val m18DataLogRepository: M18DataLogRepository,
    private val stockInRepository: StockInRepository,
    private val stockInLineRepository: StockInLineRepository,
    private val currencyService: CurrencyService,
    private val shopService: ShopService,
) : AbstractBaseEntityService<PurchaseOrder, Long, PurchaseOrderRepository>(jdbcDao, purchaseOrderRepository) {
    open fun getPoList(): List<PurchaseOrderInfo> {
        return purchaseOrderRepository.findPurchaseOrderInfoByDeletedIsFalse()
    }

    open fun allPurchaseOrder(): List<PurchaseOrder> {
        return purchaseOrderRepository.findAll()
    }

    open fun getDetailedPo(id: Long): Map<String, Any> {
        // REMINDER: po code have duplication
        val po = purchaseOrderRepository.findPurchaseOrderInfoByIdAndDeletedIsFalse(id)
        val pol = polRepository.findAllByPurchaseOrderIdAndDeletedIsFalse(id)
//        val pol = polRepository.findAllPurchaseOrderLineInfoByPurchaseOrderIdAndDeletedIsFalse(id)
        val stockIn = stockInRepository.findByPurchaseOrderIdAndDeletedFalse(id)
        val stockInLine = if (stockIn == null) listOf() else {
            stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockIn.id!!)
        }
        val mappedPoLine = pol.map { thisPol ->
            val inLine = stockInLine.filter { it.purchaseOrderLineId == thisPol.id }
            PoLineWithStockInLine(
                thisPol.id!!,
                thisPol.purchaseOrder!!.id!!,
                thisPol.item!!.id!!,
                thisPol.itemNo!!,
                thisPol.item!!.name,
                thisPol.qty!!,
                inLine.filter{ it.status == StockInLineStatus.COMPLETE.status}.sumOf { it.acceptedQty },
                thisPol.uom!!.code,
                thisPol.price!!,
                thisPol.status!!.toString(),
                inLine
            )
        }
        val result = mapOf(
            "id" to po.id,
            "code" to po.code,
            "orderDate" to po.orderDate,
            "estimatedArrivalDate" to po.estimatedArrivalDate,
            "completeDate" to po.completeDate,
            "status" to po.status,
            "pol" to mappedPoLine
        )
        return result
    }

    open fun findByM18DataLogId(m18DataLogId: Long): PurchaseOrder? {
        return purchaseOrderRepository.findTopByM18DataLogIdAndDeletedIsFalseOrderByModifiedDesc(m18DataLogId)
    }

    open fun savePurchaseOrder(request: SavePurchaseOrderRequest): SavePurchaseOrderResponse {
        val purchaseOrder =
            request.id?.let { purchaseOrderRepository.findById(it).getOrDefault(PurchaseOrder()) } ?: PurchaseOrder()
        val supplier = request.m18SupplierId?.let { shopService.findVendorByM18Id(it) }
            ?: request.supplierId?.let { shopService.findById(it) }
        val shop = request.m18ShopId?.let { shopService.findShopByM18Id(it) }
            ?: request.shopId?.let { shopService.findById(it) }
        val currency = request.m18CurrencyId?.let { currencyService.findByM18Id(it) }
            ?: request.currencyId?.let { currencyService.findById(it) }
        val status = request.status?.let { status -> PurchaseOrderStatus.entries.find { it.value == status } }
        val type = request.type?.let { type -> PurchaseOrderType.entries.find { it.value == type } }
        val m18DataLog = request.m18DataLogId?.let { m18DataLogRepository.findById(it).getOrNull() }

        //Need check duplicate?
        purchaseOrder.apply {
            code = request.code
            this.supplier = supplier
            this.shop = shop
            this.currency = currency
            orderDate = request.orderDate
            estimatedArrivalDate = request.estimatedArrivalDate
            completeDate = request.completeDate
            this.status = status
            this.type = type
            this.m18DataLog = m18DataLog
        }

        val savedPurchaseOrder = purchaseOrderRepository.saveAndFlush(purchaseOrder).let {
            SavePurchaseOrderResponse(
                id = it.id,
                code = it.code,
                supplierCode = it.supplier?.code,
                shopCode = it.shop?.code,
                currencyCode = it.currency?.code,
                orderDate = it.orderDate,
                estimatedArrivalDate = it.estimatedArrivalDate,
                completeDate = it.completeDate,
                status = it.status?.value
            )
        }

        return savedPurchaseOrder
    }


}