package com.ffii.fpsms.modules.pickOrder.service

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.Sheet
import org.apache.poi.ss.usermodel.VerticalAlignment
import org.apache.poi.ss.util.CellRangeAddress
import org.apache.poi.ss.util.RegionUtil
import org.apache.poi.xssf.usermodel.XSSFCellStyle
import org.apache.poi.xssf.usermodel.XSSFFont
import org.apache.poi.xssf.usermodel.XSSFWorkbook

object TruckLaneVersionReportExcelSupport {
    const val SUMMARY_SHEET = "版本異動報告"

    private data class Styles(
        val title: XSSFCellStyle,
        val metaKey: XSSFCellStyle,
        val metaVal: XSSFCellStyle,
        val header: XSSFCellStyle,
        val normal: XSSFCellStyle,
        val added: XSSFCellStyle,
        val deleted: XSSFCellStyle,
        val moved: XSSFCellStyle,
        val edited: XSSFCellStyle,
        val highlight: XSSFCellStyle,
    )

    private fun buildStyles(wb: XSSFWorkbook): Styles {
        fun font(size: Short, bold: Boolean = false, color: Short? = null): XSSFFont {
            val f = wb.createFont() as XSSFFont
            f.fontHeightInPoints = size
            f.bold = bold
            if (color != null) f.color = color
            return f
        }

        fun style(
            align: HorizontalAlignment,
            vAlign: VerticalAlignment = VerticalAlignment.CENTER,
            bg: Short? = null,
            bold: Boolean = false,
            size: Short = 11,
            border: BorderStyle = BorderStyle.THIN,
        ): XSSFCellStyle {
            return (wb.createCellStyle() as XSSFCellStyle).apply {
                alignment = align
                verticalAlignment = vAlign
                borderTop = border
                borderBottom = border
                borderLeft = border
                borderRight = border
                if (bg != null) {
                    fillForegroundColor = bg
                    fillPattern = FillPatternType.SOLID_FOREGROUND
                }
                setFont(font(size, bold = bold))
                wrapText = true
            }
        }

        val title = style(HorizontalAlignment.CENTER, bg = IndexedColors.WHITE.index, bold = true, size = 16, border = BorderStyle.MEDIUM)
        val metaKey = style(HorizontalAlignment.LEFT, bg = IndexedColors.GREY_25_PERCENT.index, bold = true, border = BorderStyle.THIN)
        val metaVal = style(HorizontalAlignment.LEFT, bg = IndexedColors.WHITE.index, bold = false, border = BorderStyle.THIN)
        val header = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.ROYAL_BLUE.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borderTop = BorderStyle.MEDIUM
            borderBottom = BorderStyle.MEDIUM
            borderLeft = BorderStyle.MEDIUM
            borderRight = BorderStyle.MEDIUM
            setFont(font(11, bold = true, color = IndexedColors.WHITE.index))
        }
        val normal = style(HorizontalAlignment.LEFT)
        val added = style(HorizontalAlignment.LEFT, bg = IndexedColors.LIGHT_GREEN.index, border = BorderStyle.THIN)
        val deleted = style(HorizontalAlignment.LEFT, bg = IndexedColors.ROSE.index, border = BorderStyle.THIN)
        val moved = style(HorizontalAlignment.LEFT, bg = IndexedColors.LIGHT_YELLOW.index, border = BorderStyle.THIN)
        val edited = style(HorizontalAlignment.LEFT, bg = IndexedColors.GREY_25_PERCENT.index, border = BorderStyle.THIN)
        val highlight = style(HorizontalAlignment.LEFT, bg = IndexedColors.LIGHT_ORANGE.index, border = BorderStyle.THIN, bold = true)
        return Styles(title, metaKey, metaVal, header, normal, added, deleted, moved, edited, highlight)
    }

    private fun ensureRow(sheet: Sheet, r: Int) = sheet.getRow(r) ?: sheet.createRow(r)
    private fun cell(sheet: Sheet, r: Int, c: Int) = ensureRow(sheet, r).getCell(c) ?: ensureRow(sheet, r).createCell(c)

    private fun mergeRow(sheet: Sheet, r: Int, c0: Int, c1: Int, style: XSSFCellStyle) {
        for (c in c0..c1) cell(sheet, r, c).cellStyle = style
        if (c0 == c1) return
        val region = CellRangeAddress(r, r, c0, c1)
        sheet.addMergedRegion(region)
        RegionUtil.setBorderTop(BorderStyle.MEDIUM, region, sheet)
        RegionUtil.setBorderBottom(BorderStyle.MEDIUM, region, sheet)
        RegionUtil.setBorderLeft(BorderStyle.MEDIUM, region, sheet)
        RegionUtil.setBorderRight(BorderStyle.MEDIUM, region, sheet)
    }

    data class SummaryMeta(
        val title: String,
        val editor: String,
        val created: String,
        val fromVersionId: Long,
        val toVersionId: Long,
        val note: String?,
        val statsText: String,
    )

    enum class RowType { ADDED, DELETED, MOVED, EDITED }

    data class SummaryRow(
        val type: RowType,
        val shopName: String,
        val shopCode: String,
        val fromLane: String,
        val toLane: String,
        val changeText: String,
        /** 欄位名集合，用於高亮「變更資訊」cell */
        val changedFields: Set<String> = emptySet(),
    )

    fun writeSummarySheet(wb: XSSFWorkbook, meta: SummaryMeta, rows: List<SummaryRow>) {
        val st = buildStyles(wb)
        val sheet = wb.createSheet(SUMMARY_SHEET)

        // column widths
        sheet.setColumnWidth(0, 10 * 256) // type
        sheet.setColumnWidth(1, 22 * 256) // shop
        sheet.setColumnWidth(2, 12 * 256) // code
        sheet.setColumnWidth(3, 18 * 256) // from
        sheet.setColumnWidth(4, 18 * 256) // to
        sheet.setColumnWidth(5, 60 * 256) // text

        var r = 0
        mergeRow(sheet, r, 0, 5, st.title)
        cell(sheet, r, 0).setCellValue(meta.title)
        sheet.getRow(r)?.heightInPoints = 26f
        r++

        fun metaRow(k: String, v: String) {
            cell(sheet, r, 0).apply { cellStyle = st.metaKey; setCellValue(k) }
            mergeRow(sheet, r, 1, 5, st.metaVal)
            cell(sheet, r, 1).setCellValue(v)
            r++
        }

        metaRow("編輯者", meta.editor)
        metaRow("建立時間", meta.created)
        metaRow("版本", "from #${meta.fromVersionId} → to #${meta.toVersionId}")
        metaRow("摘要", meta.statsText)
        if (!meta.note.isNullOrBlank()) metaRow("備註", meta.note.trim())

        r++

        // header
        val headerRowIndex = r
        val headers = listOf("類型", "分店", "代碼", "From 車線", "To 車線", "變更資訊")
        for (c in headers.indices) {
            cell(sheet, r, c).apply { cellStyle = st.header; setCellValue(headers[c]) }
        }
        sheet.getRow(r)?.heightInPoints = 18f
        r++

        for (row in rows) {
            val baseStyle =
                when (row.type) {
                    RowType.ADDED -> st.added
                    RowType.DELETED -> st.deleted
                    RowType.MOVED -> st.moved
                    RowType.EDITED -> st.edited
                }

            fun set(c: Int, v: String, highlight: Boolean = false) {
                cell(sheet, r, c).apply {
                    cellStyle = if (highlight) st.highlight else baseStyle
                    setCellValue(v)
                }
            }

            set(0, row.type.name)
            set(1, row.shopName)
            set(2, row.shopCode)
            set(3, row.fromLane, highlight = row.type == RowType.MOVED || row.type == RowType.ADDED || row.type == RowType.DELETED)
            set(4, row.toLane, highlight = row.type == RowType.MOVED || row.type == RowType.ADDED || row.type == RowType.DELETED)
            set(5, row.changeText, highlight = row.changedFields.isNotEmpty())
            r++
        }

        sheet.createFreezePane(0, headerRowIndex + 1)
    }
}

