package com.ffii.fpsms.modules.report.web

import org.springframework.http.*
import org.springframework.web.bind.annotation.*
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.math.BigDecimal
import com.ffii.fpsms.modules.report.service.ItemQcFailReportService
import com.ffii.fpsms.modules.report.service.ReportService
import org.apache.poi.ss.usermodel.BorderStyle
import org.apache.poi.ss.usermodel.FillPatternType
import org.apache.poi.ss.usermodel.HorizontalAlignment
import org.apache.poi.ss.usermodel.IndexedColors
import org.apache.poi.ss.usermodel.VerticalAlignment
import org.apache.poi.ss.usermodel.DataFormat
import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.ss.util.WorkbookUtil
import org.apache.poi.xssf.usermodel.XSSFWorkbook

@RestController
@RequestMapping("/report")
class ItemQcFailReportController(

    private val reportService: ReportService,
    private val itemQcFailReportService: ItemQcFailReportService,
) {

    @GetMapping("/print-item-qc-fail")
    fun generateItemQcFailReport(
        @RequestParam(required = false) stockCategory: String?,
        @RequestParam(required = false) itemCode: String?,
        @RequestParam(required = false) lastInDateStart: String?,
        @RequestParam(required = false) lastInDateEnd: String?,
    ): ResponseEntity<ByteArray> {
        val parameters = mutableMapOf<String, Any>()

        parameters["stockCategory"] = stockCategory ?: "All"
        parameters["stockSubCategory"] = stockCategory ?: "All" 
        parameters["itemNo"] = itemCode ?: "All"
        parameters["year"] = java.time.LocalDate.now().year.toString()
        parameters["reportDate"] = java.time.LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"))
        parameters["reportTime"] = java.time.LocalTime.now().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss"))

        parameters["storeLocation"] = ""
        parameters["balanceFilterStart"] = ""
        parameters["balanceFilterEnd"] = ""
        parameters["lastInDateStart"] = lastInDateStart ?: ""
        parameters["lastInDateEnd"] = lastInDateEnd ?: ""
        parameters["lastOutDateStart"] = ""
        parameters["lastOutDateEnd"] = ""

        val dbData = itemQcFailReportService.searchItemQcFailReport(
            stockCategory = stockCategory,
            itemCode = itemCode,
            lastInDateStart = lastInDateStart,
            lastInDateEnd = lastInDateEnd,
        )

        val pdfBytes = reportService.createPdfResponse(
            "/jasper/ItemQCReport.jrxml",
            parameters,
            dbData
        )

        val headers = org.springframework.http.HttpHeaders().apply {
            contentType = org.springframework.http.MediaType.APPLICATION_PDF
            setContentDispositionFormData("attachment", "ItemQCFailReport.pdf")
            set("filename", "ItemQCFailReport.pdf")
        }
        return org.springframework.http.ResponseEntity(pdfBytes, headers, org.springframework.http.HttpStatus.OK)
    }

    @GetMapping("/print-item-qc-fail-excel")
    fun exportItemQcFailReportExcel(
        @RequestParam(required = false) stockCategory: String?,
        @RequestParam(required = false) itemCode: String?,
        @RequestParam(required = false) lastInDateStart: String?,
        @RequestParam(required = false) lastInDateEnd: String?,
    ): ResponseEntity<ByteArray> {
        val dbData = itemQcFailReportService.searchItemQcFailReport(
            stockCategory = stockCategory,
            itemCode = itemCode,
            lastInDateStart = lastInDateStart,
            lastInDateEnd = lastInDateEnd,
        )

        val reportTitle = "庫存品質檢測報告"
        val excelBytes = createItemQcFailExcel(
            dbData = dbData,
            reportTitle = reportTitle,
            lastInDateStart = lastInDateStart,
            lastInDateEnd = lastInDateEnd
        )

        val headers = org.springframework.http.HttpHeaders().apply {
            contentType = org.springframework.http.MediaType.parseMediaType(
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            )
            setContentDispositionFormData("attachment", "ItemQCFailReport.xlsx")
            set("filename", "ItemQCFailReport.xlsx")
        }
        return org.springframework.http.ResponseEntity(excelBytes, headers, org.springframework.http.HttpStatus.OK)
    }

    private fun createItemQcFailExcel(
        dbData: List<Map<String, Any>>,
        reportTitle: String,
        lastInDateStart: String?,
        lastInDateEnd: String?
    ): ByteArray {
        val workbook: Workbook = XSSFWorkbook()
        val safeSheetName = WorkbookUtil.createSafeSheetName(reportTitle)
        val sheet = workbook.createSheet(safeSheetName)

        var rowIndex = 0
        val columnCount = 14

        val titleStyle = workbook.createCellStyle().apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
        }
        val titleFont = workbook.createFont().apply {
            bold = true
            fontHeightInPoints = 16
        }
        titleStyle.setFont(titleFont)

        // Title row
        run {
            val titleRowIndex = rowIndex++
            val row = sheet.createRow(titleRowIndex)
            val cell = row.createCell(0)
            cell.setCellValue(reportTitle)
            cell.cellStyle = titleStyle
            // Merge title across columns so the text won't be blocked by narrow col A.
            sheet.addMergedRegion(
                org.apache.poi.ss.util.CellRangeAddress(
                    titleRowIndex,
                    titleRowIndex,
                    0,
                    columnCount - 1
                )
            )
            row.heightInPoints = 24f
        }

        // Info row
        run {
            val row = sheet.createRow(rowIndex++)
            val cell = row.createCell(0)
            val startTxt = lastInDateStart ?: ""
            val endTxt = lastInDateEnd ?: ""
            cell.setCellValue("QC 不合格日期: ${startTxt} - ${endTxt}")
        }

        val headerStyle = workbook.createCellStyle().apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.TOP
            wrapText = true
            fillForegroundColor = IndexedColors.GREY_25_PERCENT.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
        }
        val headerFont = workbook.createFont().apply { bold = true }
        headerStyle.setFont(headerFont)

        val textStyle = workbook.createCellStyle().apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
        }

        val wrappedTextStyle = workbook.createCellStyle().apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.TOP
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            wrapText = true
        }

        val integerNumberStyle = workbook.createCellStyle().apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            val df = workbook.creationHelper.createDataFormat()
            dataFormat = df.getFormat("#,##0")
        }

        val decimalNumberStyle = workbook.createCellStyle().apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            val df = workbook.creationHelper.createDataFormat()
            dataFormat = df.getFormat("#,##0.##")
        }

        val headers = listOf(
            "庫存類別",
            "物料編號",
            "物料名稱",
            "單位",
            "批號",
            "到期日",
            "QC 類型",
            "QC 範本",
            "不合格標準",
            "批量",
            "不合格數量",
            "參考資料",
            "備註",
            "訂單/工單"
        )

        // Header row
        run {
            val row = sheet.createRow(rowIndex++)
            headers.forEachIndexed { col, h ->
                val cell = row.createCell(col)
                cell.setCellValue(h)
                cell.cellStyle = headerStyle
            }
            // Increase header row height so wrapped header text won't be blocked/truncated.
            row.heightInPoints = 35f
        }

        dbData.forEach { r ->
            val row = sheet.createRow(rowIndex++)
            fun writeText(col: Int, value: Any?) {
                val cell = row.createCell(col)
                cell.setCellValue(value?.toString() ?: "")
                cell.cellStyle = textStyle
            }

            fun writeNumber(col: Int, value: Any?) {
                val raw = value?.toString()?.trim() ?: ""
                // Some SQL strings may end with "."; Excel would display it as "4." otherwise.
                val cleaned = raw.removeSuffix(".")
                val bd = cleaned.toBigDecimalOrNull()
                val cell = row.createCell(col)

                if (bd == null) {
                    cell.setCellValue(cleaned)
                    cell.cellStyle = textStyle
                } else {
                    val stripped = bd.stripTrailingZeros()
                    if (stripped.scale() <= 0) {
                        // Render integer without decimal dot.
                        cell.setCellValue(stripped.toDouble())
                        cell.cellStyle = integerNumberStyle
                    } else {
                        cell.setCellValue(stripped.toDouble())
                        cell.cellStyle = decimalNumberStyle
                    }
                }
            }

            // Keys must match ItemQcFailReportService SQL aliases
            writeText(0, r["stockSubCategory"])
            writeText(1, r["itemNo"])
            writeText(2, r["itemName"])
            writeText(3, r["unitOfMeasure"])
            writeText(4, r["lotNo"])
            writeText(5, r["expiryDate"])
            writeText(6, r["qcType"])
            writeText(7, r["qcTemplate"])
            val defectCriteria = r["qcDefectCriteria"]
            // Wrap this column because values can be long.
            run {
                val cell = row.createCell(8)
                cell.setCellValue(defectCriteria?.toString() ?: "")
                cell.cellStyle = wrappedTextStyle
            }
            writeNumber(9, r["lotQty"])
            writeNumber(10, r["defectQty"])
            writeText(11, r["refData"])
            writeText(12, r["remark"])
            writeText(13, r["orderRefNo"])

            // If "不合格標準" is long, increase row height so wrapped text is visible.
            val defectText = defectCriteria?.toString() ?: ""
            // Estimate wrap lines using the known column width (approx chars per line).
            // This avoids ugly/uneven row heights from simple length thresholds.
            val charsPerLine = 38.0
            val lines = maxOf(
                1,
                kotlin.math.ceil(defectText.length / charsPerLine).toInt()
            )
            val baseHeight = 18f  // single-line
            val lineHeight = 13.5f // additional lines
            row.heightInPoints = (baseHeight + (lines - 1) * lineHeight).coerceIn(18f, 110f)
        }

        // Column widths: keep reasonable defaults, then auto-size a bit (optional)
        run {
            // Set explicit column widths (Excel units = 1/256th of a character width).
            val widths = intArrayOf(
                12, // stockSubCategory
                16, // itemNo
                18, // itemName
                10, // unit
                14, // lotNo
                12, // expiryDate
                10, // qcType
                20, // qcTemplate
                42, // qcDefectCriteria (wrap)
                12, // lotQty
                14, // defectQty
                22, // refData
                16, // remark
                18, // orderRefNo
            )
            for (col in 0 until widths.size) {
                sheet.setColumnWidth(col, widths[col] * 256)
            }
        }

        return workbookToByteArray(workbook)
    }

    private fun workbookToByteArray(workbook: Workbook): ByteArray {
        val out = java.io.ByteArrayOutputStream()
        workbook.write(out)
        workbook.close()
        return out.toByteArray()
    }
}


