package com.ffii.fpsms.py

import com.ffii.fpsms.modules.jobOrder.entity.JobOrder
import com.ffii.fpsms.modules.master.service.ItemUomService
import com.ffii.fpsms.modules.stock.entity.StockInLineRepository

object PyJobOrderListMapper {

    private val GRADE_IN_PARENS = Regex("""^P\+\d+$""", RegexOption.IGNORE_CASE)
    private val PAREN_SEGMENT = Regex("""[（(]([^（）()]*)[）)]""")
    private val UOM_UNIT_TOKEN = Regex(
        """(包|袋|磅|千克|公斤|克|毫升|公升|個|隻|斤|安士|ml|ML|kg|KG|\d+\s*[gG]\b|/包|/[磅千克克])""",
    )

    /**
     * True when [segment] looks like stock/pack UOM text (e.g. "1包X2磅", "0.9L/包"),
     * not usage/grade notes (e.g. "菠菜用", "熟", "P+4").
     */
    fun looksLikePackagingUom(segment: String): Boolean {
        val s = segment.trim()
        if (s.isEmpty()) return false
        if (GRADE_IN_PARENS.matches(s)) return false
        if (!s.any { it.isDigit() }) return false
        return UOM_UNIT_TOKEN.containsMatchIn(s)
    }

    /** True if any parenthetical segment in [name] is packaging/UOM-like. */
    fun nameAlreadyHasPackagingUom(name: String): Boolean =
        PAREN_SEGMENT.findAll(name).any { looksLikePackagingUom(it.groupValues[1]) }

    /**
     * Append stock [udfudesc] in parentheses when the base name lacks packaging UOM.
     * Used by Bag3 / bagPrint via GET /py/job-orders.
     */
    fun buildDisplayItemName(baseName: String?, stockUnitDesc: String?): String? {
        val stock = stockUnitDesc?.trim().orEmpty()
        val baseTrim = baseName?.trim().orEmpty()
        return when {
            stock.isEmpty() && baseTrim.isEmpty() -> null
            stock.isEmpty() -> baseTrim
            baseTrim.isEmpty() -> "($stock)"
            nameAlreadyHasPackagingUom(baseTrim) -> baseTrim
            stockAlreadyPresentInName(baseTrim, stock) -> baseTrim
            else -> "$baseTrim($stock)"
        }
    }

    private fun stockAlreadyPresentInName(name: String, stockUnitDesc: String): Boolean {
        val norm = stockUnitDesc.replace(" ", "").lowercase()
        if (norm.isEmpty()) return false
        val nameNorm = name.replace(" ", "").lowercase()
        return nameNorm.contains(norm)
    }

    /**
     * Item codes where /laserPrint must not append stock UOM to [itemName] (laser field length limit).
     * BOM base name only, e.g. PP1175 "鮮檸檬汁(P+4)" without "(1包X2磅)" suffix.
     */
    private val LASER_OMIT_APPENDED_STOCK_UOM_ITEM_CODES = setOf("PP1175")

    /**
     * Display name for GET /py/laser-job-orders and /plastic/laser-job-orders (/laserPrint page).
     * Same as [buildDisplayItemName] except listed item codes never get stock UOM appended.
     */
    fun buildDisplayItemNameForLaser(
        baseName: String?,
        stockUnitDesc: String?,
        itemCode: String?,
    ): String? {
        val code = itemCode?.trim().orEmpty()
        if (code in LASER_OMIT_APPENDED_STOCK_UOM_ITEM_CODES) {
            val baseTrim = baseName?.trim().orEmpty()
            return baseTrim.ifEmpty { null }
        }
        return buildDisplayItemName(baseName, stockUnitDesc)
    }

    fun toListItem(
        jo: JobOrder,
        printed: PrintedQtyByChannel?,
        stockInLineRepository: StockInLineRepository,
        itemUomService: ItemUomService,
    ): PyJobOrderListItem {
        val itemCode = jo.bom?.item?.code ?: jo.bom?.code
        val baseName = jo.bom?.name ?: jo.bom?.item?.name
        val itemId = jo.bom?.item?.id
        val stockUnitDesc = itemId?.let { id ->
            itemUomService.findStockUnitByItemId(id)?.uom?.udfudesc
        }
        val itemName = buildDisplayItemName(baseName, stockUnitDesc)

        val stockInLine = jo.id?.let { stockInLineRepository.findFirstByJobOrder_IdAndDeletedFalse(it) }
        val stockInLineId = stockInLine?.id
        val lotNo = stockInLine?.lotNo
        val p = printed ?: PrintedQtyByChannel()

        return PyJobOrderListItem(
            id = jo.id!!,
            code = jo.code,
            planStart = jo.planStart,
            itemCode = itemCode,
            itemName = itemName,
            reqQty = jo.reqQty,
            stockInLineId = stockInLineId,
            itemId = itemId,
            lotNo = lotNo,
            bagPrintedQty = p.bagPrintedQty,
            labelPrintedQty = p.labelPrintedQty,
            laserPrintedQty = p.laserPrintedQty,
        )
    }

    /** /laserPrint list: may shorten [itemName] for laser length limits (see [buildDisplayItemNameForLaser]). */
    fun toLaserListItem(
        jo: JobOrder,
        printed: PrintedQtyByChannel?,
        stockInLineRepository: StockInLineRepository,
        itemUomService: ItemUomService,
    ): PyJobOrderListItem {
        val itemCode = jo.bom?.item?.code ?: jo.bom?.code
        val baseName = jo.bom?.name ?: jo.bom?.item?.name
        val itemId = jo.bom?.item?.id
        val stockUnitDesc = itemId?.let { id ->
            itemUomService.findStockUnitByItemId(id)?.uom?.udfudesc
        }
        val itemName = buildDisplayItemNameForLaser(baseName, stockUnitDesc, itemCode)

        val stockInLine = jo.id?.let { stockInLineRepository.findFirstByJobOrder_IdAndDeletedFalse(it) }
        val stockInLineId = stockInLine?.id
        val lotNo = stockInLine?.lotNo
        val p = printed ?: PrintedQtyByChannel()

        return PyJobOrderListItem(
            id = jo.id!!,
            code = jo.code,
            planStart = jo.planStart,
            itemCode = itemCode,
            itemName = itemName,
            reqQty = jo.reqQty,
            stockInLineId = stockInLineId,
            itemId = itemId,
            lotNo = lotNo,
            bagPrintedQty = p.bagPrintedQty,
            labelPrintedQty = p.labelPrintedQty,
            laserPrintedQty = p.laserPrintedQty,
        )
    }
}
