package com.ffii.fpsms.modules.stock.trace

import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.stock.web.model.*
import org.springframework.stereotype.Component

@Component
open class TraceProductionLoader(
    private val jdbcDao: JdbcDao,
    private val sql: TraceSqlSupport,
    private val bomLoader: TraceBomLoader,
) {
    open fun loadProductionSteps(jobOrderId: Long): List<ItemLotTraceProductionStep> {
        val stepMaterials = bomLoader.loadStepMaterialsByJobOrder(jobOrderId)
        val query = """
            SELECT
                ppl.id AS processLineId,
                ppl.bomProcessId AS bomProcessId,
                COALESCE(ppl.name, '') AS stepName,
                COALESCE(ppl.description, '') AS description,
                COALESCE(e.code, '') AS equipmentCode,
                COALESCE(e.name, ppl.equipment_name, '') AS equipmentName,
                COALESCE(u.name, '') AS operatorName,
                ppl.startTime AS startTime,
                ppl.endTime AS endTime,
                COALESCE(ppl.outputFromProcessQty, 0) AS outputQty,
                COALESCE(ppl.scrapQty, 0) AS scrapQty,
                (
                    COALESCE(ppl.defectQty, 0)
                    + COALESCE(ppl.defectQty2, 0)
                    + COALESCE(ppl.defectQty3, 0)
                ) AS defectQty,
                COALESCE(ppl.status, '') AS status,
                ppl.seqNo AS seqNo
            FROM productprocess pp
            INNER JOIN productprocessline ppl ON ppl.productprocessid = pp.id AND ppl.deleted = 0
            LEFT JOIN equipment e ON ppl.equipmentId = e.id AND e.deleted = 0
            LEFT JOIN user u ON ppl.operatorId = u.id AND u.deleted = 0
            WHERE pp.jobOrderId = :jobOrderId AND pp.deleted = 0
            ORDER BY ppl.seqNo, ppl.id
        """.trimIndent()
        return jdbcDao.queryForList(query, mapOf("jobOrderId" to jobOrderId)).map { row ->
            val bomProcessId = sql.longVal(row["bomProcessId"])
            ItemLotTraceProductionStep(
                processLineId = sql.longVal(row["processLineId"]) ?: 0L,
                stepName = sql.str(row["stepName"]),
                description = sql.str(row["description"]),
                equipmentCode = sql.str(row["equipmentCode"]),
                equipmentName = sql.str(row["equipmentName"]),
                operatorName = sql.str(row["operatorName"]),
                startTime = sql.formatDateTime(row["startTime"]),
                endTime = sql.formatDateTime(row["endTime"]),
                outputQty = sql.decimal(row["outputQty"]),
                scrapQty = sql.decimal(row["scrapQty"]),
                defectQty = sql.decimal(row["defectQty"]),
                status = sql.str(row["status"]),
                seqNo = sql.longVal(row["seqNo"]),
                bomProcessId = bomProcessId,
                stepMaterials = bomProcessId?.let { stepMaterials[it].orEmpty() } ?: emptyList(),
            )
        }
    }

    open fun loadByproductLots(jobOrderId: Long, currentItemId: Long): List<ItemLotTraceByproductLot> {
        val query = """
            SELECT
                il.id AS inventoryLotId,
                sil.id AS stockInLineId,
                COALESCE(il.lotNo, '') AS lotNo,
                COALESCE(bp_it.code, '') AS itemCode,
                COALESCE(bp_it.name, '') AS itemName,
                COALESCE(sil.acceptedQty, 0) AS qty,
                COALESCE(uc.udfudesc, '') AS uom,
                COALESCE(ppl.name, '') AS processStepName,
                COALESCE(sil.created, sil.modified, sil.receiptDate) AS producedAt,
                COALESCE(jo.code, '') AS jobOrderCode,
                jo.id AS jobOrderId
            FROM job_order jo
            INNER JOIN stock_in_line sil ON sil.jobOrderId = jo.id AND sil.deleted = 0
            INNER JOIN inventory_lot il ON sil.inventoryLotId = il.id AND il.deleted = 0
            INNER JOIN items bp_it ON il.itemId = bp_it.id AND bp_it.deleted = 0
            LEFT JOIN item_uom iu ON bp_it.id = iu.itemId AND iu.stockUnit = 1
            LEFT JOIN uom_conversion uc ON iu.uomId = uc.id
            LEFT JOIN productprocess pp ON pp.jobOrderId = jo.id AND pp.deleted = 0
            LEFT JOIN productprocessline ppl ON ppl.productprocessid = pp.id
                AND ppl.deleted = 0
                AND ppl.byproductId = bp_it.id
            WHERE jo.id = :jobOrderId
              AND jo.deleted = 0
              AND il.itemId <> :currentItemId
              AND EXISTS (
                  SELECT 1
                  FROM productprocess pp2
                  INNER JOIN productprocessline ppl2 ON ppl2.productprocessid = pp2.id AND ppl2.deleted = 0
                  WHERE pp2.jobOrderId = jo.id
                    AND pp2.deleted = 0
                    AND ppl2.byproductId = il.itemId
              )
            ORDER BY producedAt, il.lotNo
        """.trimIndent()
        return jdbcDao.queryForList(
            query,
            mapOf("jobOrderId" to jobOrderId, "currentItemId" to currentItemId),
        ).map { row ->
            ItemLotTraceByproductLot(
                inventoryLotId = sql.longVal(row["inventoryLotId"]),
                stockInLineId = sql.longVal(row["stockInLineId"]),
                lotNo = sql.str(row["lotNo"]),
                itemCode = sql.str(row["itemCode"]),
                itemName = sql.str(row["itemName"]),
                qty = sql.decimal(row["qty"]),
                uom = sql.str(row["uom"]),
                processStepName = sql.str(row["processStepName"]),
                producedAt = sql.formatDateTime(row["producedAt"]),
                jobOrderCode = sql.str(row["jobOrderCode"]),
                jobOrderId = sql.longVal(row["jobOrderId"]),
            )
        }.distinctBy { "${it.itemCode}:${it.lotNo}" }
    }
}
