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

import net.sf.jasperreports.engine.*
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
import org.springframework.http.*
import org.springframework.web.bind.annotation.*
import java.io.InputStream
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import com.ffii.fpsms.modules.common.SecurityUtils
import com.ffii.fpsms.modules.report.service.ReportService

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

    @GetMapping("/stock-take-rounds")
    fun getStockTakeRounds(): List<Map<String, Any>> =
        reportService.getStockTakeRoundOptions()

    @GetMapping("/print-report1")
    fun generateReport1(
        @RequestParam fromDate: String,
        @RequestParam toDate: String,
        //this is an exampe of optional param
        @RequestParam(required = false) itemType: String?
    ): ResponseEntity<ByteArray> {
        val parameters = mutableMapOf<String, Any>()
        parameters["fromDate"] = fromDate
        parameters["toDate"] = toDate
        // you may put more params to show
        parameters["param1"] = "param1Value"
        parameters["param2"] = "param2Value"

        // Query the DB to get a list of data
        val dbData = reportService.searchReport1(fromDate, toDate, itemType)

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

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

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

    @GetMapping("/print-report3")
    fun generateReport3(
        //@RequestParam fromDate: String,
       // @RequestParam toDate: String,
        //this is an exampe of optional param
        @RequestParam(required = false) itemType: String?
    ): ResponseEntity<ByteArray> {
        val parameters = mutableMapOf<String, Any>()
        //parameters["lastInDateStart"] = fromDate
        //parameters["lastInDateEnd"] = toDate
        // you may put more params to show
        parameters["lastOutDateStart"] = "param1Value"
        parameters["lastOutDateEnd"] = "param2Value"

        // Query the DB to get a list of data
        val dbData = reportService.searchReport3(itemType)

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

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

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

    @GetMapping("/print-stock-in-traceability")
    fun generateStockInTraceabilityReport(
        @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>()
        
        // Set report header parameters
        parameters["itemNo"] = itemCode ?: "All"
        parameters["reportDate"] = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
        parameters["reportTime"] = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
        parameters["lastInDateStart"] = lastInDateStart ?: ""
        parameters["lastInDateEnd"] = lastInDateEnd ?: ""

        // Query the DB to get a list of data
        val dbData = reportService.searchStockInTraceabilityReport(
            stockCategory,
            itemCode,
            lastInDateStart,
            lastInDateEnd
        )

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

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

        return ResponseEntity(pdfBytes, headers, HttpStatus.OK)
    }
    @GetMapping("/print-fg-delivery-report")
    fun generateFGDeliveryReport(
        @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>()
        
        // 设置报表头部参数
        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"] = ""
    
        val dbData = reportService.searchFGDeliveryReport(
            stockCategory, 
            stockSubCategory, 
            itemCode, 
            year, 
            lastOutDateStart, 
            lastOutDateEnd
        )
    
        val pdfBytes = reportService.createPdfResponse(
            "/jasper/FGDeliveryReport.jrxml", 
            parameters, 
            dbData
        )
    
        val headers = HttpHeaders().apply {
            contentType = MediaType.APPLICATION_PDF
            setContentDispositionFormData("attachment", "FGDeliveryReport.pdf")
            set("filename", "FGDeliveryReport.pdf") 
        }
    
        return ResponseEntity(pdfBytes, headers, HttpStatus.OK)
    }
    @GetMapping("/print-stock-balance")
    fun generateStockBalanceReport(
        @RequestParam(required = false) stockCategory: String?,
        @RequestParam(required = false) itemCode: String?,
        @RequestParam(required = false) stockDate: String?,
        @RequestParam(required = false) balanceFilterStart: String?,
        @RequestParam(required = false) balanceFilterEnd: String?,
        @RequestParam(required = false) storeLocation: String?,
        @RequestParam(required = false) lastInDateStart: String?,
        @RequestParam(required = false) lastInDateEnd: String?,
        @RequestParam(required = false) lastOutDateStart: String?,
        @RequestParam(required = false) lastOutDateEnd: String?,
        @RequestParam(required = false, defaultValue = "0") stockTakeRoundId: Long,
    ): ResponseEntity<ByteArray> {
        val parameters = mutableMapOf<String, Any>()
        parameters["stockCategory"] = stockCategory ?: "All"
        parameters["itemNo"] = itemCode ?: "All"
        parameters["reportDate"] = (stockDate ?: LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
        parameters["reportTime"] = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
        parameters["storeLocation"] = storeLocation ?: ""
        parameters["balanceFilterStart"] = balanceFilterStart ?: ""
        parameters["balanceFilterEnd"] = balanceFilterEnd ?: ""
        parameters["lastInDateStart"] = lastInDateStart ?: ""
        parameters["lastInDateEnd"] = lastInDateEnd ?: ""
        parameters["lastOutDateStart"] = lastOutDateStart ?: ""
        parameters["lastOutDateEnd"] = lastOutDateEnd ?: ""

        val dbData =
            if (!stockDate.isNullOrBlank()) {
                reportService.searchStockBalanceReportByDate(
                    stockCategory = stockCategory,
                    itemCode = itemCode,
                    stockDate = stockDate,
                    balanceFilterStart = balanceFilterStart,
                    balanceFilterEnd = balanceFilterEnd,
                    storeLocation = storeLocation,
                )
            } else {
                reportService.searchStockBalanceReport(
                    stockCategory,
                    itemCode,
                    balanceFilterStart,
                    balanceFilterEnd,
                    storeLocation,
                    lastInDateStart,
                    lastInDateEnd,
                    lastOutDateStart,
                    lastOutDateEnd,
                    stockTakeRoundId
                )
            }

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

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

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

    /**
     * GRN (Goods Received Note) report data for Excel export.
     * Query by receipt date range and optional item code. Returns JSON { "rows": [ ... ] }.
     */
    @GetMapping("/grn-report")
    fun getGrnReport(
        @RequestParam(required = false) receiptDateStart: String?,
        @RequestParam(required = false) receiptDateEnd: String?,
        @RequestParam(required = false) itemCode: String?,
        @RequestParam(required = false) supplier: String?,
        @RequestParam(required = false) poCode: String?,
        @RequestParam(required = false) grnCode: String?,
    ): Map<String, Any> {
        val rows = reportService.searchGrnReport(
            receiptDateStart,
            receiptDateEnd,
            itemCode,
            supplier,
            poCode,
            grnCode,
        )
        val isAdmin = SecurityUtils.isGranted("ADMIN")
        val sanitizedRows =
            if (isAdmin) {
                rows
            } else {
                rows.map { row ->
                    row.filterKeys { it !in GRN_FINANCIAL_KEYS }
                }
            }
        val listedPoAmounts =
            if (isAdmin) {
                reportService.searchGrnListedPoAmounts(
                    receiptDateStart, 
                    receiptDateEnd, 
                    itemCode,
                    supplier,
                    poCode,
                    grnCode,
                    )
            } else {
                mapOf(
                    "currencyTotals" to emptyList<Map<String, Any?>>(),
                    "byPurchaseOrder" to emptyList<Map<String, Any?>>(),
                )
            }
        return mapOf(
            "rows" to sanitizedRows,
            "listedPoAmounts" to listedPoAmounts,
        )
    }

    companion object {
        /** GRN report fields only users with ADMIN authority may see */
        private val GRN_FINANCIAL_KEYS = setOf("unitPrice", "lineAmount", "currencyCode")
    }

    /**
     * DRY-RUN GRN preview for M18 (shows stock qty vs converted purchase qty).
     *
     * Example: `/report/grn-preview-m18?receiptDate=2026-03-16`
     * CSV (Excel-openable): `/report/grn-preview-m18?receiptDate=2026-03-16&format=csv`
     */
    @GetMapping("/grn-preview-m18")
    fun getGrnPreviewM18(
        @RequestParam receiptDate: String,
        @RequestParam(required = false, defaultValue = "json") format: String,
    ): Any {
        val rows = reportService.searchGrnPreviewM18(receiptDate)
        if (format.equals("csv", ignoreCase = true)) {
            val headers = listOf(
                "receiptDate",
                "poCode",
                "deliveryNoteNo",
                "stockInLineId",
                "purchaseOrderLineId",
                "itemCode",
                "itemName",
                "stockQty",
                "purchaseQty",
                "stockUomDesc",
                "purchaseUomDesc",
                "status",
            )
            val sb = StringBuilder()
            sb.append(headers.joinToString(",")).append("\n")
            rows.forEach { r ->
                val line = headers.joinToString(",") { h ->
                    val v = r[h]
                    val s = (v?.toString() ?: "").replace("\"", "\"\"")
                    "\"$s\""
                }
                sb.append(line).append("\n")
            }
            val bytes = sb.toString().toByteArray(Charsets.UTF_8)
            val httpHeaders = HttpHeaders().apply {
                contentType = MediaType("text", "csv")
                set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=grn-preview-m18-$receiptDate.csv")
            }
            return ResponseEntity(bytes, httpHeaders, HttpStatus.OK)
        }
        return mapOf("rows" to rows)
    }

}