package com.ffii.fpsms.modules.deliveryOrder.service

import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus
import org.springframework.stereotype.Service

@Service
class TruckRoutingSummaryService(
    private val jdbcDao: JdbcDao,
) {
    fun getStoreOptions(): List<Map<String, String>> {
        val sql = """
            SELECT DISTINCT t.Store_id AS value
            FROM truck t
            WHERE t.deleted = 0
              AND t.Store_id IS NOT NULL
              AND TRIM(t.Store_id) <> ''
            ORDER BY t.Store_id
        """.trimIndent()

        return jdbcDao.queryForList(sql, emptyMap<String, Any>()).map { row ->
            val rawValue = (row["value"] ?: "").toString().trim()
            val label = when (rawValue.uppercase()) {
                "2F", "2/F" -> "2/F"
                "4F", "4/F" -> "4/F"
                else -> rawValue
            }
            mapOf("label" to label, "value" to rawValue)
        }
    }

    fun getLaneOptions(storeId: String?): List<Map<String, String>> {
        val args = mutableMapOf<String, Any>()
        val storeSql = if (!storeId.isNullOrBlank()) {
            args["storeId"] = storeId.replace("/", "")
            "AND REPLACE(t.Store_id, '/', '') = :storeId"
        } else {
            ""
        }

        val sql = """
            SELECT DISTINCT t.TruckLanceCode AS value
            FROM truck t
            WHERE t.deleted = 0
              AND t.TruckLanceCode IS NOT NULL
              AND TRIM(t.TruckLanceCode) <> ''
              $storeSql
            ORDER BY t.TruckLanceCode
        """.trimIndent()

        return jdbcDao.queryForList(sql, args).map { row ->
            val value = (row["value"] ?: "").toString().trim()
            mapOf("label" to value, "value" to value)
        }
    }

    fun search(storeId: String?, truckLanceCode: String?, reportDate: String?): List<Map<String, Any>> {
        val args = mutableMapOf<String, Any>()
        val storeSql = if (!storeId.isNullOrBlank()) {
            args["storeId"] = storeId.replace("/", "")
            "AND REPLACE(t.Store_id, '/', '') = :storeId"
        } else {
            ""
        }
        val laneSql = if (!truckLanceCode.isNullOrBlank()) {
            args["truckLanceCode"] = truckLanceCode.trim()
            "AND t.TruckLanceCode = :truckLanceCode"
        } else {
            ""
        }
        val storeSqlForMax = if (!storeId.isNullOrBlank()) {
            "AND REPLACE(t2.Store_id, '/', '') = :storeId"
        } else {
            ""
        }
        val laneSqlForMax = if (!truckLanceCode.isNullOrBlank()) {
            "AND t2.TruckLanceCode = :truckLanceCode"
        } else {
            ""
        }
        val cartonDateSql = if (!reportDate.isNullOrBlank()) {
            args["reportDate"] = reportDate.trim()
            "AND dpor.RequiredDeliveryDate = :reportDate"
        } else {
            ""
        }

        val sql = """
            SELECT
                CAST(
                    CASE
                        WHEN seq_max.maxLoading IS NULL OR t.LoadingSequence IS NULL THEN COALESCE(t.LoadingSequence, 0)
                        ELSE seq_max.maxLoading - t.LoadingSequence + 1
                    END AS CHAR
                ) AS dropOffSequence,
                COALESCE(s.`code`, '') AS shopCode,
                TRIM(
                    CASE
                        WHEN LOCATE(' - ', COALESCE(s.name, '')) > 0
                        THEN SUBSTRING(
                            COALESCE(s.name, ''),
                            LOCATE(' - ', COALESCE(s.name, '')) + 3
                        )
                        ELSE COALESCE(s.name, '')
                    END
                ) AS shopName,
                COALESCE(s.addr3, '') AS address,
                CASE
                    WHEN COALESCE(carton_sum.qty, 0) = 0 THEN '-'
                    ELSE CAST(carton_sum.qty AS CHAR)
                END AS noOfCartons
            FROM truck t
            LEFT JOIN shop s
                ON t.shopId = s.id
               AND s.deleted = 0
            LEFT JOIN (
                SELECT truck_id, SUM(cartonQty) AS qty
                FROM do_pick_order_record dpor
                WHERE dpor.deleted = 0
                  $cartonDateSql
                GROUP BY truck_id
            ) carton_sum ON carton_sum.truck_id = t.id
            CROSS JOIN (
                SELECT MAX(t2.LoadingSequence) AS maxLoading
                FROM truck t2
                WHERE t2.deleted = 0
                  $storeSqlForMax
                  $laneSqlForMax
            ) seq_max
            WHERE t.deleted = 0
              $storeSql
              $laneSql
            ORDER BY t.LoadingSequence DESC, COALESCE(s.`code`, t.ShopCode) ASC
        """.trimIndent()

        val rows = jdbcDao.queryForList(sql, args)
        if (rows.isNotEmpty()) {
            return rows
        }

        return listOf(
            mapOf(
                "dropOffSequence" to "",
                "shopCode" to "",
                "shopName" to "",
                "address" to "",
                "noOfCartons" to "",
            )
        )
    }

    fun getUnpickedOrderCount(storeId: String?, truckLanceCode: String?, reportDate: String?): Int {
        if (storeId.isNullOrBlank() || truckLanceCode.isNullOrBlank() || reportDate.isNullOrBlank()) {
            return 0
        }
        val args = mutableMapOf<String, Any>(
            "storeId" to storeId.replace("/", ""),
            "truckLanceCode" to truckLanceCode.trim(),
            "reportDate" to reportDate.trim(),
            "pendingStatus" to DoPickOrderStatus.pending.name,
        )
        val sql = """
            SELECT COUNT(1) AS unpickedCount
            FROM do_pick_order dpo
            WHERE dpo.deleted = 0
              AND REPLACE(dpo.store_id, '/', '') = :storeId
              AND dpo.TruckLanceCode = :truckLanceCode
              AND dpo.RequiredDeliveryDate = :reportDate
              AND dpo.ticket_status = :pendingStatus
        """.trimIndent()
        val row = jdbcDao.queryForList(sql, args).firstOrNull()
        return (row?.get("unpickedCount") as? Number)?.toInt() ?: 0
    }

    /**
     * Earliest departure time among trucks matching store + lane (for PDF header).
     */
    fun getDepartureTimeLabel(storeId: String?, truckLanceCode: String?): String {
        val args = mutableMapOf<String, Any>()
        val storeSql = if (!storeId.isNullOrBlank()) {
            args["storeId"] = storeId.replace("/", "")
            "AND REPLACE(t.Store_id, '/', '') = :storeId"
        } else {
            return ""
        }
        val laneSql = if (!truckLanceCode.isNullOrBlank()) {
            args["truckLanceCode"] = truckLanceCode.trim()
            "AND t.TruckLanceCode = :truckLanceCode"
        } else {
            return ""
        }
        val sql = """
            SELECT DATE_FORMAT(MIN(t.DepartureTime), '%H:%i') AS departureLabel
            FROM truck t
            WHERE t.deleted = 0
              AND t.DepartureTime IS NOT NULL
              $storeSql
              $laneSql
        """.trimIndent()
        val row = jdbcDao.queryForList(sql, args).firstOrNull()
        return (row?.get("departureLabel") ?: "").toString().trim()
    }

    fun formatStoreIdForDisplay(raw: String?): String {
        if (raw.isNullOrBlank()) return ""
        val normalized = raw.trim().replace("/", "")
        return when (normalized.uppercase()) {
            "2F" -> "2/F"
            "4F" -> "4/F"
            else -> raw.trim()
        }
    }
}
