package com.ffii.fpsms.modules.report.service

import com.ffii.core.support.JdbcDao
import org.springframework.stereotype.Service

@Service
class FGStockOutTraceabilityReportService(
    private val jdbcDao: JdbcDao,
) {
    fun getDistinctHandlersForFGStockOutTraceability(): List<String> {
        val sql = """
        SELECT DISTINCT h.handler
        FROM (
            SELECT TRIM(COALESCE(picker_user.name, modified_user.name, '')) AS handler
            FROM stock_out_line sol
            INNER JOIN stock_out so ON sol.stockOutId = so.id AND so.deleted = 0 AND so.type = 'do'
            LEFT JOIN user picker_user ON sol.handled_by = picker_user.id AND picker_user.deleted = 0
            LEFT JOIN user modified_user ON sol.modifiedBy = modified_user.staffNo AND modified_user.deleted = 0 AND sol.handled_by IS NULL
            WHERE sol.deleted = 0
            UNION
            SELECT TRIM(IFNULL(handlerName, '')) AS handler
            FROM delivery_order_pick_order
            WHERE deleted = 0
              AND ticketStatus = 'completed'
              AND IFNULL(handlerName, '') <> ''
        ) h
        WHERE TRIM(IFNULL(h.handler, '')) <> ''
        ORDER BY h.handler
    """.trimIndent()

        return jdbcDao
            .queryForList(sql, emptyMap<String, Any>())
            .map { row -> (row["handler"]?.toString() ?: "").trim() }
            .filter { it.isNotBlank() }
    }

    fun searchFGStockOutTraceabilityReport(
        stockCategory: String?,
        stockSubCategory: String?,
        itemCode: String?,
        year: String?,
        lastOutDateStart: String?,
        lastOutDateEnd: String?,
        handler: String?,
    ): List<Map<String, Any>> {
        val args = mutableMapOf<String, Any>()

        val stockCategorySql = buildMultiValueExactClause(
            stockCategory,
            "it.type",
            "stockCategory",
            args,
        )

        // Keep parameter for API compatibility; currently no SQL filter is needed.
        val stockSubCategorySql = ""

        val itemCodeSql = buildMultiValueLikeClause(
            itemCode,
            "it.code",
            "itemCode",
            args,
        )

        val yearSql = if (!year.isNullOrBlank()) {
            args["year"] = year
            "AND YEAR(IFNULL(dopo.requiredDeliveryDate, do.estimatedArrivalDate)) = :year"
        } else {
            ""
        }

        val lastOutDateStartSql = if (!lastOutDateStart.isNullOrBlank()) {
            val formattedDate = lastOutDateStart.replace("/", "-")
            args["lastOutDateStart"] = formattedDate
            "AND DATE(IFNULL(dopo.requiredDeliveryDate, do.estimatedArrivalDate)) >= DATE(:lastOutDateStart)"
        } else {
            ""
        }

        val lastOutDateEndSql = if (!lastOutDateEnd.isNullOrBlank()) {
            val formattedDate = lastOutDateEnd.replace("/", "-")
            args["lastOutDateEnd"] = formattedDate
            "AND DATE(IFNULL(dopo.requiredDeliveryDate, do.estimatedArrivalDate)) <= DATE(:lastOutDateEnd)"
        } else {
            ""
        }

        val handlerSql = buildMultiValueExactClause(
            handler,
            "COALESCE(picker_user.name, modified_user.name, IFNULL(dopo.handlerName, ''))",
            "handler",
            args,
        )

        val sql = """
      SELECT
        IFNULL(DATE_FORMAT(
            IFNULL(dopo.requiredDeliveryDate, do.estimatedArrivalDate),
            '%Y-%m-%d'
        ), '') AS deliveryDate,
        IFNULL(it.code, '') AS itemNo,
        IFNULL(it.name, '') AS itemName,
        IFNULL(uc.udfudesc, '') AS unitOfMeasure,
        IFNULL(dopo.deliveryNoteCode, '') AS dnNo,
        CAST(IFNULL(sp.id, 0) AS CHAR) AS customerId,
        IFNULL(sp.name, '') AS customerName,
        FORMAT(
            ROUND(SUM(IFNULL(sol.qty, 0)) OVER (PARTITION BY it.code), 0), 0
        ) AS qtyNumeric,
        FORMAT(ROUND(IFNULL(sol.qty, 0), 0), 0) AS qty,
        '' AS truckNo,
        '' AS driver,
        IFNULL(do.code, '') AS deliveryOrderNo,
        IFNULL(po.code, '') AS fgPickOrderNo,
        IFNULL(po.code, '') AS stockReqNo,
        IFNULL(il.lotNo, '') AS lotNo,
        IFNULL(DATE_FORMAT(il.expiryDate, '%Y-%m-%d'), '') AS expiryDate,
        FORMAT(ROUND(IFNULL(sol.qty, 0), 0), 0) AS stockOutQty,
        COALESCE(
            picker_user.name,
            modified_user.name,
            dopo.handlerName,
            ''
        ) AS handler,
        COALESCE(
            picker_user.name,
            modified_user.name,
            dopo.handlerName,
            ''
        ) AS pickedBy,
        GROUP_CONCAT(DISTINCT wh.code ORDER BY wh.code SEPARATOR ', ') AS storeLocation,
        '' AS pickRemark,
        FORMAT(
            ROUND(SUM(IFNULL(sol.qty, 0)) OVER (PARTITION BY it.code), 0), 0
        ) AS totalStockOutQty,
        0 AS stockSubCategory
    FROM delivery_order_pick_order dopo
        INNER JOIN pick_order po
            ON po.deliveryOrderPickOrderId = dopo.id
           AND po.deleted = 0
        INNER JOIN delivery_order do
            ON po.doId = do.id
           AND do.deleted = 0
        LEFT JOIN shop sp
            ON do.shopId = sp.id
           AND sp.deleted = 0
        LEFT JOIN pick_order_line pol
            ON pol.poId = po.id
           AND pol.deleted = 0
        LEFT JOIN items it
            ON pol.itemId = it.id
           AND it.deleted = 0
        LEFT JOIN delivery_order_line dol
            ON dol.deliveryOrderId = do.id
           AND dol.itemId = pol.itemId
           AND dol.deleted = 0
        LEFT JOIN item_uom iu
            ON it.id = iu.itemId
           AND iu.stockUnit = 1
        LEFT JOIN uom_conversion uc
            ON iu.uomId = uc.id
        LEFT JOIN stock_out_line sol
            ON pol.id = sol.pickOrderLineId
           AND sol.itemId = it.id
           AND sol.deleted = 0
        LEFT JOIN stock_out so
            ON sol.stockOutId = so.id
           AND so.deleted = 0
           AND so.type = 'do'
        LEFT JOIN inventory_lot_line ill
            ON sol.inventoryLotLineId = ill.id
           AND ill.deleted = 0
        LEFT JOIN inventory_lot il
            ON ill.inventoryLotId = il.id
           AND il.deleted = 0
        LEFT JOIN warehouse wh
            ON ill.warehouseId = wh.id
           AND wh.deleted = 0
        LEFT JOIN user picker_user
            ON sol.handled_by = picker_user.id
           AND picker_user.deleted = 0
        LEFT JOIN user modified_user
            ON sol.modifiedBy = modified_user.staffNo
           AND modified_user.deleted = 0
           AND sol.handled_by IS NULL
    WHERE
        dopo.deleted = 0
        AND dopo.ticketStatus = 'completed'
        $stockCategorySql
        $stockSubCategorySql
        $itemCodeSql
        $yearSql
        $lastOutDateStartSql
        $lastOutDateEndSql
        $handlerSql
        GROUP BY
        sol.id,
        dopo.requiredDeliveryDate,
        dopo.handlerName,
        do.estimatedArrivalDate,
        it.code,
        it.name,
        uc.udfudesc,
        dopo.deliveryNoteCode,
        sp.id,
        sp.name,
        sol.qty,
        picker_user.name,
        modified_user.name,
        po.code,
        do.code,
        il.lotNo,
        il.expiryDate
    ORDER BY
        it.code,
        deliveryDate,
        il.lotNo
""".trimIndent()

        return jdbcDao.queryForList(sql, args)
    }

    private fun buildMultiValueLikeClause(
        paramValue: String?,
        columnName: String,
        paramPrefix: String,
        args: MutableMap<String, Any>,
    ): String {
        if (paramValue.isNullOrBlank()) return ""

        val values = paramValue.split(",").map { it.trim() }.filter { it.isNotBlank() }
        if (values.isEmpty()) return ""

        val conditions = values.mapIndexed { index, value ->
            val paramName = "${paramPrefix}_$index"
            args[paramName] = "%$value%"
            "$columnName LIKE :$paramName"
        }

        return "AND (${conditions.joinToString(" OR ")})"
    }

    private fun buildMultiValueExactClause(
        paramValue: String?,
        columnName: String,
        paramPrefix: String,
        args: MutableMap<String, Any>,
    ): String {
        if (paramValue.isNullOrBlank()) return ""

        val values = paramValue.split(",").map { it.trim() }.filter { it.isNotBlank() }
        if (values.isEmpty()) return ""

        val conditions = values.mapIndexed { index, value ->
            val paramName = "${paramPrefix}_$index"
            args[paramName] = value
            "$columnName = :$paramName"
        }

        return "AND (${conditions.joinToString(" OR ")})"
    }
}
