package com.ffii.fpsms.modules.report.web
import org.springframework.web.bind.annotation.*
import org.springframework.http.*
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import com.ffii.fpsms.modules.report.service.StockLedgerReportService
import com.ffii.fpsms.modules.report.service.ReportService
import org.apache.poi.ss.usermodel.BorderStyle
import org.apache.poi.ss.usermodel.CellStyle
import org.apache.poi.ss.usermodel.DataFormat
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.Row
import org.apache.poi.ss.usermodel.VerticalAlignment
import org.apache.poi.ss.util.CellRangeAddress
import org.apache.poi.ss.util.WorkbookUtil
import org.apache.poi.xssf.usermodel.XSSFCellStyle
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import java.io.ByteArrayOutputStream

@RestController
@RequestMapping("/report")
class StockLedgerReportController(
    private val reportService: ReportService,
    private val stockLedgerReportService: StockLedgerReportService,
) {
    private data class ExcelStyles(
        val title: XSSFCellStyle,
        val subtitle: XSSFCellStyle,
        val header: XSSFCellStyle,
        val text: XSSFCellStyle,
        val center: XSSFCellStyle,
        val int: XSSFCellStyle,
        val dash: XSSFCellStyle,
        val sumQty: XSSFCellStyle,
        val sumLabel: XSSFCellStyle,
        val sumEmpty: XSSFCellStyle,
        val sumHidden: XSSFCellStyle,
    )

    @GetMapping("/print-stock-ledger")
fun generateStockLedgerReport(
    @RequestParam(required = false) stockCategory: String?,
    @RequestParam(required = false) itemCode: String?,
    @RequestParam(required = false) storeLocation: String?,
    // URL 參數名仍然是 lastInDateStart / lastInDateEnd
    @RequestParam(name = "lastInDateStart", required = false) reportPeriodStart: String?,
    @RequestParam(name = "lastInDateEnd", required = false) reportPeriodEnd: String?,
): ResponseEntity<ByteArray> {
    val parameters = mutableMapOf<String, Any>()

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

    parameters["storeLocation"] = storeLocation ?: ""
    parameters["balanceFilterStart"] = ""
    parameters["balanceFilterEnd"] = ""
    parameters["reportPeriodStart"] = reportPeriodStart ?: ""
    parameters["reportPeriodEnd"] = reportPeriodEnd ?: ""


    val dbData = stockLedgerReportService.searchStockLedgerReport(
        stockCategory = stockCategory,
        itemCode = itemCode,
        storeLocation = storeLocation,
        reportPeriodStart = reportPeriodStart,
        reportPeriodEnd = reportPeriodEnd,
    )

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

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

    @GetMapping("/print-stock-ledger-excel")
    fun exportStockLedgerReportExcel(
        @RequestParam(required = false) stockCategory: String?,
        @RequestParam(required = false) itemCode: String?,
        @RequestParam(required = false) storeLocation: String?,
        @RequestParam(name = "lastInDateStart", required = false) reportPeriodStart: String?,
        @RequestParam(name = "lastInDateEnd", required = false) reportPeriodEnd: String?,
    ): ResponseEntity<ByteArray> {
        val dbData = stockLedgerReportService.searchStockLedgerReport(
            stockCategory = stockCategory,
            itemCode = itemCode,
            storeLocation = storeLocation,
            reportPeriodStart = reportPeriodStart,
            reportPeriodEnd = reportPeriodEnd,
        )

        val excelBytes = createStockLedgerExcel(
            dbData = dbData,
            reportPeriodStart = reportPeriodStart ?: "",
            reportPeriodEnd = reportPeriodEnd ?: "",
        )

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

    private fun createStyles(workbook: XSSFWorkbook): ExcelStyles {
        val titleStyle = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            val font = workbook.createFont().apply {
                bold = true
                fontHeightInPoints = 14
            }
            setFont(font)
        }
        val subtitleStyle = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            val font = workbook.createFont().apply {
                bold = true
                fontHeightInPoints = 12
            }
            setFont(font)
        }
        val headerStyle = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.GREY_25_PERCENT.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            val font = workbook.createFont().apply { bold = true }
            setFont(font)
        }
        val textStyle = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
        }
        val centerStyle = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
        }
        val intStyle = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            val df: DataFormat = workbook.createDataFormat()
            dataFormat = df.getFormat("#,##0")
        }
        val dashStyle = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
        }
        val sumQty = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.CENTER
            val df: DataFormat = workbook.createDataFormat()
            dataFormat = df.getFormat("#,##0")
            borderTop = BorderStyle.THICK
            borderBottom = BorderStyle.THICK
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            val font = workbook.createFont().apply { bold = true }
            setFont(font)
        }
        val sumLabel = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THICK
            borderBottom = BorderStyle.THICK
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            val font = workbook.createFont().apply { bold = true }
            setFont(font)
        }
        val sumEmpty = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THICK
            borderBottom = BorderStyle.THICK
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
        }
        val sumHidden = (workbook.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THICK
            borderBottom = BorderStyle.THICK
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            val font = workbook.createFont().apply { color = IndexedColors.WHITE.index }
            setFont(font)
        }
        return ExcelStyles(
            title = titleStyle,
            subtitle = subtitleStyle,
            header = headerStyle,
            text = textStyle,
            center = centerStyle,
            int = intStyle,
            dash = dashStyle,
            sumQty = sumQty,
            sumLabel = sumLabel,
            sumEmpty = sumEmpty,
            sumHidden = sumHidden,
        )
    }

    private fun setTextCell(row: Row, col: Int, value: Any?, style: XSSFCellStyle) {
        row.createCell(col).apply {
            setCellValue(value?.toString() ?: "")
            cellStyle = style
        }
    }

    private fun parseSignedNumber(value: Any?): Double? {
        val raw = value?.toString()?.trim().orEmpty()
        if (raw.isBlank() || raw == "-" || raw.equals("null", ignoreCase = true)) return null
        val negative = raw.startsWith("(") && raw.endsWith(")")
        val cleaned = raw.removePrefix("(").removeSuffix(")").replace(",", "").trim()
        val n = cleaned.toDoubleOrNull() ?: return null
        return if (negative) -n else n
    }

    private fun setIntCellFromFormatted(
        row: Row,
        col: Int,
        value: Any?,
        intStyle: XSSFCellStyle,
        dashStyle: XSSFCellStyle,
    ) {
        val cell = row.createCell(col)
        val parsed = parseSignedNumber(value)
        if (parsed == null) {
            cell.setCellValue("")
            cell.cellStyle = dashStyle
            return
        }
        if (parsed < 0) {
            val s = "%,d".format(kotlin.math.abs(parsed).toLong())
            cell.setCellValue("($s)")
            cell.cellStyle = dashStyle
        } else {
            cell.setCellValue(parsed)
            cell.cellStyle = intStyle
        }
    }

    private fun createStockLedgerExcel(
        dbData: List<Map<String, Any>>,
        reportPeriodStart: String,
        reportPeriodEnd: String,
    ): ByteArray {
        val workbook = XSSFWorkbook()
        val styles = createStyles(workbook)
        val reportTitle = "庫存明細報告"
        val sheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(reportTitle))

        val headers = listOf(
            "貨品編號", "貨品名稱", "單位",
            "出入賬日期", "類型", "批號", "到期日",
            "纍計期初存量", "入庫", "出庫", "纍計存量",
            "參考編號", "存貨位置",
        )
        val totalColumns = headers.size
        var rowIndex = 0

        val titleRow = sheet.createRow(rowIndex++)
        titleRow.createCell(0).apply {
            setCellValue(reportTitle)
            cellStyle = styles.title
        }
        sheet.addMergedRegion(CellRangeAddress(0, 0, 0, totalColumns - 1))

        val reportDateTime =
            LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) +
                "(" +
                LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) +
                ")"
        val subtitleRow = sheet.createRow(rowIndex++)
        subtitleRow.createCell(0).apply {
            setCellValue("報告日期：$reportDateTime")
            cellStyle = styles.subtitle
        }
        sheet.addMergedRegion(CellRangeAddress(1, 1, 0, 4))
        subtitleRow.createCell(5).apply {
            setCellValue("報告期間：${reportPeriodStart.trim()} 至 ${reportPeriodEnd.trim()}")
            cellStyle = styles.subtitle
        }
        sheet.addMergedRegion(CellRangeAddress(1, 1, 5, totalColumns - 1))
        sheet.createRow(rowIndex++)

        val headerRowIndex = rowIndex
        val headerRow = sheet.createRow(rowIndex++)
        headers.forEachIndexed { i, h ->
            headerRow.createCell(i).apply {
                setCellValue(h)
                cellStyle = styles.header
            }
        }

        fun addItemSummaryRow(itemNo: String, itemName: String, uom: String, totalIn: Any?, totalOut: Any?, totalBal: Any?) {
            val r = sheet.createRow(rowIndex++)
            r.createCell(0).apply { setCellValue(itemNo); cellStyle = styles.sumHidden }
            r.createCell(1).apply { setCellValue(itemName); cellStyle = styles.sumHidden }
            r.createCell(2).apply { setCellValue(uom); cellStyle = styles.sumHidden }
            for (c in 3 until totalColumns) {
                r.createCell(c).apply { setCellValue(""); cellStyle = styles.sumEmpty }
            }
            // totals should align with numeric columns (shift right by 1)
            r.getCell(7).apply { setCellValue("貨品總量："); cellStyle = styles.sumLabel }
            setIntCellFromFormatted(r, 8, totalIn, styles.sumQty, styles.dash)
            setIntCellFromFormatted(r, 9, totalOut, styles.sumQty, styles.dash)
            setIntCellFromFormatted(r, 10, totalBal, styles.sumQty, styles.dash)
        }

        if (dbData.isEmpty()) {
            val r = sheet.createRow(rowIndex++)
            for (c in 0 until totalColumns) {
                r.createCell(c).apply { setCellValue("-"); cellStyle = styles.text }
            }
        } else {
            var currentItemNo: String? = null
            var currentItemName = ""
            var currentUom = ""
            var lastTotals: Triple<Any?, Any?, Any?> = Triple(null, null, null)

            dbData.forEach { m ->
                val itemNo = m["itemNo"]?.toString().orEmpty()
                val itemName = m["itemName"]?.toString().orEmpty()
                val uom = m["unitOfMeasure"]?.toString().orEmpty()

                if (currentItemNo != null && itemNo != currentItemNo) {
                    addItemSummaryRow(currentItemNo!!, currentItemName, currentUom, lastTotals.first, lastTotals.second, lastTotals.third)
                    sheet.createRow(rowIndex++)
                }

                val r = sheet.createRow(rowIndex++)
                setTextCell(r, 0, itemNo, styles.text)
                setTextCell(r, 1, itemName, styles.text)
                setTextCell(r, 2, uom, styles.center)
                setTextCell(r, 3, m["trnDate"], styles.center)
                val typeText = m["trnRefNo"]?.toString()?.trim().orEmpty().let { t ->
                    if (t.equals("Expiry", ignoreCase = true)) "過期" else t
                }
                setTextCell(r, 4, typeText, styles.center)
                setTextCell(r, 5, m["lotNo"], styles.text)
                setTextCell(r, 6, m["expiryDate"], styles.center)
                setIntCellFromFormatted(r, 7, m["cumOpeningBal"], styles.int, styles.dash)
                setIntCellFromFormatted(r, 8, m["stockIn"], styles.int, styles.dash)
                setIntCellFromFormatted(r, 9, m["stockOut"], styles.int, styles.dash)
                setIntCellFromFormatted(r, 10, m["cumBalance"], styles.int, styles.dash)
                setTextCell(r, 11, m["orderRefNo"], styles.text)
                setTextCell(r, 12, m["storeLocation"], styles.center)

                currentItemNo = itemNo
                currentItemName = itemName
                currentUom = uom
                lastTotals = Triple(m["totalStockIn"], m["totalStockOut"], m["totalCumBalance"])
            }

            addItemSummaryRow(currentItemNo ?: "", currentItemName, currentUom, lastTotals.first, lastTotals.second, lastTotals.third)
        }

        val lastRowIndex = rowIndex - 1
        if (lastRowIndex >= headerRowIndex) {
            sheet.setAutoFilter(CellRangeAddress(headerRowIndex, lastRowIndex, 0, 0))
        }
        val widths = intArrayOf(14, 26, 10, 12, 10, 16, 12, 14, 10, 10, 12, 18, 12)
        widths.forEachIndexed { i, w -> sheet.setColumnWidth(i, w * 256) }

        val out = ByteArrayOutputStream()
        workbook.use { it.write(out) }
        return out.toByteArray()
    }
}