package com.ffii.fpsms.modules.dashboard.service

import com.ffii.fpsms.modules.dashboard.web.models.GoodsReceiptStatusResponse
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository
import com.ffii.fpsms.modules.qc.entity.QcResultRepository
import com.ffii.fpsms.modules.stock.entity.StockInLineRepository
import org.springframework.stereotype.Service
import java.time.LocalDate

@Service
class GoodsReceiptStatusService(
    private val purchaseOrderRepository: PurchaseOrderRepository,
    private val stockInLineRepository: StockInLineRepository,
    private val qcResultRepository: QcResultRepository
) {
    private data class Agg(
        val supplierId: Long?,
        val supplierName: String,
        var expectedNoOfDelivery: Int = 0,
        val ordersReceivedAtDock: MutableSet<Long> = mutableSetOf(),
        var itemsInspected: Int = 0,
        var itemsWithIqcIssue: Int = 0,
        var itemsCompletedPutAway: Int = 0
    )

    fun getGoodsReceiptStatus(date: LocalDate): List<GoodsReceiptStatusResponse> {
        val from = date.atStartOfDay()
        val to = date.plusDays(1).atStartOfDay()

        val purchaseOrders = purchaseOrderRepository.findAllByEstimatedArrivalDateRange(from, to)
        val stockInLines = stockInLineRepository.findByReceiptDateAndDeletedFalse(date)

        val stockInLineIds = stockInLines.mapNotNull { it.id }
        val inspectedLineIdSet = if (stockInLineIds.isEmpty()) {
            emptySet()
        } else {
            qcResultRepository
                .findDistinctStockInLineIdsByStockInLineIdInAndDeletedFalse(stockInLineIds)
                .toSet()
        }
        val failedLineIdSet = if (stockInLineIds.isEmpty()) {
            emptySet()
        } else {
            qcResultRepository
                .findDistinctFailedStockInLineIdsByStockInLineIdInAndDeletedFalse(stockInLineIds)
                .toSet()
        }

        val bySupplier = mutableMapOf<Long?, Agg>()

        fun upsertAgg(supplierId: Long?, supplierName: String): Agg {
            return bySupplier.getOrPut(supplierId) {
                Agg(supplierId = supplierId, supplierName = supplierName)
            }
        }

        // Expected deliveries (by PO estimated arrival date)
        purchaseOrders.forEach { po ->
            val supplier = po.supplier
            val supplierId = supplier?.id
            val supplierName = supplier?.name ?: "N/A"
            upsertAgg(supplierId, supplierName).expectedNoOfDelivery += 1
        }

        // Receiving / IQC / Put-away (by stock-in receipt date)
        stockInLines.forEach { sil ->
            val supplier = sil.purchaseOrder?.supplier
            val supplierId = supplier?.id
            val supplierName = supplier?.name ?: "N/A"
            val agg = upsertAgg(supplierId, supplierName)

            val silId = sil.id ?: return@forEach

            // Orders received at dock: count orders with DN + (supplier) lot no entered
            val poId = sil.purchaseOrder?.id
            if (poId != null && !sil.dnNo.isNullOrBlank() && !sil.productLotNo.isNullOrBlank()) {
                agg.ordersReceivedAtDock.add(poId)
            }

            // Items inspected: any IQC result recorded
            if (inspectedLineIdSet.contains(silId)) {
                agg.itemsInspected += 1
            }

            // Items with IQC issue: any failed IQC criteria
            if (failedLineIdSet.contains(silId)) {
                agg.itemsWithIqcIssue += 1
            }

            // Put-away completed at store: stock-in line completed
            if (sil.status.equals("completed", ignoreCase = true)) {
                agg.itemsCompletedPutAway += 1
            }
        }

        return bySupplier.values
            .map { agg ->
                GoodsReceiptStatusResponse(
                    supplierId = agg.supplierId,
                    supplierName = agg.supplierName,
                    expectedNoOfDelivery = agg.expectedNoOfDelivery,
                    noOfOrdersReceivedAtDock = agg.ordersReceivedAtDock.size,
                    noOfItemsInspected = agg.itemsInspected,
                    noOfItemsWithIqcIssue = agg.itemsWithIqcIssue,
                    noOfItemsCompletedPutAwayAtStore = agg.itemsCompletedPutAway
                )
            }
            .sortedBy { it.supplierName }
    }
}

