package com.ffii.fpsms.modules.jobOrder.service

import com.ffii.fpsms.modules.jobOrder.web.model.LaserBag2AutoSendReport
import com.ffii.fpsms.modules.jobOrder.web.model.LaserBag2JobSendResult
import com.ffii.fpsms.modules.jobOrder.web.model.LaserBag2SendRequest
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.time.LocalDate

/**
 * Finds packaging job orders for [planStart] using the same filter as [PlasticBagPrinterService.listLaserPrintJobOrders]
 * ([LASER_PRINT.itemCodes]), then sends Bag2-style laser TCP payloads via [PlasticBagPrinterService.sendLaserBag2Job],
 * which uses [com.ffii.fpsms.modules.common.SettingNames.LASER_PRINT_HOST] / [LASER_PRINT_PORT] from the database.
 *
 * Safety rules:
 * 1) Probe laser TCP first; offline = skip.
 * 2) If [LASER_PRINT.lastReceiveSuccess] is already on [planStart] (AUTO or MANUAL), skip for the day.
 * 3) Send only the first matching job order.
 *
 * [sendsPerJob] TCP rounds per job order (default from `laser.bag2.auto-send.sends-per-job`, typically 1).
 * [sendLaserBag2Job] may still retry once internally on failure.
 */
@Service
class LaserBag2AutoSendService(
    private val plasticBagPrinterService: PlasticBagPrinterService,
    @Value("\${laser.bag2.auto-send.sends-per-job:1}") private val defaultSendsPerJob: Int,
    @Value("\${laser.bag2.auto-send.delay-between-sends-ms:3000}") private val defaultDelayBetweenSendsMs: Long,
) {
    private val logger = LoggerFactory.getLogger(javaClass)

    fun runAutoSend(
        planStart: LocalDate,
        limitPerRun: Int = 0,
        sendsPerJob: Int = defaultSendsPerJob,
        delayBetweenSendsMs: Long = defaultDelayBetweenSendsMs,
    ): LaserBag2AutoSendReport {
        val (reachable, laserIp, laserPort) = plasticBagPrinterService.probeLaserBag2Tcp()
        if (!reachable) {
            logger.warn("Connection failed to the laser print: {} / {}", laserIp, laserPort)
            return LaserBag2AutoSendReport(
                planStart = planStart,
                jobOrdersFound = 0,
                jobOrdersProcessed = 0,
                results = emptyList(),
            )
        }

        if (plasticBagPrinterService.hasLaserBag2ReceiveOnDate(planStart)) {
            logger.info("Laser Bag2 auto-send skipped: already sent (AUTO/MANUAL) on {}", planStart)
            return LaserBag2AutoSendReport(
                planStart = planStart,
                jobOrdersFound = 0,
                jobOrdersProcessed = 0,
                results = emptyList(),
            )
        }

        val orders = plasticBagPrinterService.listLaserPrintJobOrders(planStart)
        val effectiveLimit = if (limitPerRun > 0) limitPerRun.coerceAtMost(1) else 1
        val toProcess = orders.take(effectiveLimit)
        val results = mutableListOf<LaserBag2JobSendResult>()

        logger.info(
            "Laser Bag2 auto-send: planStart={}, found={}, processing={}, sendsPerJob={}",
            planStart,
            orders.size,
            toProcess.size,
            sendsPerJob,
        )

        for (jo in toProcess) {
            var lastMsg = ""
            var overallOk = true
            var lastPrinterAck: String? = null
            var lastReceiveAck = false
            for (attempt in 1..sendsPerJob) {
                val resp = plasticBagPrinterService.sendLaserBag2Job(
                    LaserBag2SendRequest(
                        itemId = jo.itemId,
                        stockInLineId = jo.stockInLineId,
                        itemCode = jo.itemCode,
                        itemName = jo.itemName,
                        jobOrderId = jo.id,
                        jobOrderNo = jo.code,
                        lotNo = jo.lotNo,
                        source = "AUTO",
                    ),
                )
                lastMsg = resp.message
                lastPrinterAck = resp.printerAck
                lastReceiveAck = resp.receiveAcknowledged
                if (!resp.success) {
                    overallOk = false
                    logger.warn("Laser send failed jobOrderId={} attempt={}: {}", jo.id, attempt, resp.message)
                    break
                }
                if (attempt < sendsPerJob) {
                    try {
                        Thread.sleep(delayBetweenSendsMs)
                    } catch (e: InterruptedException) {
                        Thread.currentThread().interrupt()
                        overallOk = false
                        lastMsg = "Interrupted"
                        break
                    }
                }
            }
            results.add(
                LaserBag2JobSendResult(
                    jobOrderId = jo.id,
                    itemCode = jo.itemCode,
                    success = overallOk,
                    message = lastMsg,
                    printerAck = lastPrinterAck,
                    receiveAcknowledged = lastReceiveAck,
                ),
            )
        }

        return LaserBag2AutoSendReport(
            planStart = planStart,
            jobOrdersFound = orders.size,
            jobOrdersProcessed = toProcess.size,
            results = results,
        )
    }
}
