package com.ffii.fpsms.modules.jobOrder.web

import com.ffii.fpsms.modules.jobOrder.service.LaserBag2AutoSendService
import com.ffii.fpsms.modules.jobOrder.service.PlasticBagPrinterService
import com.ffii.fpsms.modules.jobOrder.web.model.PrintRequest
import com.ffii.fpsms.modules.jobOrder.web.model.LaserRequest
import com.ffii.fpsms.modules.jobOrder.web.model.Laser2Request
import com.ffii.fpsms.modules.jobOrder.web.model.LaserBag2AutoSendReport
import com.ffii.fpsms.modules.jobOrder.web.model.LaserBag2SendRequest
import com.ffii.fpsms.modules.jobOrder.web.model.LaserBag2SendResponse
import com.ffii.fpsms.modules.jobOrder.web.model.LaserBag2MarkInfoResponse
import com.ffii.fpsms.modules.jobOrder.web.model.LaserBag2SettingsResponse
import com.ffii.fpsms.modules.jobOrder.web.model.NgpclPushResponse
import com.ffii.fpsms.modules.jobOrder.web.model.OnPackQrDownloadRequest
import com.ffii.fpsms.modules.jobOrder.web.model.PrinterStatusRequest
import com.ffii.fpsms.modules.jobOrder.web.model.PrinterStatusResponse
import com.ffii.fpsms.py.PyJobOrderListItem
import jakarta.servlet.http.HttpServletResponse
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
import java.time.LocalDate
import org.springframework.http.ResponseEntity
import org.slf4j.LoggerFactory

@RestController
@RequestMapping("/plastic")
class PlasticBagPrinterController(
    private val plasticBagPrinterService: PlasticBagPrinterService,
    private val laserBag2AutoSendService: LaserBag2AutoSendService,
) {
    private val logger = LoggerFactory.getLogger(javaClass)

    /** System defaults + current values from [LASER_PRINT.host] / [LASER_PRINT.port] / [LASER_PRINT.itemCodes] (see Liquibase). */
    @GetMapping("/laser-bag2-settings")
    fun getLaserBag2Settings(): LaserBag2SettingsResponse {
        return plasticBagPrinterService.getLaserBag2Settings()
    }

    /**
     * Live laser TCP queries: `GetMarkData`, `GetMarkStatus` (0 idle / 1 marking / 2 other), `GetMarkedCount`.
     * Uses [LASER_PRINT.host] / [LASER_PRINT.port] (same as print-laser-bag2).
     */
    @GetMapping("/laser-bag2-mark-info")
    fun getLaserBag2MarkInfo(): LaserBag2MarkInfoResponse {
        return plasticBagPrinterService.getLaserBag2MarkInfo()
    }

    /**
     * Job orders for /laserPrint: same as GET /py/job-orders but filtered by [LASER_PRINT.itemCodes] (comma-separated).
     * Blank itemCodes setting = no filter (all 包裝 job orders for the day).
     */
    @GetMapping("/laser-job-orders")
    fun listLaserJobOrders(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) planStart: LocalDate?,
    ): ResponseEntity<List<PyJobOrderListItem>> {
        val date = planStart ?: LocalDate.now()
        return ResponseEntity.ok(plasticBagPrinterService.listLaserPrintJobOrders(date))
    }

    /**
     * Bag2.py laser TCP protocol: `{"itemId":n,"stockInLineId":m};code;name;;` or `0;code;name;;`
     */
    @PostMapping("/print-laser-bag2")
    fun printLaserBag2(@RequestBody request: LaserBag2SendRequest): ResponseEntity<LaserBag2SendResponse> {
        val resp = plasticBagPrinterService.sendLaserBag2Job(request)
        return if (resp.success) {
            ResponseEntity.ok(resp)
        } else {
            ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(resp)
        }
    }

    /**
     * Same as /laserPrint row workflow: list job orders for [planStart] filtered by LASER_PRINT.itemCodes,
     * then auto-send at most the first matching job order via LASER_PRINT.host/port
     * (`laser.bag2.auto-send.sends-per-job` rounds for that one job).
     * For manual runs from /testing; scheduler uses [LaserBag2AutoSendScheduler].
     */
    @PostMapping("/laser-bag2-auto-send")
    fun runLaserBag2AutoSend(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) planStart: LocalDate?,
        @RequestParam(required = false, defaultValue = "0") limitPerRun: Int,
    ): ResponseEntity<LaserBag2AutoSendReport> {
        val date = planStart ?: LocalDate.now()
        val report = laserBag2AutoSendService.runAutoSend(
            planStart = date,
            limitPerRun = limitPerRun,
        )
        return ResponseEntity.ok(report)
    }

    @PostMapping("/check-printer")
    fun checkPrinter(@RequestBody request: PrinterStatusRequest): ResponseEntity<PrinterStatusResponse> {
        val (connected, message) = plasticBagPrinterService.checkPrinterConnection(
            printerType = request.printerType,
            printerIp = request.printerIp,
            printerPort = request.printerPort,
            labelCom = request.labelCom,
        )

        val body = PrinterStatusResponse(
            connected = connected,
            message = message,
        )

        return if (connected) {
            ResponseEntity.ok(body)
        } else {
            ResponseEntity.status(503).body(body)
        }
    }

    @PostMapping("/download-onpack-qr")
    fun downloadOnPackQr(
        @RequestBody request: OnPackQrDownloadRequest,
        response: HttpServletResponse,
    ) {
        try {
            val zipBytes = plasticBagPrinterService.generateOnPackQrZip(request.jobOrders)
            response.contentType = "application/zip"
            response.setHeader(
                HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"onpack_qr_codes.zip\""
            )
            response.setContentLength(zipBytes.size)
            response.outputStream.write(zipBytes)
            response.outputStream.flush()
        } catch (e: IllegalArgumentException) {
            response.status = HttpServletResponse.SC_BAD_REQUEST
            response.contentType = "text/plain;charset=UTF-8"
            response.writer.write(e.message ?: "Invalid request")
            response.writer.flush()
        } catch (e: Exception) {
            logger.error("POST /plastic/download-onpack-qr failed", e)
            try {
                if (!response.isCommitted) {
                    response.reset()
                }
            } catch (_: Exception) {
                /* ignore */
            }
            response.status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR
            response.contentType = "text/plain;charset=UTF-8"
            response.writer.write(e.message ?: "Download failed")
            response.writer.flush()
        }
    }

    /** OnPack2023 檸檬機: `onpack2030_2` templates with embedded QR (Static text only; no separate .bmp). */
    @PostMapping("/download-onpack-qr-text")
    fun downloadOnPackQrText(
        @RequestBody request: OnPackQrDownloadRequest,
        response: HttpServletResponse,
    ) {
        try {
            val zipBytes = plasticBagPrinterService.generateOnPackQrTextZip(request.jobOrders)
            response.contentType = "application/zip"
            response.setHeader(
                HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"onpack2023_lemon_qr.zip\""
            )
            response.setContentLength(zipBytes.size)
            response.outputStream.write(zipBytes)
            response.outputStream.flush()
        } catch (e: IllegalArgumentException) {
            response.status = HttpServletResponse.SC_BAD_REQUEST
            response.contentType = "text/plain;charset=UTF-8"
            response.writer.write(e.message ?: "Invalid request")
            response.writer.flush()
        } catch (e: Exception) {
            logger.error("POST /plastic/download-onpack-qr-text failed", e)
            try {
                if (!response.isCommitted) {
                    response.reset()
                }
            } catch (_: Exception) {
                /* ignore */
            }
            response.status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR
            response.contentType = "text/plain;charset=UTF-8"
            response.writer.write(e.message ?: "Download failed")
            response.writer.flush()
        }
    }

    /**
     * Same payload as [downloadOnPackQrText], but POSTs the generated ZIP to `ngpcl.push-url` (application/zip).
     * Returns JSON: `{ pushed, message }`. When push URL is unset, `pushed` is false — use download instead.
     */
    @PostMapping("/ngpcl/push-onpack-qr-text")
    fun pushOnPackQrTextToNgpcl(@RequestBody request: OnPackQrDownloadRequest): ResponseEntity<NgpclPushResponse> {
        return ResponseEntity.ok(plasticBagPrinterService.pushOnPackQrTextZipToNgpcl(request.jobOrders))
    }

    /**
     * Test API to generate and download the printer job files as a ZIP.
     * ONPACK2030
     */
    @GetMapping("/get-printer6")
    fun testPrintJob(
        @RequestParam itemCode: String,
        @RequestParam lotNo: String,
        @RequestParam expiryDate: String,
        @RequestParam productName: String,
        response: HttpServletResponse
    ) {
        // Generate the ZIP bundle via the service
        val zipBytes = plasticBagPrinterService.generatePrintJobBundle(
            itemCode, 
            lotNo, 
            expiryDate, 
            productName
        )

        // Set headers for file download
        response.contentType = "application/zip"
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"TT_${itemCode}_Job.zip\"")
        response.setContentLength(zipBytes.size)

        // Write to response stream
        response.outputStream.write(zipBytes)
        response.outputStream.flush()
    }

    /*
    @PostMapping("/print-dataflex")
    fun printDataFlex(@RequestBody request: PrintRequest): ResponseEntity<String> {
        return try {
            plasticBagPrinterService.sendDataFlexJob(request)
            ResponseEntity.ok("Print command sent successfully")
        } catch (e: Exception) {
            ResponseEntity.status(500).body("Error: ${e.message}")
        }
    }*/

    @PostMapping("/print-dataflex")
    fun printDataFlex6330Zpl(@RequestBody request: PrintRequest): ResponseEntity<String> {
        return try {
            plasticBagPrinterService.sendDataFlex6330Zpl(request)
            ResponseEntity.ok("DataFlex 6330 ZPL print command sent successfully to ${request.printerIp}:${request.printerPort}")
        } catch (e: Exception) {
            ResponseEntity.status(500).body("Error sending ZPL to DataFlex 6330: ${e.message}")
        }
    }

    @PostMapping("/print-laser")
    fun printLaser(@RequestBody request: LaserRequest): ResponseEntity<String> {
        return try {
            // Call the laser marking service
            plasticBagPrinterService.sendLaserMark(request)
            ResponseEntity.ok("Laser marking command sent successfully to ${request.printerIp}")
        } catch (e: Exception) {
            // Log the error and return 500 status
            ResponseEntity.status(500).body("Laser Error: ${e.message}")
        }
    }

    @PostMapping("/preview-laser")
    fun previewLaser(@RequestBody request: LaserRequest): ResponseEntity<String> {
        return try {
            plasticBagPrinterService.sendLaserPreview(request)
            ResponseEntity.ok("Red light activated")
        } catch (e: Exception) {
            ResponseEntity.status(500).body("Preview Error: ${e.message}")
        }
    }

    @PostMapping("/print-tsc")
    fun printTsc(@RequestBody request: PrintRequest): ResponseEntity<String> {
        return try {
            plasticBagPrinterService.sendTscPrintJob(request)
            ResponseEntity.ok("TSC Print command sent to ${request.printerIp}")
        } catch (e: Exception) {
            ResponseEntity.status(500).body("Error: ${e.message}")
        }
    }

    @GetMapping("/test-x40")
    fun testX40(
        @RequestParam printerIp: String,
        @RequestParam(defaultValue = "3001") port: Int
    ): ResponseEntity<String> {
        val (success, message) = plasticBagPrinterService.testSmartDateX40Tcp(printerIp, port)
        return if (success) {
            ResponseEntity.ok("OK - $message")
        } else {
            ResponseEntity.status(503).body("FAIL - $message")
        }
    }

    @PostMapping("/print-laser-tcp")
    fun printLaserTcp(@RequestBody request: Laser2Request): ResponseEntity<String> {
        return try {
            val success = plasticBagPrinterService.sendLaserTcpUpdateAndMark(
                ip = request.printerIp,
                port = request.printerPort.toInt(),
                textChannel3 = request.textChannel3 
                    ?: throw IllegalArgumentException("缺少 textChannel3"),
                textChannel4 = request.textChannel4 
                    ?: throw IllegalArgumentException("缺少 textChannel4"),
                text3ObjectName = request.text3ObjectName ?: "Text3",
                text4ObjectName = request.text4ObjectName ?: "Text4"
            )

            if (success) {
                ResponseEntity.ok("Han's Laser TCP: 更新文字並觸發打標成功")
            } else {
                ResponseEntity.status(500)
                    .body("Han's Laser TCP: 部分或全部命令失敗，請檢查連接、物件名稱或 EZCAD TCP Server")
            }
        } catch (e: Exception) {
            ResponseEntity.status(500).body("Laser TCP Error: ${e.message}")
        }
    }

}