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 RouteReportExcelSupport {
    const val SHEET_NAME = "車線Report"
    const val BLOCK_WIDTH = 2 // 每間物流公司一個 block：2 欄

    data class Styles(
        val title: XSSFCellStyle,
        val titlePreparedBy: XSSFCellStyle,
        val company: XSSFCellStyle,
        val plate: XSSFCellStyle,
        val timeHeader: XSSFCellStyle,
        val laneLeft: XSSFCellStyle,
        val laneFill: XSSFCellStyle,
        val district: XSSFCellStyle,
        val shopNo: XSSFCellStyle,
        val shopText: XSSFCellStyle,
        val total: XSSFCellStyle,
        val driverLabel: XSSFCellStyle,
        val driverValue: XSSFCellStyle,
    )

    private fun borders(st: XSSFCellStyle, border: BorderStyle = BorderStyle.THIN) {
        st.borderTop = border
        st.borderBottom = border
        st.borderLeft = border
        st.borderRight = border
    }

    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
        }

        val title = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            borders(this, BorderStyle.MEDIUM)
            setFont(font(16, bold = true))
        }

        val titlePreparedBy = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.CENTER
            borders(this, BorderStyle.MEDIUM)
            setFont(font(11, bold = true))
        }

        val company = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.GREY_25_PERCENT.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borders(this, BorderStyle.MEDIUM)
            setFont(font(12, bold = true))
        }

        val plate = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            borders(this, BorderStyle.THIN)
            setFont(font(11, bold = true))
        }

        val timeHeader = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.LIGHT_YELLOW.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borders(this, BorderStyle.MEDIUM)
            setFont(font(11, bold = true))
        }

        val laneLeft = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.GREY_40_PERCENT.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borders(this, BorderStyle.MEDIUM)
            setFont(font(11, bold = true, color = IndexedColors.WHITE.index))
        }

        val laneFill = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.GREY_25_PERCENT.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borders(this, BorderStyle.MEDIUM)
            setFont(font(11, bold = true))
        }

        val district = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.LIGHT_CORNFLOWER_BLUE.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borders(this, BorderStyle.THIN)
            setFont(font(11, bold = true))
        }

        val shopNo = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.RIGHT
            verticalAlignment = VerticalAlignment.TOP
            borders(this, BorderStyle.THIN)
            setFont(font(11, bold = true))
        }

        val shopText = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.TOP
            wrapText = true
            borders(this, BorderStyle.THIN)
            setFont(font(11))
        }

        val total = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.GREY_25_PERCENT.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borders(this, BorderStyle.MEDIUM)
            setFont(font(11, bold = true))
        }

        val driverLabel = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.CENTER
            verticalAlignment = VerticalAlignment.CENTER
            fillForegroundColor = IndexedColors.GREY_40_PERCENT.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            borders(this, BorderStyle.MEDIUM)
            setFont(font(11, bold = true, color = IndexedColors.WHITE.index))
        }

        val driverValue = (wb.createCellStyle() as XSSFCellStyle).apply {
            alignment = HorizontalAlignment.LEFT
            verticalAlignment = VerticalAlignment.CENTER
            borders(this, BorderStyle.MEDIUM)
            setFont(font(11, bold = true))
        }

        return Styles(
            title = title,
            titlePreparedBy = titlePreparedBy,
            company = company,
            plate = plate,
            timeHeader = timeHeader,
            laneLeft = laneLeft,
            laneFill = laneFill,
            district = district,
            shopNo = shopNo,
            shopText = shopText,
            total = total,
            driverLabel = driverLabel,
            driverValue = driverValue,
        )
    }

    private fun ensureRow(sheet: Sheet, r: Int) = sheet.getRow(r) ?: sheet.createRow(r)
    private fun ensureCell(sheet: Sheet, r: Int, c: Int) =
        ensureRow(sheet, r).let { row -> row.getCell(c) ?: row.createCell(c) }

    fun styleRange(
        sheet: Sheet,
        row: Int,
        firstCol: Int,
        lastCol: Int,
        style: XSSFCellStyle,
    ) {
        for (c in firstCol..lastCol) {
            ensureCell(sheet, row, c).cellStyle = style
        }
    }

    fun mergeAndStyle(
        sheet: Sheet,
        row: Int,
        firstCol: Int,
        lastCol: Int,
        style: XSSFCellStyle,
        border: BorderStyle = BorderStyle.MEDIUM,
    ) {
        for (c in firstCol..lastCol) {
            ensureCell(sheet, row, c).cellStyle = style
        }
        // POI 不允許 merge 單一 cell（需 2+ cells）。此時只套 style + cell border 即可。
        if (firstCol == lastCol) return
        val region = CellRangeAddress(row, row, firstCol, lastCol)
        sheet.addMergedRegion(region)
        RegionUtil.setBorderTop(border, region, sheet)
        RegionUtil.setBorderBottom(border, region, sheet)
        RegionUtil.setBorderLeft(border, region, sheet)
        RegionUtil.setBorderRight(border, region, sheet)
    }

    fun applyColumnWidths(sheet: Sheet, blockIndex: Int) {
        val base = blockIndex * BLOCK_WIDTH
        sheet.setColumnWidth(base + 0, 10 * 256)
        sheet.setColumnWidth(base + 1, 26 * 256)
    }

    fun writeTitle(
        sheet: Sheet,
        st: Styles,
        titleText: String,
        preparedByText: String,
        totalBlocks: Int,
    ) {
        val lastCol = (totalBlocks * BLOCK_WIDTH - 1).coerceAtLeast(0)
        val r = 0
        // 預留右邊 2 欄顯示「製表: xxx」
        val preparedCols = 1.coerceAtMost(lastCol + 1)
        val preparedFirstCol = (lastCol - preparedCols + 1).coerceAtLeast(0)
        val titleLastCol = (preparedFirstCol - 1).coerceAtLeast(0)

        if (preparedFirstCol == 0) {
            // 欄位不足：整行仍以 title style 輸出（避免 merge 範圍倒轉）
            mergeAndStyle(sheet, r, 0, lastCol, st.title, BorderStyle.MEDIUM)
            ensureCell(sheet, r, 0).setCellValue("$titleText  $preparedByText")
        } else {
            mergeAndStyle(sheet, r, 0, titleLastCol, st.title, BorderStyle.MEDIUM)
            ensureCell(sheet, r, 0).setCellValue(titleText)

            mergeAndStyle(sheet, r, preparedFirstCol, lastCol, st.titlePreparedBy, BorderStyle.MEDIUM)
            ensureCell(sheet, r, preparedFirstCol).setCellValue(preparedByText)
        }
        sheet.getRow(r)?.heightInPoints = 26f
    }

    data class BlockMeta(
        val companyName: String,
        val plate: String,
        val driverName: String,
        val driverNumber: String,
    )

    /**
     * @return 最後寫到的 row index（含）
     */
    fun writeCompanyBlock(
        sheet: Sheet,
        st: Styles,
        blockIndex: Int,
        startRow: Int,
        meta: BlockMeta,
        groups: List<TimeGroup>,
        totalShopCount: Int,
    ): Int {
        val baseCol = blockIndex * BLOCK_WIDTH
        applyColumnWidths(sheet, blockIndex)

        var r = startRow

        // 公司名
        mergeAndStyle(sheet, r, baseCol, baseCol + 1, st.company, BorderStyle.MEDIUM)
        ensureCell(sheet, r, baseCol).setCellValue(meta.companyName)
        sheet.getRow(r)?.heightInPoints = 18f
        r++

        // 車牌
        mergeAndStyle(sheet, r, baseCol, baseCol + 1, st.plate, BorderStyle.THIN)
        ensureCell(sheet, r, baseCol).setCellValue(meta.plate)
        r++

        for (tg in groups) {
            mergeAndStyle(sheet, r, baseCol, baseCol + 1, st.timeHeader, BorderStyle.MEDIUM)
            ensureCell(sheet, r, baseCol).setCellValue(tg.timeLabel)
            r++

            for (lg in tg.lanes) {
                // 車線標題：左一格強調，右三格補底
                ensureCell(sheet, r, baseCol).apply {
                    cellStyle = st.laneLeft
                    setCellValue(lg.laneCode)
                }
                // 2 欄版：右側只剩 1 格（不 merge）
                ensureCell(sheet, r, baseCol + 1).cellStyle = st.laneFill
                r++

                for (dg in lg.districts) {
                    mergeAndStyle(sheet, r, baseCol, baseCol + 1, st.district, BorderStyle.THIN)
                    ensureCell(sheet, r, baseCol).setCellValue(dg.districtLabel)
                    r++

                    var idx = 1
                    for (s in dg.shops) {
                        ensureCell(sheet, r, baseCol).apply {
                            cellStyle = st.shopNo
                            setCellValue("$idx.")
                        }
                        // shop row 不做 merge：避免 merged regions 爆量導致寫檔/開檔變慢
                        styleRange(sheet, r, baseCol + 1, baseCol + 1, st.shopText)
                        ensureCell(sheet, r, baseCol + 1).setCellValue(s)
                        val lines = (s.count { it == '\n' } + 1).coerceAtLeast(1)
                        val h = (16f * lines).coerceIn(18f, 72f)
                        sheet.getRow(r)?.heightInPoints = h
                        r++
                        idx++
                    }
                }
            }
        }

        // 分店數目
        mergeAndStyle(sheet, r, baseCol, baseCol + 1, st.total, BorderStyle.MEDIUM)
        ensureCell(sheet, r, baseCol).setCellValue("分店數目：$totalShopCount")
        r++

        // 車長 / driver
        ensureCell(sheet, r, baseCol).cellStyle = st.driverLabel
        ensureCell(sheet, r, baseCol).setCellValue("車長")
        ensureCell(sheet, r, baseCol + 1).cellStyle = st.driverValue
        ensureCell(sheet, r, baseCol + 1).setCellValue(meta.driverName)
        r++

        // driver number
        // 2 欄版：電話/司機號碼跨兩欄合併成一格（像截圖的大白格）
        mergeAndStyle(sheet, r, baseCol, baseCol + 1, st.driverValue, BorderStyle.MEDIUM)
        ensureCell(sheet, r, baseCol).setCellValue(meta.driverNumber)
        r++

        return r - 1
    }

    data class TimeGroup(
        val timeLabel: String,
        val lanes: List<LaneGroup>,
    )

    data class LaneGroup(
        val laneCode: String,
        val districts: List<DistrictGroup>,
    )

    data class DistrictGroup(
        val districtLabel: String,
        val shops: List<String>,
    )
}

