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

import lombok.Data;
import org.apache.poi.ss.usermodel.CellStyle;
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.Workbook;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Component
public class ProjectQuarterOutputExcelBuilder extends AbstractProjectOutputExcelBuilder {

    private static final String SHEET1_NAME = "专业间项目考核产值年度季度计取表";
    private static final String SHEET1_FALLBACK_NAME = "项目级年度季度计取表";

    private static final String SHEET2_NAME = "专业维度统计表";
    private static final String SHEET2_FALLBACK_NAME = "专业维度统计";

    private static final String TOTAL_LABEL = "合计";
    private static final int SHEET1_SPECIALTY_RATIO_START_COL = 23;
    private static final int SHEET1_SPECIALTY_AMOUNT_START_COL = 30;
    private static final int SHEET2_BASE_COL_COUNT = 4;
    private static final int SHEET2_SPECIALTY_BLOCK_WIDTH = 5;

    private static final List<String> OUTPUT_TYPE_ORDER = Arrays.asList(
            "六大专业考核产值",
            "专项分包产值",
            "源头合作分包产值",
            "内部协作产值",
            "其他产值"
    );

    private static final List<String> FIXED_OUTPUT_TYPES = Arrays.asList(
            "六大专业考核产值",
            "专项分包产值",
            "源头合作分包产值",
            "内部协作产值"
    );

    private static final List<String> SPECIALTY_NAMES = Arrays.asList(
            "建筑",
            "精装",
            "结构",
            "给排水",
            "暖通",
            "电气",
            "数字化设计"
    );

    private static final List<String> SHEET2_SIGNATURE_LABELS = Arrays.asList(
            "建筑专业负责人（签名）",
            "精装专业负责人（签名）",
            "结构专业负责人（签名）",
            "给排水专业负责人（签名）",
            "暖通专业负责人（签名）",
            "电气专业负责人（签名）",
            "数字化设计专业负责人（签名）"
    );

    public Workbook build(ExportData data) {
        Workbook workbook = createWorkbook();
        buildSheet1(workbook, data);
        buildSheet2(workbook, data);
        return workbook;
    }

    private void buildSheet1(Workbook workbook, ExportData data) {
        Sheet sheet = createSheet(workbook, SHEET1_NAME, SHEET1_FALLBACK_NAME);
        setColumnWidths(sheet,
                10, 24, 16, 20, 12, 12, 12, 12, 14,
                10, 14, 12, 12, 12, 12, 14,
                10, 14, 12, 12, 12, 12, 14,
                10, 10, 10, 10, 10, 10, 10,
                12, 12, 12, 12, 12, 12, 12);

        CellStyle baseHeaderStyle = createHeaderStyle(workbook);
        CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
                IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
        CellStyle cellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
                null, HorizontalAlignment.CENTER, false, (short) 10);
        CellStyle leftCellStyle = createDerivedStyle(workbook, cellStyle,
                null, HorizontalAlignment.LEFT, false, (short) 10);
        CellStyle totalStyle = createDerivedStyle(workbook, baseHeaderStyle,
                IndexedColors.GOLD, HorizontalAlignment.CENTER, true, (short) 10);
        CellStyle totalLeftStyle = createDerivedStyle(workbook, totalStyle,
                IndexedColors.GOLD, HorizontalAlignment.LEFT, true, (short) 10);
        CellStyle signatureStyle = createDerivedStyle(workbook, createInfoStyle(workbook),
                null, HorizontalAlignment.LEFT, false, (short) 10);

        int rowIndex = 0;
        rowIndex = buildSheet1Header(sheet, rowIndex, data, headerStyle, cellStyle);

        int bodyStartRow = rowIndex;
        List<OutputTypeGroup> groups = buildOutputTypeGroups(data);
        for (OutputTypeGroup group : groups) {
            int groupStartRow = rowIndex;
            for (QuarterRow rowData : group.getRows()) {
                writeSheet1DetailRow(sheet, rowIndex++, data, rowData, cellStyle, leftCellStyle);
            }
            mergeOutputTypeColumn(sheet, groupStartRow, rowIndex - 1, group.getOutputType(), cellStyle);
        }

        QuarterRow totalRow = resolveProjectTotalRow(data);
        int totalRowIndex = -1;
        if (totalRow != null) {
            totalRowIndex = rowIndex;
            writeSheet1TotalRow(sheet, rowIndex++, data, totalRow, totalStyle, totalLeftStyle);
        }

        mergeProjectNameColumn(sheet, bodyStartRow, totalRowIndex >= 0 ? totalRowIndex : rowIndex - 1,
                safeText(data.getProjectName()), cellStyle);

        writeSheet1SignatureRow(sheet, rowIndex, data, signatureStyle);
    }

    private int buildSheet1Header(Sheet sheet, int rowIndex, ExportData data,
                                  CellStyle headerStyle, CellStyle valueStyle) {
        int firstRow = buildCommonLeftHeader(sheet, rowIndex, data, headerStyle, valueStyle);

        setMergedRegionText(sheet, firstRow, firstRow + 1, 4, 8, "本年度项目考核产值（万元）", headerStyle);
        setText(sheet.getRow(firstRow + 2), 4, "一季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 5, "二季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 6, "三季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 7, "四季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 8, "本年度小计", headerStyle);

        setMergedRegionText(sheet, firstRow, firstRow, 9, 15, "项目经理 / 工程负责人年度 / 季度项目考核产值", headerStyle);
        setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 9, 10, "项总 / 工程负责人", headerStyle);
        setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 11, 15, "考核产值（万元）", headerStyle);
        setText(sheet.getRow(firstRow + 2), 9, "占比", headerStyle);
        setText(sheet.getRow(firstRow + 2), 10, "总考核产值", headerStyle);
        setText(sheet.getRow(firstRow + 2), 11, "一季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 12, "二季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 13, "三季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 14, "四季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 15, "本年度总计", headerStyle);

        setMergedRegionText(sheet, firstRow, firstRow, 16, 22, "六大专业年度 / 季度项目考核产值合计", headerStyle);
        setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 16, 17, "六大专业", headerStyle);
        setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 18, 22, "考核产值（万元）", headerStyle);
        setText(sheet.getRow(firstRow + 2), 16, "占比", headerStyle);
        setText(sheet.getRow(firstRow + 2), 17, "总考核产值", headerStyle);
        setText(sheet.getRow(firstRow + 2), 18, "一季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 19, "二季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 20, "三季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 21, "四季度", headerStyle);
        setText(sheet.getRow(firstRow + 2), 22, "本年度总计", headerStyle);

        setMergedRegionText(sheet, firstRow, firstRow, SHEET1_SPECIALTY_RATIO_START_COL, sheet1LastCol(),
                "各专业年度 / 季度项目考核产值", headerStyle);
        setMergedRegionText(sheet, firstRow + 1, firstRow + 1,
                SHEET1_SPECIALTY_RATIO_START_COL, SHEET1_SPECIALTY_AMOUNT_START_COL - 1,
                "各专业考核产值占比", headerStyle);
        setMergedRegionText(sheet, firstRow + 1, firstRow + 1,
                SHEET1_SPECIALTY_AMOUNT_START_COL, sheet1LastCol(),
                "各专业考核产值", headerStyle);
        for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
            setText(sheet.getRow(firstRow + 2), SHEET1_SPECIALTY_RATIO_START_COL + i, SPECIALTY_NAMES.get(i), headerStyle);
            setText(sheet.getRow(firstRow + 2), SHEET1_SPECIALTY_AMOUNT_START_COL + i, SPECIALTY_NAMES.get(i), headerStyle);
        }
        return rowIndex + 3;
    }

    private void writeSheet1DetailRow(Sheet sheet, int rowIndex, ExportData data, QuarterRow rowData,
                                      CellStyle cellStyle, CellStyle leftCellStyle) {
        Row row = sheet.createRow(rowIndex);
        setText(row, 0, rowData.getSerialNo(), cellStyle);
        setText(row, 1, safeText(data.getProjectName()), cellStyle);
        setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
        setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);
        setText(row, 4, textOrBlank(rowData.getQuarterOneAmountWan()), cellStyle);
        setText(row, 5, textOrBlank(rowData.getQuarterTwoAmountWan()), cellStyle);
        setText(row, 6, textOrBlank(rowData.getQuarterThreeAmountWan()), cellStyle);
        setText(row, 7, textOrBlank(rowData.getQuarterFourAmountWan()), cellStyle);
        setText(row, 8, textOrBlank(rowData.getYearTotalAmountWan()), cellStyle);
        setText(row, 9, percentTextOrBlank(rowData.getProjectLeadRatio()), cellStyle);
        setText(row, 10, textOrBlank(rowData.getProjectLeadAssessmentOutputWan()), cellStyle);
        setText(row, 11, textOrBlank(rowData.getProjectLeadQuarterOneAmountWan()), cellStyle);
        setText(row, 12, textOrBlank(rowData.getProjectLeadQuarterTwoAmountWan()), cellStyle);
        setText(row, 13, textOrBlank(rowData.getProjectLeadQuarterThreeAmountWan()), cellStyle);
        setText(row, 14, textOrBlank(rowData.getProjectLeadQuarterFourAmountWan()), cellStyle);
        setText(row, 15, textOrBlank(rowData.getProjectLeadYearTotalAmountWan()), cellStyle);
        setText(row, 16, percentTextOrBlank(rowData.getOfficeRatio()), cellStyle);
        setText(row, 17, textOrBlank(rowData.getOfficeAssessmentOutputWan()), cellStyle);
        setText(row, 18, textOrBlank(rowData.getOfficeQuarterOneAmountWan()), cellStyle);
        setText(row, 19, textOrBlank(rowData.getOfficeQuarterTwoAmountWan()), cellStyle);
        setText(row, 20, textOrBlank(rowData.getOfficeQuarterThreeAmountWan()), cellStyle);
        setText(row, 21, textOrBlank(rowData.getOfficeQuarterFourAmountWan()), cellStyle);
        setText(row, 22, textOrBlank(rowData.getOfficeYearTotalAmountWan()), cellStyle);
        for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
            setText(row, SHEET1_SPECIALTY_RATIO_START_COL + i,
                    percentTextOrBlank(resolveSpecialtyRatio(rowData, i)), cellStyle);
            setText(row, SHEET1_SPECIALTY_AMOUNT_START_COL + i,
                    textOrBlank(resolveSpecialtyAssessmentOutputWan(rowData, i)), cellStyle);
        }
    }

    private void writeSheet1TotalRow(Sheet sheet, int rowIndex, ExportData data, QuarterRow rowData,
                                     CellStyle totalStyle, CellStyle totalLeftStyle) {
        Row row = sheet.createRow(rowIndex);
        setText(row, 0, "", totalStyle);
        setText(row, 1, safeText(data.getProjectName()), totalStyle);
        setMergedRegionText(sheet, rowIndex, rowIndex, 2, 3, TOTAL_LABEL, totalStyle);
        setText(row, 4, textOrBlank(rowData.getQuarterOneAmountWan()), totalStyle);
        setText(row, 5, textOrBlank(rowData.getQuarterTwoAmountWan()), totalStyle);
        setText(row, 6, textOrBlank(rowData.getQuarterThreeAmountWan()), totalStyle);
        setText(row, 7, textOrBlank(rowData.getQuarterFourAmountWan()), totalStyle);
        setText(row, 8, textOrBlank(rowData.getYearTotalAmountWan()), totalStyle);
        setText(row, 9, "", totalStyle);
        setText(row, 10, textOrBlank(rowData.getProjectLeadAssessmentOutputWan()), totalStyle);
        setText(row, 11, textOrBlank(rowData.getProjectLeadQuarterOneAmountWan()), totalStyle);
        setText(row, 12, textOrBlank(rowData.getProjectLeadQuarterTwoAmountWan()), totalStyle);
        setText(row, 13, textOrBlank(rowData.getProjectLeadQuarterThreeAmountWan()), totalStyle);
        setText(row, 14, textOrBlank(rowData.getProjectLeadQuarterFourAmountWan()), totalStyle);
        setText(row, 15, textOrBlank(rowData.getProjectLeadYearTotalAmountWan()), totalStyle);
        setText(row, 16, "", totalStyle);
        setText(row, 17, textOrBlank(rowData.getOfficeAssessmentOutputWan()), totalStyle);
        setText(row, 18, textOrBlank(rowData.getOfficeQuarterOneAmountWan()), totalStyle);
        setText(row, 19, textOrBlank(rowData.getOfficeQuarterTwoAmountWan()), totalStyle);
        setText(row, 20, textOrBlank(rowData.getOfficeQuarterThreeAmountWan()), totalStyle);
        setText(row, 21, textOrBlank(rowData.getOfficeQuarterFourAmountWan()), totalStyle);
        setText(row, 22, textOrBlank(rowData.getOfficeYearTotalAmountWan()), totalStyle);
        setMergedRegionText(sheet, rowIndex, rowIndex,
                SHEET1_SPECIALTY_RATIO_START_COL, SHEET1_SPECIALTY_AMOUNT_START_COL - 1,
                "", totalLeftStyle);
        for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
            setText(row, SHEET1_SPECIALTY_AMOUNT_START_COL + i,
                    textOrBlank(resolveSpecialtyAssessmentOutputWan(rowData, i)), totalStyle);
        }
    }

    private void writeSheet1SignatureRow(Sheet sheet, int rowIndex, ExportData data, CellStyle signatureStyle) {
        sheet.createRow(rowIndex);
        int totalColumnCount = sheet1LastCol() + 1;
        int leftEndCol = (totalColumnCount / 2) - 1;
        setMergedRegionText(sheet, rowIndex, rowIndex, 0, leftEndCol,
                safeText(defaultSignatureLabel(data.getCenterSignerLabel(), "设计中心相关负责人（签名）：")),
                signatureStyle);
        setMergedRegionText(sheet, rowIndex, rowIndex, leftEndCol + 1, sheet1LastCol(),
                safeText(defaultSignatureLabel(data.getProjectSignerLabel(), "项目经理/工程负责人（签名）：")),
                signatureStyle);
    }

    private void buildSheet2(Workbook workbook, ExportData data) {
        Sheet sheet = createSheet(workbook, SHEET2_NAME, SHEET2_FALLBACK_NAME);
        setColumnWidths(sheet, buildSheet2ColumnWidths());

        CellStyle baseHeaderStyle = createHeaderStyle(workbook);
        CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
                IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
        CellStyle cellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
                null, HorizontalAlignment.CENTER, false, (short) 10);
        CellStyle leftCellStyle = createDerivedStyle(workbook, cellStyle,
                null, HorizontalAlignment.LEFT, false, (short) 10);
        CellStyle totalStyle = createDerivedStyle(workbook, baseHeaderStyle,
                IndexedColors.GOLD, HorizontalAlignment.CENTER, true, (short) 10);
        CellStyle signatureStyle = createDerivedStyle(workbook, createInfoStyle(workbook),
                null, HorizontalAlignment.CENTER, false, (short) 10);

        int rowIndex = 0;
        rowIndex = buildSheet2Header(sheet, rowIndex, data, headerStyle, cellStyle);

        int bodyStartRow = rowIndex;
        List<OutputTypeGroup> groups = buildOutputTypeGroups(data);
        for (OutputTypeGroup group : groups) {
            int groupStartRow = rowIndex;
            for (QuarterRow detailRow : group.getRows()) {
                writeSheet2DetailRow(sheet, rowIndex++, data, detailRow, cellStyle, leftCellStyle);
            }
            mergeOutputTypeColumn(sheet, groupStartRow, rowIndex - 1, group.getOutputType(), cellStyle);
        }

        int totalRowIndex = rowIndex;
        writeSheet2ProjectTotalRow(sheet, rowIndex++, data,
                summarizeSheet2SpecialtyBlocks(extractDetailRows(data)), totalStyle);

        mergeProjectNameColumn(sheet, bodyStartRow, totalRowIndex, safeText(data.getProjectName()), cellStyle);

        writeSheet2SignatureRow(sheet, rowIndex, signatureStyle);
    }

    private int buildSheet2Header(Sheet sheet, int rowIndex, ExportData data,
                                  CellStyle headerStyle, CellStyle valueStyle) {
        int firstRow = buildCommonLeftHeader(sheet, rowIndex, data, headerStyle, valueStyle);

        int startCol = SHEET2_BASE_COL_COUNT;
        int endCol = sheet2LastCol();
        setMergedRegionText(sheet, firstRow, firstRow, startCol, endCol,
                "各专业年度 / 季度项目考核产值", headerStyle);
        for (String specialtyName : SPECIALTY_NAMES) {
            setMergedRegionText(sheet, firstRow + 1, firstRow + 1, startCol, startCol + SHEET2_SPECIALTY_BLOCK_WIDTH - 1,
                    specialtyName + "专业项目考核产值（万元）", headerStyle);
            setText(sheet.getRow(firstRow + 2), startCol, "一季度", headerStyle);
            setText(sheet.getRow(firstRow + 2), startCol + 1, "二季度", headerStyle);
            setText(sheet.getRow(firstRow + 2), startCol + 2, "三季度", headerStyle);
            setText(sheet.getRow(firstRow + 2), startCol + 3, "四季度", headerStyle);
            setText(sheet.getRow(firstRow + 2), startCol + 4, "本年度总计", headerStyle);
            startCol += SHEET2_SPECIALTY_BLOCK_WIDTH;
        }
        return rowIndex + 3;
    }

    private void writeSheet2DetailRow(Sheet sheet, int rowIndex, ExportData data, QuarterRow rowData,
                                      CellStyle cellStyle, CellStyle leftCellStyle) {
        Row row = sheet.createRow(rowIndex);
        setText(row, 0, rowData.getSerialNo(), cellStyle);
        setText(row, 1, safeText(data.getProjectName()), cellStyle);
        setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
        setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);
        writeSheet2SpecialtyBlocks(row, buildSheet2SpecialtyBlocks(rowData), cellStyle);
    }

    private void writeSheet2ProjectTotalRow(Sheet sheet, int rowIndex, ExportData data,
                                            List<Sheet2SpecialtyBlock> specialtyBlocks,
                                            CellStyle totalStyle) {
        Row row = sheet.createRow(rowIndex);
        setText(row, 0, "", totalStyle);
        setText(row, 1, safeText(data.getProjectName()), totalStyle);
        setMergedRegionText(sheet, rowIndex, rowIndex, 2, 3, TOTAL_LABEL, totalStyle);
        writeSheet2SpecialtyBlocks(sheet.getRow(rowIndex), specialtyBlocks, totalStyle);
    }

    private void writeSheet2SignatureRow(Sheet sheet, int rowIndex, CellStyle signatureStyle) {
        sheet.createRow(rowIndex);
        setMergedRegionText(sheet, rowIndex, rowIndex, 0, SHEET2_BASE_COL_COUNT - 1, "", signatureStyle);
        int startCol = SHEET2_BASE_COL_COUNT;
        for (String label : SHEET2_SIGNATURE_LABELS) {
            int endCol = startCol + SHEET2_SPECIALTY_BLOCK_WIDTH - 1;
            setMergedRegionText(sheet, rowIndex, rowIndex, startCol, endCol, label, signatureStyle);
            startCol = endCol + 1;
        }
    }

    private void writeSheet2SpecialtyBlocks(Row row, List<Sheet2SpecialtyBlock> specialtyBlocks, CellStyle cellStyle) {
        int startCol = SHEET2_BASE_COL_COUNT;
        for (Sheet2SpecialtyBlock block : specialtyBlocks) {
            setText(row, startCol, textOrBlank(block.getQuarterOneAmountWan()), cellStyle);
            setText(row, startCol + 1, textOrBlank(block.getQuarterTwoAmountWan()), cellStyle);
            setText(row, startCol + 2, textOrBlank(block.getQuarterThreeAmountWan()), cellStyle);
            setText(row, startCol + 3, textOrBlank(block.getQuarterFourAmountWan()), cellStyle);
            setText(row, startCol + 4, textOrBlank(block.getYearTotalAmountWan()), cellStyle);
            startCol += SHEET2_SPECIALTY_BLOCK_WIDTH;
        }
    }

    private List<Sheet2SpecialtyBlock> summarizeSheet2SpecialtyBlocks(List<QuarterRow> rows) {
        List<Sheet2SpecialtyBlock> result = buildEmptySheet2SpecialtyBlocks();
        for (QuarterRow row : rows) {
            if (row == null) {
                continue;
            }
            List<Sheet2SpecialtyBlock> currentBlocks = buildSheet2SpecialtyBlocks(row);
            for (int i = 0; i < currentBlocks.size(); i++) {
                Sheet2SpecialtyBlock totalBlock = result.get(i);
                Sheet2SpecialtyBlock currentBlock = currentBlocks.get(i);
                totalBlock.setQuarterOneAmountWan(addAmount(totalBlock.getQuarterOneAmountWan(), currentBlock.getQuarterOneAmountWan()));
                totalBlock.setQuarterTwoAmountWan(addAmount(totalBlock.getQuarterTwoAmountWan(), currentBlock.getQuarterTwoAmountWan()));
                totalBlock.setQuarterThreeAmountWan(addAmount(totalBlock.getQuarterThreeAmountWan(), currentBlock.getQuarterThreeAmountWan()));
                totalBlock.setQuarterFourAmountWan(addAmount(totalBlock.getQuarterFourAmountWan(), currentBlock.getQuarterFourAmountWan()));
                totalBlock.setYearTotalAmountWan(addAmount(totalBlock.getYearTotalAmountWan(), currentBlock.getYearTotalAmountWan()));
            }
        }
        return result;
    }

    private List<Sheet2SpecialtyBlock> buildSheet2SpecialtyBlocks(QuarterRow row) {
        if (row != null && row.isPlaceholderRow()) {
            return buildBlankSheet2SpecialtyBlocks();
        }
        List<Sheet2SpecialtyBlock> result = new ArrayList<>();
        for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
            BigDecimal specialtyRatio = resolveSpecialtyRatio(row, i);
            Sheet2SpecialtyBlock block = new Sheet2SpecialtyBlock();
            block.setQuarterOneAmountWan(multiplyAmountByRatio(row.getOfficeQuarterOneAmountWan(), specialtyRatio));
            block.setQuarterTwoAmountWan(multiplyAmountByRatio(row.getOfficeQuarterTwoAmountWan(), specialtyRatio));
            block.setQuarterThreeAmountWan(multiplyAmountByRatio(row.getOfficeQuarterThreeAmountWan(), specialtyRatio));
            block.setQuarterFourAmountWan(multiplyAmountByRatio(row.getOfficeQuarterFourAmountWan(), specialtyRatio));
            block.setYearTotalAmountWan(addAmount(block.getQuarterOneAmountWan(),
                    addAmount(block.getQuarterTwoAmountWan(),
                            addAmount(block.getQuarterThreeAmountWan(), block.getQuarterFourAmountWan()))));
            result.add(block);
        }
        return result;
    }

    private List<Sheet2SpecialtyBlock> buildBlankSheet2SpecialtyBlocks() {
        List<Sheet2SpecialtyBlock> result = new ArrayList<>();
        for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
            result.add(new Sheet2SpecialtyBlock());
        }
        return result;
    }

    private List<Sheet2SpecialtyBlock> buildEmptySheet2SpecialtyBlocks() {
        List<Sheet2SpecialtyBlock> result = new ArrayList<>();
        for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
            Sheet2SpecialtyBlock block = new Sheet2SpecialtyBlock();
            block.setQuarterOneAmountWan(zeroAmount());
            block.setQuarterTwoAmountWan(zeroAmount());
            block.setQuarterThreeAmountWan(zeroAmount());
            block.setQuarterFourAmountWan(zeroAmount());
            block.setYearTotalAmountWan(zeroAmount());
            result.add(block);
        }
        return result;
    }

    private int outputTypeOrder(String outputType) {
        int index = OUTPUT_TYPE_ORDER.indexOf(safeText(outputType));
        return index >= 0 ? index : OUTPUT_TYPE_ORDER.size();
    }

    private int buildCommonLeftHeader(Sheet sheet, int rowIndex, ExportData data,
                                      CellStyle headerStyle, CellStyle valueStyle) {
        int firstRow = rowIndex;
        sheet.createRow(firstRow);
        sheet.createRow(firstRow + 1);
        sheet.createRow(firstRow + 2);

        String projectCode = data == null ? "" : safeText(data.getProjectCode());
        setMergedRegionText(sheet, firstRow, firstRow + 1, 0, 0, "工程编号", headerStyle);
        setMergedRegionText(sheet, firstRow, firstRow + 1, 1, 1, projectCode, valueStyle);
        setMergedRegionText(sheet, firstRow, firstRow + 1, 2, 3, "类别", headerStyle);
        setText(sheet.getRow(firstRow + 2), 0, "序号", headerStyle);
        setText(sheet.getRow(firstRow + 2), 1, "项目名称", headerStyle);
        setText(sheet.getRow(firstRow + 2), 2, "产值类型", headerStyle);
        setText(sheet.getRow(firstRow + 2), 3, "设计内容", headerStyle);
        return firstRow;
    }

    private void mergeProjectNameColumn(Sheet sheet, int startRow, int endRow, String projectName, CellStyle style) {
        if (startRow < 0 || endRow < startRow) {
            return;
        }
        setMergedRegionText(sheet, startRow, endRow, 1, 1, safeText(projectName), style);
    }

    private void mergeOutputTypeColumn(Sheet sheet, int startRow, int endRow, String outputType, CellStyle style) {
        if (startRow < 0 || endRow < startRow) {
            return;
        }
        setMergedRegionText(sheet, startRow, endRow, 2, 2, safeText(outputType), style);
    }

    private List<QuarterRow> extractDetailRows(ExportData data) {
        if (data == null || data.getRows() == null || data.getRows().isEmpty()) {
            return Collections.emptyList();
        }
        List<QuarterRow> detailRows = new ArrayList<>();
        for (QuarterRow row : data.getRows()) {
            if (row != null && !row.isTotalRow()) {
                detailRows.add(row);
            }
        }
        detailRows.sort(Comparator
                .comparingInt((QuarterRow row) -> outputTypeOrder(row.getOutputType()))
                .thenComparing(QuarterRow::getSerialNo, Comparator.nullsLast(Integer::compareTo)));
        return detailRows;
    }

    private QuarterRow resolveProjectTotalRow(ExportData data) {
        if (data == null || data.getRows() == null) {
            return null;
        }
        for (QuarterRow row : data.getRows()) {
            if (row != null && row.isTotalRow()) {
                return row;
            }
        }
        return null;
    }

    private List<OutputTypeGroup> buildOutputTypeGroups(ExportData data) {
        List<QuarterRow> detailRows = extractDetailRows(data);
        int nextSerialNo = detailRows.stream()
                .map(QuarterRow::getSerialNo)
                .filter(item -> item != null)
                .max(Integer::compareTo)
                .orElse(0) + 1;
        Map<String, List<QuarterRow>> groupedMap = new LinkedHashMap<>();
        for (QuarterRow row : detailRows) {
            groupedMap.computeIfAbsent(safeText(row.getOutputType()), key -> new ArrayList<>()).add(row);
        }

        List<OutputTypeGroup> result = new ArrayList<>();
        for (String outputType : FIXED_OUTPUT_TYPES) {
            List<QuarterRow> rows = groupedMap.remove(outputType);
            if (rows == null || rows.isEmpty()) {
                rows = new ArrayList<>();
                rows.add(buildPlaceholderRow(outputType, nextSerialNo++));
            }
            OutputTypeGroup group = new OutputTypeGroup();
            group.setOutputType(outputType);
            group.setRows(rows);
            result.add(group);
        }
        for (Map.Entry<String, List<QuarterRow>> entry : groupedMap.entrySet()) {
            OutputTypeGroup group = new OutputTypeGroup();
            group.setOutputType(entry.getKey());
            group.setRows(entry.getValue());
            result.add(group);
        }
        return result;
    }

    private QuarterRow buildPlaceholderRow(String outputType, Integer serialNo) {
        QuarterRow row = new QuarterRow();
        row.setSerialNo(serialNo);
        row.setOutputType(outputType);
        row.setDesignContent("");
        row.setPlaceholderRow(true);
        return row;
    }

    private String defaultSignatureLabel(String value, String fallback) {
        String text = safeText(value);
        return text.isEmpty() ? fallback : text;
    }

    private BigDecimal addAmount(BigDecimal left, BigDecimal right) {
        return amountOrZero(left).add(amountOrZero(right)).setScale(2, RoundingMode.HALF_UP);
    }

    private BigDecimal amountOrZero(BigDecimal value) {
        return value == null ? zeroAmount() : value.setScale(2, RoundingMode.HALF_UP);
    }

    private BigDecimal zeroAmount() {
        return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
    }

    private BigDecimal multiplyAmountByRatio(BigDecimal amount, BigDecimal ratioValue) {
        return amountOrZero(amount).multiply(ratio(ratioValue)).setScale(2, RoundingMode.HALF_UP);
    }

    private BigDecimal resolveSpecialtyRatio(QuarterRow row, int index) {
        switch (index) {
            case 0:
                return row.getArchRatio();
            case 1:
                return row.getDecorRatio();
            case 2:
                return row.getStructRatio();
            case 3:
                return row.getWaterRatio();
            case 4:
                return row.getHvacRatio();
            case 5:
                return row.getElecRatio();
            case 6:
                return row.getDigitalRatio();
            default:
                return null;
        }
    }

    private BigDecimal resolveSpecialtyAssessmentOutputWan(QuarterRow row, int index) {
        switch (index) {
            case 0:
                return row.getArchAssessmentOutputWan();
            case 1:
                return row.getDecorAssessmentOutputWan();
            case 2:
                return row.getStructAssessmentOutputWan();
            case 3:
                return row.getWaterAssessmentOutputWan();
            case 4:
                return row.getHvacAssessmentOutputWan();
            case 5:
                return row.getElecAssessmentOutputWan();
            case 6:
                return row.getDigitalAssessmentOutputWan();
            default:
                return null;
        }
    }

    private int[] buildSheet2ColumnWidths() {
        int[] widths = new int[SHEET2_BASE_COL_COUNT + SPECIALTY_NAMES.size() * SHEET2_SPECIALTY_BLOCK_WIDTH];
        widths[0] = 10;
        widths[1] = 22;
        widths[2] = 16;
        widths[3] = 16;
        int index = SHEET2_BASE_COL_COUNT;
        for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
            widths[index++] = 12;
            widths[index++] = 12;
            widths[index++] = 12;
            widths[index++] = 12;
            widths[index++] = 14;
        }
        return widths;
    }

    private int sheet1LastCol() {
        return SHEET1_SPECIALTY_AMOUNT_START_COL + SPECIALTY_NAMES.size() - 1;
    }

    private int sheet2LastCol() {
        return SHEET2_BASE_COL_COUNT + SPECIALTY_NAMES.size() * SHEET2_SPECIALTY_BLOCK_WIDTH - 1;
    }

    private String textOrBlank(BigDecimal value) {
        if (value == null) {
            return "";
        }
        return value.setScale(2, RoundingMode.HALF_UP).toPlainString();
    }

    private String percentTextOrBlank(BigDecimal value) {
        return value == null ? "" : percentText(value);
    }

    @Data
    public static class ExportData {
        private String projectCode;
        private String projectName;
        private Integer year;
        private String centerSignerLabel;
        private String projectSignerLabel;
        private List<QuarterRow> rows;
    }

    @Data
    public static class QuarterRow {
        private Integer serialNo;
        private boolean totalRow;
        private boolean placeholderRow;
        private String outputType;
        private String designContent;
        private BigDecimal quarterOneAmountWan;
        private BigDecimal quarterTwoAmountWan;
        private BigDecimal quarterThreeAmountWan;
        private BigDecimal quarterFourAmountWan;
        private BigDecimal yearTotalAmountWan;
        private BigDecimal projectLeadRatio;
        private BigDecimal projectLeadAssessmentOutputWan;
        private BigDecimal projectLeadQuarterOneAmountWan;
        private BigDecimal projectLeadQuarterTwoAmountWan;
        private BigDecimal projectLeadQuarterThreeAmountWan;
        private BigDecimal projectLeadQuarterFourAmountWan;
        private BigDecimal projectLeadYearTotalAmountWan;
        private BigDecimal officeRatio;
        private BigDecimal officeAssessmentOutputWan;
        private BigDecimal officeQuarterOneAmountWan;
        private BigDecimal officeQuarterTwoAmountWan;
        private BigDecimal officeQuarterThreeAmountWan;
        private BigDecimal officeQuarterFourAmountWan;
        private BigDecimal officeYearTotalAmountWan;
        private BigDecimal archRatio;
        private BigDecimal decorRatio;
        private BigDecimal structRatio;
        private BigDecimal waterRatio;
        private BigDecimal hvacRatio;
        private BigDecimal elecRatio;
        private BigDecimal digitalRatio;
        private BigDecimal archAssessmentOutputWan;
        private BigDecimal decorAssessmentOutputWan;
        private BigDecimal structAssessmentOutputWan;
        private BigDecimal waterAssessmentOutputWan;
        private BigDecimal hvacAssessmentOutputWan;
        private BigDecimal elecAssessmentOutputWan;
        private BigDecimal digitalAssessmentOutputWan;
    }

    @Data
    private static class OutputTypeGroup {
        private String outputType;
        private List<QuarterRow> rows;
    }

    @Data
    private static class Sheet2SpecialtyBlock {
        private BigDecimal quarterOneAmountWan;
        private BigDecimal quarterTwoAmountWan;
        private BigDecimal quarterThreeAmountWan;
        private BigDecimal quarterFourAmountWan;
        private BigDecimal yearTotalAmountWan;
    }

}
