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

import net.sf.jasperreports.engine.*
import org.springframework.http.*
import org.springframework.web.bind.annotation.*
import java.io.ByteArrayOutputStream
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import com.ffii.fpsms.modules.report.service.SemiFGProductionAnalysisReportService
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.util.WorkbookUtil
import org.apache.poi.ss.usermodel.VerticalAlignment
import org.apache.poi.ss.usermodel.DataValidationConstraint
import org.apache.poi.ss.usermodel.DataValidationHelper
import org.apache.poi.ss.usermodel.SheetVisibility
import org.apache.poi.ss.util.CellRangeAddress
import org.apache.poi.ss.util.CellRangeAddressList
import org.apache.poi.ss.util.CellReference
import org.apache.poi.xddf.usermodel.chart.AxisPosition
import org.apache.poi.xddf.usermodel.chart.BarDirection
import org.apache.poi.xddf.usermodel.chart.ChartTypes
import org.apache.poi.xddf.usermodel.chart.LegendPosition
import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory
import org.apache.poi.xssf.usermodel.XSSFClientAnchor
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import kotlin.math.roundToLong

@RestController
@RequestMapping("/report")
class SemiFGProductionAnalysisReportController(
    private val semiFGProductionAnalysisReportService: SemiFGProductionAnalysisReportService,
    private val reportService: ReportService,
) {

    @GetMapping("/print-semi-fg-production-analysis")
    fun generateSemiFGProductionAnalysisReport(
        @RequestParam(required = false) stockCategory: String?,
        @RequestParam(required = false) stockSubCategory: String?,
        @RequestParam(required = false) itemCode: String?,
        @RequestParam(required = false) year: String?,
        @RequestParam(required = false) lastOutDateStart: String?,
        @RequestParam(required = false) lastOutDateEnd: String?
    ): ResponseEntity<ByteArray> {
        val parameters = mutableMapOf<String, Any>()
        
        // Set report header parameters
        parameters["stockCategory"] = stockCategory ?: "All"
        parameters["stockSubCategory"] = stockSubCategory ?: "All"
        parameters["itemNo"] = itemCode ?: "All"
        parameters["year"] = 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["lastOutDateStart"] = lastOutDateStart ?: ""
        parameters["lastOutDateEnd"] = lastOutDateEnd ?: ""
        parameters["deliveryPeriodStart"] = ""
        parameters["deliveryPeriodEnd"] = ""

        // Query the DB to get a list of data
        val dbData = semiFGProductionAnalysisReportService.searchSemiFGProductionAnalysisReport(
            stockCategory,
            stockSubCategory,
            itemCode,
            year,
            lastOutDateStart,
            lastOutDateEnd
        )

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

        val headers = HttpHeaders().apply {
            contentType = MediaType.APPLICATION_PDF
            setContentDispositionFormData("attachment", "SemiFGProductionAnalysisReport.pdf")
            set("filename", "SemiFGProductionAnalysisReport.pdf")
        }

        return ResponseEntity(pdfBytes, headers, HttpStatus.OK)
    }

    @GetMapping("/print-semi-fg-production-analysis-excel")
    fun exportSemiFGProductionAnalysisReportExcel(
        @RequestParam(required = false) stockCategory: String?,
        @RequestParam(required = false) stockSubCategory: String?,
        @RequestParam(required = false) itemCode: String?,
        @RequestParam(required = false) year: String?,
        @RequestParam(required = false) lastOutDateStart: String?,
        @RequestParam(required = false) lastOutDateEnd: String?
    ): ResponseEntity<ByteArray> {
        val dbData = semiFGProductionAnalysisReportService.searchSemiFGProductionAnalysisReport(
            stockCategory,
            stockSubCategory,
            itemCode,
            year,
            lastOutDateStart,
            lastOutDateEnd
        )

        val excelBytes = createSemiFGProductionAnalysisExcel(
            dbData = dbData,
            reportTitle = "成品/半成品生產分析報告",
            year = year
        )

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

        return ResponseEntity(excelBytes, headers, HttpStatus.OK)
    }

    private fun createSemiFGProductionAnalysisExcel(
        dbData: List<Map<String, Any>>,
        reportTitle: String,
        year: String?
    ): ByteArray {
        val workbook = XSSFWorkbook()
        val safeSheetName = WorkbookUtil.createSafeSheetName(reportTitle)
        val sheet = workbook.createSheet(safeSheetName)
        val totalColumns = 16
        var rowIndex = 0

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

        val infoStyle = workbook.createCellStyle().apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
        }

        val headerStyle = workbook.createCellStyle().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 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 numberStyle = workbook.createCellStyle().apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.CENTER
            borderTop = BorderStyle.THIN
            borderBottom = BorderStyle.THIN
            borderLeft = BorderStyle.THIN
            borderRight = BorderStyle.THIN
            dataFormat = workbook.creationHelper.createDataFormat().getFormat("#,##0")
        }

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

        val titleRow = sheet.createRow(rowIndex++)
        titleRow.heightInPoints = 28f
        val titleCell = titleRow.createCell(0)
        titleCell.setCellValue(reportTitle)
        titleCell.cellStyle = titleStyle
        sheet.addMergedRegion(CellRangeAddress(0, 0, 0, totalColumns - 1))

        val infoRow = sheet.createRow(rowIndex++)
        val reportDateCell = infoRow.createCell(0)
        reportDateCell.setCellValue("報告日期：${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))}")
        reportDateCell.cellStyle = infoStyle
        val reportTimeCell = infoRow.createCell(6)
        reportTimeCell.setCellValue("報告時間：${LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))}")
        reportTimeCell.cellStyle = infoStyle
        val reportYearCell = infoRow.createCell(11)
        reportYearCell.setCellValue("年份：${year?.takeIf { it.isNotBlank() && it != "All" } ?: "All"}")
        reportYearCell.cellStyle = infoStyle
        sheet.addMergedRegion(CellRangeAddress(1, 1, 0, 5))
        sheet.addMergedRegion(CellRangeAddress(1, 1, 6, 10))
        sheet.addMergedRegion(CellRangeAddress(1, 1, 11, 15))

        rowIndex++ // spacer row

        val headers = listOf(
            "貨品編號",
            "貨品名稱",
            "單位",
            "一月",
            "二月",
            "三月",
            "四月",
            "五月",
            "六月",
            "七月",
            "八月",
            "九月",
            "十月",
            "十一月",
            "十二月",
            "上架總計"
        )

        val headerRow = sheet.createRow(rowIndex++)
        headers.forEachIndexed { index, name ->
            val cell = headerRow.createCell(index)
            cell.setCellValue(name)
            cell.cellStyle = headerStyle
        }

        if (dbData.isEmpty()) {
            val dataRow = sheet.createRow(rowIndex++)
            setTextCell(dataRow, 0, "-", textStyle)
            setTextCell(dataRow, 1, "-", textStyle)
            setTextCell(dataRow, 2, "-", textStyle)
            for (col in 3 until totalColumns) {
                setDashCell(dataRow, col, dashStyle)
            }
        } else {
            dbData.forEach { row ->
                val dataRow = sheet.createRow(rowIndex++)
                setTextCell(dataRow, 0, row["itemNo"]?.toString()?.ifBlank { "-" } ?: "-", textStyle)
                setTextCell(dataRow, 1, row["itemName"]?.toString()?.ifBlank { "-" } ?: "-", textStyle)
                setTextCell(dataRow, 2, row["unitOfMeasure"]?.toString()?.ifBlank { "-" } ?: "-", textStyle)
                setNumberCell(dataRow, 3, row["qtyJan"], numberStyle, dashStyle)
                setNumberCell(dataRow, 4, row["qtyFeb"], numberStyle, dashStyle)
                setNumberCell(dataRow, 5, row["qtyMar"], numberStyle, dashStyle)
                setNumberCell(dataRow, 6, row["qtyApr"], numberStyle, dashStyle)
                setNumberCell(dataRow, 7, row["qtyMay"], numberStyle, dashStyle)
                setNumberCell(dataRow, 8, row["qtyJun"], numberStyle, dashStyle)
                setNumberCell(dataRow, 9, row["qtyJul"], numberStyle, dashStyle)
                setNumberCell(dataRow, 10, row["qtyAug"], numberStyle, dashStyle)
                setNumberCell(dataRow, 11, row["qtySep"], numberStyle, dashStyle)
                setNumberCell(dataRow, 12, row["qtyOct"], numberStyle, dashStyle)
                setNumberCell(dataRow, 13, row["qtyNov"], numberStyle, dashStyle)
                setNumberCell(dataRow, 14, row["qtyDec"], numberStyle, dashStyle)
                setNumberCell(dataRow, 15, row["totalProductionQty"], numberStyle, dashStyle)
            }
        }

        val widths = intArrayOf(18, 24, 12, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12)
        widths.forEachIndexed { idx, width -> sheet.setColumnWidth(idx, width * 256) }

        addSemiFGProductionChartSheets(workbook, dbData)

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

    /**
     * Adds a visible chart sheet with:
     * - Cell B1 dropdown to choose 貨品編號 or 「全部」(sum of all items)
     * - Formulas that pull monthly qty from a very-hidden data sheet
     * - Column bar chart bound to those formula cells (updates when selection changes)
     */
    private fun addSemiFGProductionChartSheets(workbook: XSSFWorkbook, dbData: List<Map<String, Any>>) {
        val monthKeys = listOf(
            "qtyJan", "qtyFeb", "qtyMar", "qtyApr", "qtyMay", "qtyJun",
            "qtyJul", "qtyAug", "qtySep", "qtyOct", "qtyNov", "qtyDec",
        )
        val monthLabels = listOf(
            "一月", "二月", "三月", "四月", "五月", "六月",
            "七月", "八月", "九月", "十月", "十一月", "十二月",
        )

        val dataSheetName = WorkbookUtil.createSafeSheetName("圖表數據")
        val dataSheet = workbook.createSheet(dataSheetName)

        val headerRow = dataSheet.createRow(0)
        headerRow.createCell(0).setCellValue("貨品編號")
        monthLabels.forEachIndexed { i, label ->
            headerRow.createCell(1 + i).setCellValue(label)
        }

        dbData.forEachIndexed { idx, row ->
            val r = dataSheet.createRow(1 + idx)
            r.createCell(0).setCellValue(row["itemNo"]?.toString() ?: "")
            monthKeys.forEachIndexed { mi, key ->
                r.createCell(1 + mi).setCellValue(parseNumericForExcelChart(row[key]))
            }
        }

        val zColIndex = CellReference.convertColStringToIndex("Z")
        val zHeader = dataSheet.getRow(0) ?: dataSheet.createRow(0)
        zHeader.createCell(zColIndex).setCellValue("全部")
        dbData.forEachIndexed { idx, row ->
            val r = dataSheet.getRow(1 + idx) ?: dataSheet.createRow(1 + idx)
            r.createCell(zColIndex).setCellValue(row["itemNo"]?.toString() ?: "")
        }

        val lastDataRow1Based = 1 + dbData.size
        val zListEnd1Based = 1 + dbData.size

        val chartSheetName = WorkbookUtil.createSafeSheetName("生產圖表")
        val chartSheet = workbook.createSheet(chartSheetName)

        val labelR = chartSheet.createRow(0)
        labelR.createCell(0).setCellValue("選擇貨品編號")
        val filterCell = labelR.createCell(1)
        val defaultItemNo = dbData.firstOrNull()?.get("itemNo")?.toString()?.trim()
        filterCell.setCellValue(defaultItemNo?.takeIf { it.isNotBlank() } ?: "全部")

        val dvHelper: DataValidationHelper = chartSheet.dataValidationHelper
        val listFormula = "'$dataSheetName'!\$Z\$1:\$Z\$$zListEnd1Based"
        val constraint: DataValidationConstraint = dvHelper.createFormulaListConstraint(listFormula)
        val regions = CellRangeAddressList(0, 0, 1, 1)
        val validation = dvHelper.createValidation(constraint, regions)
        validation.showErrorBox = true
        validation.createErrorBox("無效選擇", "請從清單中選擇貨品編號或「全部」。")
        validation.showPromptBox = true
        validation.createPromptBox("篩選", "選擇貨品編號檢視該品項各月數量；選擇「全部」顯示所有品項加總。")
        chartSheet.addValidationData(validation)

        val catRowIndex = 3
        val valRowIndex = 4
        val firstMonthCol = 1
        val lastMonthCol = 12

        val catRow = chartSheet.createRow(catRowIndex)
        val valRow = chartSheet.createRow(valRowIndex)
        for (i in 0 until 12) {
            catRow.createCell(firstMonthCol + i).setCellValue(monthLabels[i])
            val colLetter = CellReference.convertNumToColString(firstMonthCol + i)
            val formula = if (lastDataRow1Based < 2) {
                "0"
            } else {
                "IF(TRIM(\$B\$1)=\"全部\",SUM('$dataSheetName'!$colLetter\$2:$colLetter\$$lastDataRow1Based),IFERROR(INDEX('$dataSheetName'!$colLetter\$2:$colLetter\$$lastDataRow1Based,MATCH(TRIM(\$B\$1),'$dataSheetName'!\$A\$2:\$A\$$lastDataRow1Based,0)),0))"
            }
            valRow.createCell(firstMonthCol + i).setCellFormula(formula)
        }
        catRow.createCell(lastMonthCol + 1).setCellValue("總和")
        valRow.createCell(lastMonthCol + 1).setCellFormula("SUM(B5:M5)")

        val drawing = chartSheet.createDrawingPatriarch()
        val anchor = XSSFClientAnchor(0, 0, 0, 0, 1, 6, 14, 28)
        val chart = drawing.createChart(anchor)
        chart.setTitleText("各月生產數量（依選擇之貨品編號）")
        chart.setTitleOverlay(false)

        val legend = chart.getOrAddLegend()
        legend.position = LegendPosition.BOTTOM

        val bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM)
        bottomAxis.setTitle("月份")
        val leftAxis = chart.createValueAxis(AxisPosition.LEFT)
        leftAxis.setTitle("生產數量")

        val catRangeAddr = CellRangeAddress(catRowIndex, catRowIndex, firstMonthCol, lastMonthCol)
        val valRangeAddr = CellRangeAddress(valRowIndex, valRowIndex, firstMonthCol, lastMonthCol)
        val categories = XDDFDataSourcesFactory.fromStringCellRange(chartSheet, catRangeAddr)
        val values = XDDFDataSourcesFactory.fromNumericCellRange(chartSheet, valRangeAddr)

        val barData = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis) as XDDFBarChartData
        barData.barDirection = BarDirection.COL
        val series = barData.addSeries(categories, values)
        series.setTitle("生產數量", null)
        chart.plot(barData)

        chartSheet.setColumnWidth(0, 18 * 256)
        chartSheet.setColumnWidth(1, 10 * 256)
        chartSheet.setColumnWidth(lastMonthCol + 1, 12 * 256)

        workbook.setSheetVisibility(workbook.getSheetIndex(dataSheet), SheetVisibility.VERY_HIDDEN)
        workbook.setSheetOrder(chartSheetName, 1)
    }

    private fun parseNumericForExcelChart(value: Any?): Double {
        if (value == null) return 0.0
        return when (value) {
            is Number -> value.toDouble()
            is String -> {
                val s = value.replace(",", "").trim()
                if (s.isEmpty() || s == "-") return 0.0
                s.toDoubleOrNull() ?: 0.0
            }
            else -> value.toString().replace(",", "").trim().toDoubleOrNull() ?: 0.0
        }
    }

    private fun setTextCell(
        row: org.apache.poi.ss.usermodel.Row,
        col: Int,
        value: Any?,
        style: org.apache.poi.ss.usermodel.CellStyle
    ) {
        val cell = row.createCell(col)
        cell.setCellValue(value?.toString() ?: "")
        cell.cellStyle = style
    }

    private fun setNumberCell(
        row: org.apache.poi.ss.usermodel.Row,
        col: Int,
        value: Any?,
        style: org.apache.poi.ss.usermodel.CellStyle,
        dashStyle: org.apache.poi.ss.usermodel.CellStyle
    ) {
        val cell = row.createCell(col)
        val parsed = when (value) {
            is Number -> value.toDouble()
            is String -> value.replace(",", "").toDoubleOrNull() ?: 0.0
            else -> value?.toString()?.replace(",", "")?.toDoubleOrNull() ?: 0.0
        }
        if (parsed == 0.0) {
            cell.setCellValue("-")
            cell.cellStyle = dashStyle
        } else {
            cell.setCellValue(parsed.roundToLong().toDouble())
            cell.cellStyle = style
        }
    }

    private fun setDashCell(
        row: org.apache.poi.ss.usermodel.Row,
        col: Int,
        style: org.apache.poi.ss.usermodel.CellStyle
    ) {
        val cell = row.createCell(col)
        cell.setCellValue("-")
        cell.cellStyle = style
    }

    @GetMapping("/semi-fg-item-codes")
    fun getSemiFGItemCodes(
        @RequestParam(required = false) stockCategory: String?
    ): ResponseEntity<List<Map<String, String>>> {
        val itemCodes = semiFGProductionAnalysisReportService.getSemiFGItemCodes(stockCategory)
        return ResponseEntity(itemCodes, HttpStatus.OK)
    }

    @GetMapping("/semi-fg-item-codes-with-category")
    fun getSemiFGItemCodesWithCategory(
        @RequestParam(required = false) stockCategory: String?
    ): ResponseEntity<List<Map<String, String>>> {
        val itemCodesWithCategory = semiFGProductionAnalysisReportService.getSemiFGItemCodesWithCategory(stockCategory)
        return ResponseEntity(itemCodesWithCategory, HttpStatus.OK)
    }
}
