package cn.iocoder.lyzsys.module.tjt.service.report.builder;

import cn.iocoder.lyzsys.framework.common.util.http.HttpUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;

public abstract class AbstractProjectOutputExcelBuilder {

    private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
    private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP);

    protected Workbook createWorkbook() {
        return new XSSFWorkbook();
    }

    protected Sheet createSheet(Workbook workbook, String preferredName, String fallbackName) {
        return workbook.createSheet(buildSheetName(preferredName, fallbackName));
    }

    protected CellStyle createTitleStyle(Workbook workbook) {
        CellStyle style = createBaseStyle(workbook);
        Font font = workbook.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 14);
        style.setFont(font);
        style.setAlignment(HorizontalAlignment.CENTER);
        return style;
    }

    protected CellStyle createInfoStyle(Workbook workbook) {
        CellStyle style = createBaseStyle(workbook);
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 10);
        style.setFont(font);
        return style;
    }

    protected CellStyle createHeaderStyle(Workbook workbook) {
        CellStyle style = createBaseStyle(workbook);
        Font font = workbook.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 10);
        style.setFont(font);
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style.setAlignment(HorizontalAlignment.CENTER);
        return style;
    }

    protected CellStyle createCellStyle(Workbook workbook) {
        CellStyle style = createBaseStyle(workbook);
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 10);
        style.setFont(font);
        return style;
    }

    protected CellStyle createDerivedStyle(Workbook workbook, CellStyle sourceStyle,
                                           IndexedColors fillColor, HorizontalAlignment alignment,
                                           boolean bold, short fontHeightInPoints) {
        CellStyle style = workbook.createCellStyle();
        style.cloneStyleFrom(sourceStyle);
        if (fillColor != null) {
            style.setFillForegroundColor(fillColor.getIndex());
            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        }
        if (alignment != null) {
            style.setAlignment(alignment);
        }
        Font font = workbook.createFont();
        font.setBold(bold);
        font.setFontHeightInPoints(fontHeightInPoints);
        style.setFont(font);
        return style;
    }

    protected void setText(Row row, int cellIndex, Object value, CellStyle style) {
        Cell cell = row.createCell(cellIndex);
        cell.setCellValue(value == null ? "" : String.valueOf(value));
        if (style != null) {
            cell.setCellStyle(style);
        }
    }

    protected Row getOrCreateRow(Sheet sheet, int rowIndex) {
        Row row = sheet.getRow(rowIndex);
        return row != null ? row : sheet.createRow(rowIndex);
    }

    protected int writeMergedTitleRow(Sheet sheet, int rowIndex, String title, CellStyle style, int lastCol) {
        Row row = getOrCreateRow(sheet, rowIndex);
        setText(row, 0, title, style);
        merge(sheet, rowIndex, rowIndex, 0, lastCol);
        return rowIndex + 1;
    }

    protected int writeRow(Sheet sheet, int rowIndex, CellStyle style, Object... values) {
        Row row = getOrCreateRow(sheet, rowIndex);
        for (int i = 0; i < values.length; i++) {
            setText(row, i, values[i], style);
        }
        return rowIndex + 1;
    }

    protected void writeLabelValue(Row row, int labelCellIndex, int valueCellIndex,
                                   Object label, Object value, CellStyle style) {
        setText(row, labelCellIndex, label, style);
        setText(row, valueCellIndex, value, style);
    }

    protected void merge(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {
        sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));
    }

    protected void setMergedRegionText(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol,
                                       String text, CellStyle style) {
        for (int rowIndex = firstRow; rowIndex <= lastRow; rowIndex++) {
            Row row = getOrCreateRow(sheet, rowIndex);
            for (int cellIndex = firstCol; cellIndex <= lastCol; cellIndex++) {
                setText(row, cellIndex, rowIndex == firstRow && cellIndex == firstCol ? text : "", style);
            }
        }
        if (firstRow != lastRow || firstCol != lastCol) {
            merge(sheet, firstRow, lastRow, firstCol, lastCol);
        }
    }

    protected void setColumnWidths(Sheet sheet, int... widths) {
        for (int i = 0; i < widths.length; i++) {
            sheet.setColumnWidth(i, widths[i] * 256);
        }
    }

    protected BigDecimal amount(BigDecimal value) {
        return value == null ? ZERO_AMOUNT : value.setScale(2, RoundingMode.HALF_UP);
    }

    protected BigDecimal ratio(BigDecimal value) {
        return value == null ? ZERO_RATIO : value.setScale(4, RoundingMode.HALF_UP);
    }

    protected String percentText(BigDecimal value) {
        return ratio(value).multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP) + "%";
    }

    protected String text(BigDecimal value) {
        return amount(value).toPlainString();
    }

    protected String safeText(String value) {
        return value == null ? "" : value.trim();
    }

    public void writeWorkbook(HttpServletResponse response, String fileName, Workbook workbook) throws IOException {
        try {
            response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(fileName));
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            workbook.write(response.getOutputStream());
            response.flushBuffer();
        } finally {
            workbook.close();
        }
    }

    private CellStyle createBaseStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setWrapText(true);
        return style;
    }

    private String buildSheetName(String preferred, String fallback) {
        String sheetName = sanitizeSheetName(preferred);
        if (sheetName.isEmpty()) {
            sheetName = sanitizeSheetName(fallback);
        }
        if (sheetName.length() > 31) {
            sheetName = sheetName.substring(0, 31);
        }
        return sheetName.isEmpty() ? "Sheet1" : sheetName;
    }

    private String sanitizeSheetName(String value) {
        return safeText(value).replaceAll("[\\\\/?*\\[\\]:]", "_");
    }

}
