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

/**
 * 版本 Log 用：輸出「車線報告」版面（同正常 RouteReport），但把異動的 shop row 高亮。
 *
 * 2 欄 block：左序號 / label、右內容。
 */
object TruckLaneVersionRouteReportExcelSupport {
    const val SHEET_NAME = "車線報告(版本)"
    const val BLOCK_WIDTH = 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 shopNoChanged: XSSFCellStyle,
        val shopTextChanged: XSSFCellStyle,
        val total: XSSFCellStyle,
        val driverLabel: XSSFCellStyle,
        val driverValue: XSSFCellStyle,
    )

    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 borders(st: XSSFCellStyle, border: BorderStyle) {
            st.borderTop = border
            st.borderBottom = border
            st.borderLeft = border
            st.borderRight = border
        }

        fun baseCell(
            align: HorizontalAlignment,
            bg: Short? = null,
            bold: Boolean = false,
            size: Short = 11,
            border: BorderStyle = BorderStyle.THIN,
            wrap: Boolean = false,
        ): XSSFCellStyle {
            return (wb.createCellStyle() as XSSFCellStyle).apply {
                alignment = align
                verticalAlignment = VerticalAlignment.CENTER
                borders(this, border)
                if (bg != null) {
                    fillForegroundColor = bg
                    fillPattern = FillPatternType.SOLID_FOREGROUND
                }
                setFont(font(size, bold = bold))
                wrapText = wrap
            }
        }

        val title = baseCell(HorizontalAlignment.CENTER, bold = true, size = 16, border = BorderStyle.MEDIUM)
        val titlePreparedBy = baseCell(HorizontalAlignment.RIGHT, bold = true, size = 11, border = BorderStyle.MEDIUM)
        val company = baseCell(
            HorizontalAlignment.CENTER,
            bg = IndexedColors.GREY_25_PERCENT.index,
            bold = true,
            size = 12,
            border = BorderStyle.MEDIUM,
        )
        val plate = baseCell(HorizontalAlignment.CENTER, bold = true, border = BorderStyle.THIN)
        val timeHeader = baseCell(
            HorizontalAlignment.CENTER,
            bg = IndexedColors.LIGHT_YELLOW.index,
            bold = true,
            border = BorderStyle.MEDIUM,
        )
        val laneLeft = baseCell(
            HorizontalAlignment.CENTER,
            bg = IndexedColors.GREY_40_PERCENT.index,
            bold = true,
            border = BorderStyle.MEDIUM,
        ).apply { setFont(font(11, bold = true, color = IndexedColors.WHITE.index)) }

        val laneFill = baseCell(
            HorizontalAlignment.LEFT,
            bg = IndexedColors.GREY_25_PERCENT.index,
            bold = true,
            border = BorderStyle.MEDIUM,
        )

        val district = baseCell(
            HorizontalAlignment.LEFT,
            bg = IndexedColors.LIGHT_CORNFLOWER_BLUE.index,
            bold = true,
            border = BorderStyle.THIN,
        )

        val shopNo = baseCell(HorizontalAlignment.RIGHT, border = BorderStyle.THIN).apply {
            verticalAlignment = VerticalAlignment.TOP
            setFont(font(11, bold = true))
        }
        val shopText = baseCell(HorizontalAlignment.LEFT, border = BorderStyle.THIN, wrap = true).apply {
            verticalAlignment = VerticalAlignment.TOP
        }

        val shopNoChanged = (wb.createCellStyle() as XSSFCellStyle).apply {
            cloneStyleFrom(shopNo)
            fillForegroundColor = IndexedColors.LIGHT_ORANGE.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
        }
        val shopTextChanged = (wb.createCellStyle() as XSSFCellStyle).apply {
            cloneStyleFrom(shopText)
            fillForegroundColor = IndexedColors.LIGHT_ORANGE.index
            fillPattern = FillPatternType.SOLID_FOREGROUND
            val f = wb.createFont()
            f.fontHeightInPoints = 11
            f.bold = true
            setFont(f)
        }

        val total = baseCell(HorizontalAlignment.LEFT, bg = IndexedColors.GREY_25_PERCENT.index, bold = true, border = BorderStyle.MEDIUM)
        val driverLabel = baseCell(
            HorizontalAlignment.CENTER,
            bg = IndexedColors.GREY_40_PERCENT.index,
            bold = true,
            border = BorderStyle.MEDIUM,
        ).apply { setFont(font(11, bold = true, color = IndexedColors.WHITE.index)) }
        val driverValue = baseCell(HorizontalAlignment.LEFT, bold = true, border = BorderStyle.MEDIUM)

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

    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 mergeAndStyle(sheet: Sheet, r: Int, c0: Int, c1: Int, style: XSSFCellStyle, border: BorderStyle) {
        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(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, 30 * 256)
    }

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

    data class ShopRow(
        val truckRowId: Long,
        val text: String,
        val changed: Boolean,
    )

    data class DistrictGroup(
        val district: String,
        val shops: List<ShopRow>,
    )

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

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

    fun writeTitle(sheet: Sheet, st: Styles, titleText: String, preparedByText: String, totalBlocks: Int) {
        val lastCol = (totalBlocks * BLOCK_WIDTH - 1).coerceAtLeast(0)
        val r = 0
        val preparedCols = 1.coerceAtMost(lastCol + 1)
        val preparedFirstCol = (lastCol - preparedCols + 1).coerceAtLeast(0)
        val titleLastCol = (preparedFirstCol - 1).coerceAtLeast(0)

        if (preparedFirstCol == 0) {
            mergeAndStyle(sheet, r, 0, lastCol, st.title, BorderStyle.MEDIUM)
            cell(sheet, r, 0).setCellValue("$titleText  $preparedByText")
        } else {
            mergeAndStyle(sheet, r, 0, titleLastCol, st.title, BorderStyle.MEDIUM)
            cell(sheet, r, 0).setCellValue(titleText)
            mergeAndStyle(sheet, r, preparedFirstCol, lastCol, st.titlePreparedBy, BorderStyle.MEDIUM)
            cell(sheet, r, preparedFirstCol).setCellValue(preparedByText)
        }
        sheet.getRow(r)?.heightInPoints = 26f
    }

    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)
        cell(sheet, r, baseCol).setCellValue(meta.companyName)
        r++

        mergeAndStyle(sheet, r, baseCol, baseCol + 1, st.plate, BorderStyle.THIN)
        cell(sheet, r, baseCol).setCellValue(meta.plate)
        r++

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

            for (lg in tg.lanes) {
                cell(sheet, r, baseCol).apply { cellStyle = st.laneLeft; setCellValue(lg.laneLabel) }
                cell(sheet, r, baseCol + 1).cellStyle = st.laneFill
                r++

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

                    var idx = 1
                    for (s in dg.shops) {
                        val noStyle = if (s.changed) st.shopNoChanged else st.shopNo
                        val txtStyle = if (s.changed) st.shopTextChanged else st.shopText
                        cell(sheet, r, baseCol).apply { cellStyle = noStyle; setCellValue("$idx.") }
                        cell(sheet, r, baseCol + 1).apply { cellStyle = txtStyle; setCellValue(s.text) }
                        val lines = (s.text.count { it == '\n' } + 1).coerceAtLeast(1)
                        sheet.getRow(r)?.heightInPoints = (16f * lines).coerceIn(18f, 90f)
                        r++
                        idx++
                    }
                }
            }
        }

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

        cell(sheet, r, baseCol).apply { cellStyle = st.driverLabel; setCellValue("車長") }
        cell(sheet, r, baseCol + 1).apply { cellStyle = st.driverValue; setCellValue(meta.driverName) }
        r++

        mergeAndStyle(sheet, r, baseCol, baseCol + 1, st.driverValue, BorderStyle.MEDIUM)
        cell(sheet, r, baseCol).setCellValue(meta.driverNumber)
        r++

        return r - 1
    }
}

