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

import cn.iocoder.lyzsys.module.tjt.enums.OutputSplitBizConstants;
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;
import java.util.Objects;
import java.util.stream.Collectors;

@Component
public class SpecialtyPersonOutputExcelBuilder extends AbstractProjectOutputExcelBuilder {

    private static final String SHEET1_NAME = "工作量分配";
    private static final String SHEET2_NAME = "季度考核产值";
    private static final String SHEET1_TITLE = "7.1.4 专业内人员项目考核产值年度 / 季度预算计取表（以一个专业作为参考，各专业相同）";
    private static final String SHEET2_TITLE = "专业内人员年度 / 季度项目考核产值（万元）";
    private static final String SHEET1_SECTION_TITLE = "专业内人员工作量分配";
    private static final String SHEET2_SECTION_TITLE = "专业项目考核产值（万元）";
    private static final String SHEET2_PERSON_SECTION_TITLE = "专业内人员年度 / 季度项目考核产值（万元）";
    private static final String DEFAULT_CENTER_SIGNER = "设计中心相关负责人（签名）：";
    private static final String DEFAULT_PROJECT_SIGNER = "项目经理 / 工程负责人（签名）：";
    private static final String DEFAULT_SPECIALTY_SIGNER = "专业负责人（签名）：";
    private static final String DEFAULT_DESIGNER_SIGNER = "参与设计人员（签名）：";

    private static final int SHEET1_FIXED_COL_COUNT = 4;
    private static final int SHEET1_ROLE_RATIO_WIDTH = 1;
    private static final int SHEET1_PERSON_RATIO_WIDTH = 2;
    private static final int SHEET1_ADJUSTED_PERSON_WIDTH = 1;
    private static final int SHEET1_REMARK_COL_COUNT = 1;

    private static final int SHEET2_FIXED_COL_COUNT = 4;
    private static final int SHEET2_SPECIALTY_BLOCK_WIDTH = 5;
    private static final int SHEET2_PERSON_BLOCK_WIDTH = 5;

    private static final List<String> ROLE_ORDER = Arrays.asList(
            OutputSplitBizConstants.ROLE_DIRECTOR,
            OutputSplitBizConstants.ROLE_CHECK,
            OutputSplitBizConstants.ROLE_DESIGN,
            OutputSplitBizConstants.ROLE_REVIEW,
            OutputSplitBizConstants.ROLE_APPROVE
    );

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

    private void buildSheet1(Workbook workbook, ExportData data) {
        List<RoleGroup> roleGroups = buildRoleGroups(data);
        List<PersonAmountModule> adjustedPersonModules = resolvePersonAmountModules(data);
        Sheet sheet = createSheet(workbook, SHEET1_NAME, SHEET1_NAME);
        setColumnWidths(sheet, buildSheet1ColumnWidths(roleGroups, adjustedPersonModules));

        CellStyle baseHeaderStyle = createHeaderStyle(workbook);
        CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
                IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
        CellStyle headerValueStyle = createDerivedStyle(workbook, baseHeaderStyle,
                IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, false, (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);

        int rowIndex = 0;
        rowIndex = buildSheet1Header(sheet, rowIndex, data, roleGroups, adjustedPersonModules,
                headerStyle, headerValueStyle);

        List<SpecialtyPlanningRow> rows = resolveDetailRows(data);
        int bodyStartRow = rowIndex;
        for (SpecialtyPlanningRow rowData : rows) {
            writeSheet1DetailRow(sheet, rowIndex++, rowData, roleGroups, adjustedPersonModules,
                    cellStyle, leftCellStyle);
        }
        mergeSheet1Body(sheet, bodyStartRow, rowIndex - 1, rows, cellStyle);
    }

    private void buildSheet2(Workbook workbook, ExportData data) {
        List<PersonAmountModule> personModules = resolvePersonAmountModules(data);
        int lastCol = sheet2LastCol(personModules);
        Sheet sheet = createSheet(workbook, SHEET2_NAME, SHEET2_NAME);
        setColumnWidths(sheet, buildSheet2ColumnWidths(personModules));

        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.LEFT, false, (short) 10);

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

        List<SpecialtyPlanningRow> rows = resolveSheet2Rows(data);
        int bodyStartRow = rowIndex;
        for (SpecialtyPlanningRow rowData : rows) {
            if (Boolean.TRUE.equals(rowData.getTotalRow())) {
                writeSheet2TotalRow(sheet, rowIndex++, rowData, personModules, totalStyle);
                continue;
            }
            writeSheet2DetailRow(sheet, rowIndex++, rowData, personModules, cellStyle, leftCellStyle);
        }
        mergeSheet2Body(sheet, bodyStartRow, rowIndex - 1, rows, cellStyle);
        writeSheet2SignatureRow(sheet, rowIndex, lastCol, signatureStyle);
    }

    private int buildSheet1Header(Sheet sheet, int rowIndex, ExportData data, List<RoleGroup> roleGroups,
                                  List<PersonAmountModule> adjustedPersonModules,
                                  CellStyle headerStyle, CellStyle headerValueStyle) {
        int firstRow = rowIndex;
        sheet.createRow(firstRow);
        sheet.createRow(firstRow + 1);
        sheet.createRow(firstRow + 2);
        sheet.createRow(firstRow + 3);

        setText(sheet.getRow(firstRow), 0, "工程编号", headerStyle);
        setText(sheet.getRow(firstRow), 1, safeText(data == null ? "" : data.getProjectCode()), headerValueStyle);
        setText(sheet.getRow(firstRow), 2, "工程负责人", headerStyle);
        setText(sheet.getRow(firstRow), 3, safeText(data == null ? "" : data.getEngineeringPrincipalNames()), headerValueStyle);

        int remarkCol = sheet1LastCol(roleGroups, adjustedPersonModules);
        int adjustedStartCol = sheet1AdjustedStartCol(roleGroups);
        int adjustedEndCol = remarkCol - 1;
        setMergedRegionText(sheet, firstRow, firstRow, 4, adjustedEndCol, SHEET1_SECTION_TITLE, headerStyle);
        setMergedRegionText(sheet, firstRow, firstRow + 3, remarkCol, remarkCol, "备注", headerStyle);

        setText(sheet.getRow(firstRow + 1), 0, "专业", headerStyle);
        setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 1, 3,
                safeText(data == null ? "" : data.getSpecialtyName()), headerValueStyle);

        setMergedRegionText(sheet, firstRow + 2, firstRow + 3, 0, 0, "序号", headerStyle);
        setMergedRegionText(sheet, firstRow + 2, firstRow + 3, 1, 1, "项目名称", headerStyle);
        setMergedRegionText(sheet, firstRow + 2, firstRow + 2, 2, 3, "类别", headerStyle);
        setText(sheet.getRow(firstRow + 3), 2, "产值类型", headerStyle);
        setText(sheet.getRow(firstRow + 3), 3, "设计内容", headerStyle);

        int colIndex = SHEET1_FIXED_COL_COUNT;
        for (RoleGroup roleGroup : roleGroups) {
            int roleWidth = sheet1RoleWidth(roleGroup);
            setMergedRegionText(sheet, firstRow + 1, firstRow + 1, colIndex, colIndex + roleWidth - 1,
                    roleGroup.getRoleName(), headerStyle);
            setMergedRegionText(sheet, firstRow + 2, firstRow + 3, colIndex, colIndex,
                    "岗位整体占专业比例", headerStyle);
            colIndex++;
            for (RolePersonModule module : roleGroup.getModules()) {
                setMergedRegionText(sheet, firstRow + 2, firstRow + 2, colIndex, colIndex + 1,
                        safeText(module.getEmployeeName()), headerStyle);
                setText(sheet.getRow(firstRow + 3), colIndex, "工作量比例", headerStyle);
                setText(sheet.getRow(firstRow + 3), colIndex + 1, "占专业比例", headerStyle);
                colIndex += SHEET1_PERSON_RATIO_WIDTH;
            }
        }
        setMergedRegionText(sheet, firstRow + 1, firstRow + 1, adjustedStartCol, adjustedEndCol,
                "调整后人员比例", headerStyle);
        for (PersonAmountModule module : adjustedPersonModules) {
            setMergedRegionText(sheet, firstRow + 2, firstRow + 3, colIndex, colIndex,
                    safeText(module.getEmployeeName()), headerStyle);
            colIndex += SHEET1_ADJUSTED_PERSON_WIDTH;
        }
        return rowIndex + 4;
    }

    private void writeSheet1DetailRow(Sheet sheet, int rowIndex, SpecialtyPlanningRow rowData,
                                      List<RoleGroup> roleGroups, List<PersonAmountModule> adjustedPersonModules,
                                      CellStyle cellStyle, CellStyle leftCellStyle) {
        Row row = sheet.createRow(rowIndex);
        setText(row, 0, rowData.getSerialNo(), cellStyle);
        setText(row, 1, safeText(rowData.getProjectName()), cellStyle);
        setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
        setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);

        int colIndex = SHEET1_FIXED_COL_COUNT;
        for (RoleGroup roleGroup : roleGroups) {
            setText(row, colIndex++, percentTextOrBlank(rowData.getRoleRatioMap().get(roleGroup.getRoleCode())), cellStyle);
            for (RolePersonModule module : roleGroup.getModules()) {
                RolePersonRatioValue value = rowData.getRolePersonValueMap().get(module.getKey());
                setText(row, colIndex, value == null ? "" : percentTextOrBlank(value.getWorkRatio()), cellStyle);
                setText(row, colIndex + 1, value == null ? "" : percentTextOrBlank(value.getSpecialtyRatio()), cellStyle);
                colIndex += SHEET1_PERSON_RATIO_WIDTH;
            }
        }
        for (PersonAmountModule module : adjustedPersonModules) {
            setText(row, colIndex++, percentTextOrBlank(rowData.getAdjustedPersonRatioMap().get(module.getKey())), cellStyle);
        }
        setText(row, colIndex, safeText(rowData.getRemark()), leftCellStyle);
    }

    private int buildSheet2Header(Sheet sheet, int rowIndex, ExportData data,
                                  List<PersonAmountModule> personModules, CellStyle headerStyle) {
        int firstRow = rowIndex;
        sheet.createRow(firstRow);
        sheet.createRow(firstRow + 1);
        sheet.createRow(firstRow + 2);
        sheet.createRow(firstRow + 3);

        setMergedRegionText(sheet, firstRow, firstRow + 3, 0, 0, "序号", headerStyle);
        setMergedRegionText(sheet, firstRow, firstRow + 3, 1, 1, "项目名称", headerStyle);
        setMergedRegionText(sheet, firstRow, firstRow + 2, 2, 3, "类别", headerStyle);
        setText(sheet.getRow(firstRow + 3), 2, "产值类型", headerStyle);
        setText(sheet.getRow(firstRow + 3), 3, "设计内容", headerStyle);

        int specialtyStartCol = SHEET2_FIXED_COL_COUNT;
        setMergedRegionText(sheet, firstRow, firstRow + 2, specialtyStartCol, specialtyStartCol,
                safeText(data == null ? "" : data.getSpecialtyName()), headerStyle);
        setMergedRegionText(sheet, firstRow, firstRow + 2, specialtyStartCol + 1,
                specialtyStartCol + SHEET2_SPECIALTY_BLOCK_WIDTH - 1, SHEET2_SECTION_TITLE, headerStyle);
        writeQuarterHeader(sheet.getRow(firstRow + 3), specialtyStartCol, headerStyle);

        int personStartCol = specialtyStartCol + SHEET2_SPECIALTY_BLOCK_WIDTH;
        int lastCol = sheet2LastCol(personModules);
        setMergedRegionText(sheet, firstRow, firstRow, personStartCol, lastCol, SHEET2_PERSON_SECTION_TITLE, headerStyle);

        int colIndex = personStartCol;
        for (PersonAmountModule module : personModules) {
            setMergedRegionText(sheet, firstRow + 1, firstRow + 2, colIndex, colIndex + SHEET2_PERSON_BLOCK_WIDTH - 1,
                    safeText(module.getEmployeeName()), headerStyle);
            writeQuarterHeader(sheet.getRow(firstRow + 3), colIndex, headerStyle);
            colIndex += SHEET2_PERSON_BLOCK_WIDTH;
        }
        return rowIndex + 4;
    }

    private void writeSheet2DetailRow(Sheet sheet, int rowIndex, SpecialtyPlanningRow rowData,
                                      List<PersonAmountModule> personModules, CellStyle cellStyle,
                                      CellStyle leftCellStyle) {
        Row row = sheet.createRow(rowIndex);
        setText(row, 0, rowData.getSerialNo(), cellStyle);
        setText(row, 1, safeText(rowData.getProjectName()), cellStyle);
        setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
        setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);

        int colIndex = SHEET2_FIXED_COL_COUNT;
        setText(row, colIndex, textOrBlank(rowData.getSpecialtyQuarterOneAmountWan()), cellStyle);
        setText(row, colIndex + 1, textOrBlank(rowData.getSpecialtyQuarterTwoAmountWan()), cellStyle);
        setText(row, colIndex + 2, textOrBlank(rowData.getSpecialtyQuarterThreeAmountWan()), cellStyle);
        setText(row, colIndex + 3, textOrBlank(rowData.getSpecialtyQuarterFourAmountWan()), cellStyle);
        setText(row, colIndex + 4, textOrBlank(rowData.getSpecialtyYearTotalAmountWan()), cellStyle);
        colIndex += SHEET2_SPECIALTY_BLOCK_WIDTH;

        for (PersonAmountModule module : personModules) {
            PersonAmountValue value = rowData.getPersonAmountMap().get(module.getKey());
            setText(row, colIndex, value == null ? "" : textOrBlank(value.getQuarterOneAmountWan()), cellStyle);
            setText(row, colIndex + 1, value == null ? "" : textOrBlank(value.getQuarterTwoAmountWan()), cellStyle);
            setText(row, colIndex + 2, value == null ? "" : textOrBlank(value.getQuarterThreeAmountWan()), cellStyle);
            setText(row, colIndex + 3, value == null ? "" : textOrBlank(value.getQuarterFourAmountWan()), cellStyle);
            setText(row, colIndex + 4, value == null ? "" : textOrBlank(value.getYearTotalAmountWan()), cellStyle);
            colIndex += SHEET2_PERSON_BLOCK_WIDTH;
        }
    }

    private void writeSheet2TotalRow(Sheet sheet, int rowIndex, SpecialtyPlanningRow rowData,
                                     List<PersonAmountModule> personModules, CellStyle totalStyle) {
        Row row = sheet.createRow(rowIndex);
        setText(row, 0, "", totalStyle);
        setText(row, 1, "", totalStyle);
        setMergedRegionText(sheet, rowIndex, rowIndex, 2, 3, "总计", totalStyle);

        int colIndex = SHEET2_FIXED_COL_COUNT;
        setText(row, colIndex, textOrBlank(rowData.getSpecialtyQuarterOneAmountWan()), totalStyle);
        setText(row, colIndex + 1, textOrBlank(rowData.getSpecialtyQuarterTwoAmountWan()), totalStyle);
        setText(row, colIndex + 2, textOrBlank(rowData.getSpecialtyQuarterThreeAmountWan()), totalStyle);
        setText(row, colIndex + 3, textOrBlank(rowData.getSpecialtyQuarterFourAmountWan()), totalStyle);
        setText(row, colIndex + 4, textOrBlank(rowData.getSpecialtyYearTotalAmountWan()), totalStyle);
        colIndex += SHEET2_SPECIALTY_BLOCK_WIDTH;

        for (PersonAmountModule module : personModules) {
            PersonAmountValue value = rowData.getPersonAmountMap().get(module.getKey());
            setText(row, colIndex, value == null ? "" : textOrBlank(value.getQuarterOneAmountWan()), totalStyle);
            setText(row, colIndex + 1, value == null ? "" : textOrBlank(value.getQuarterTwoAmountWan()), totalStyle);
            setText(row, colIndex + 2, value == null ? "" : textOrBlank(value.getQuarterThreeAmountWan()), totalStyle);
            setText(row, colIndex + 3, value == null ? "" : textOrBlank(value.getQuarterFourAmountWan()), totalStyle);
            setText(row, colIndex + 4, value == null ? "" : textOrBlank(value.getYearTotalAmountWan()), totalStyle);
            colIndex += SHEET2_PERSON_BLOCK_WIDTH;
        }
    }

    private void writeSheet2SignatureRow(Sheet sheet, int rowIndex, int lastCol, CellStyle signatureStyle) {
        sheet.createRow(rowIndex);
        int totalColCount = lastCol + 1;
        int segmentWidth = Math.max(1, totalColCount / 4);
        int col1End = Math.min(lastCol, segmentWidth - 1);
        int col2End = Math.min(lastCol, col1End + segmentWidth);
        int col3End = Math.min(lastCol, col2End + segmentWidth);

        setMergedRegionText(sheet, rowIndex, rowIndex, 0, col1End, DEFAULT_CENTER_SIGNER, signatureStyle);
        setMergedRegionText(sheet, rowIndex, rowIndex, col1End + 1, col2End, DEFAULT_PROJECT_SIGNER, signatureStyle);
        setMergedRegionText(sheet, rowIndex, rowIndex, col2End + 1, col3End, DEFAULT_SPECIALTY_SIGNER, signatureStyle);
        setMergedRegionText(sheet, rowIndex, rowIndex, col3End + 1, lastCol, DEFAULT_DESIGNER_SIGNER, signatureStyle);
    }

    private void mergeSheet1Body(Sheet sheet, int startRow, int endRow, List<SpecialtyPlanningRow> rows, CellStyle style) {
        if (startRow < 0 || endRow < startRow || rows.isEmpty()) {
            return;
        }
        setMergedRegionText(sheet, startRow, endRow, 1, 1, safeText(rows.get(0).getProjectName()), style);
        mergeOutputTypeColumn(sheet, startRow, rows, style);
    }

    private void mergeSheet2Body(Sheet sheet, int startRow, int endRow, List<SpecialtyPlanningRow> rows,
                                 CellStyle style) {
        if (startRow < 0 || endRow < startRow || rows.isEmpty()) {
            return;
        }
        int detailEndIndex = lastDetailIndex(rows);
        if (detailEndIndex < 0) {
            return;
        }
        setMergedRegionText(sheet, startRow, endRow, 1, 1, safeText(rows.get(0).getProjectName()), style);
        mergeOutputTypeColumn(sheet, startRow, rows.subList(0, detailEndIndex + 1), style);
    }

    private void mergeOutputTypeColumn(Sheet sheet, int bodyStartRow, List<SpecialtyPlanningRow> rows, CellStyle style) {
        if (rows == null || rows.isEmpty()) {
            return;
        }
        int groupStartIndex = 0;
        String currentOutputType = safeText(rows.get(0).getOutputType());
        for (int i = 1; i < rows.size(); i++) {
            String nextOutputType = safeText(rows.get(i).getOutputType());
            if (!Objects.equals(currentOutputType, nextOutputType)) {
                setMergedRegionText(sheet, bodyStartRow + groupStartIndex, bodyStartRow + i - 1,
                        2, 2, currentOutputType, style);
                groupStartIndex = i;
                currentOutputType = nextOutputType;
            }
        }
        setMergedRegionText(sheet, bodyStartRow + groupStartIndex, bodyStartRow + rows.size() - 1,
                2, 2, currentOutputType, style);
    }

    private List<RoleGroup> buildRoleGroups(ExportData data) {
        Map<String, List<RolePersonModule>> groupedMap = new LinkedHashMap<>();
        if (data != null && data.getRolePersonModules() != null) {
            for (RolePersonModule module : data.getRolePersonModules()) {
                if (module == null) {
                    continue;
                }
                groupedMap.computeIfAbsent(safeText(module.getRoleCode()), key -> new ArrayList<>()).add(module);
            }
        }

        List<RoleGroup> result = new ArrayList<>();
        for (String roleCode : ROLE_ORDER) {
            List<RolePersonModule> modules = groupedMap.get(roleCode);
            if (modules == null || modules.isEmpty()) {
                modules = new ArrayList<>();
                RolePersonModule placeholder = new RolePersonModule();
                placeholder.setKey(roleCode + "#placeholder");
                placeholder.setRoleCode(roleCode);
                placeholder.setRoleName(OutputSplitBizConstants.getRoleName(roleCode));
                placeholder.setEmployeeName("");
                placeholder.setPlaceholder(Boolean.TRUE);
                modules.add(placeholder);
            }
            RoleGroup group = new RoleGroup();
            group.setRoleCode(roleCode);
            group.setRoleName(resolveRoleName(modules, roleCode));
            group.setModules(modules);
            result.add(group);
        }
        return result;
    }

    private List<PersonAmountModule> resolvePersonAmountModules(ExportData data) {
        if (data == null || data.getPersonAmountModules() == null || data.getPersonAmountModules().isEmpty()) {
            PersonAmountModule placeholder = new PersonAmountModule();
            placeholder.setKey("person#placeholder");
            placeholder.setEmployeeName("");
            placeholder.setPlaceholder(Boolean.TRUE);
            return Collections.singletonList(placeholder);
        }
        return data.getPersonAmountModules().stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<SpecialtyPlanningRow> resolveDetailRows(ExportData data) {
        if (data == null || data.getRows() == null) {
            return Collections.emptyList();
        }
        List<SpecialtyPlanningRow> rows = data.getRows().stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        rows.sort(Comparator
                .comparingInt((SpecialtyPlanningRow row) -> outputTypeOrder(row.getOutputType()))
                .thenComparing(SpecialtyPlanningRow::getSerialNo, Comparator.nullsLast(Integer::compareTo)));
        return rows;
    }

    private List<SpecialtyPlanningRow> resolveSheet2Rows(ExportData data) {
        List<SpecialtyPlanningRow> detailRows = resolveDetailRows(data);
        if (detailRows.isEmpty()) {
            return detailRows;
        }
        List<SpecialtyPlanningRow> rows = new ArrayList<>(detailRows);
        rows.add(buildSheet2TotalRow(detailRows));
        return rows;
    }

    private SpecialtyPlanningRow buildSheet2TotalRow(List<SpecialtyPlanningRow> detailRows) {
        SpecialtyPlanningRow row = new SpecialtyPlanningRow();
        row.setTotalRow(Boolean.TRUE);
        row.setDesignContent("总计");
        for (SpecialtyPlanningRow detailRow : detailRows) {
            row.setSpecialtyQuarterOneAmountWan(addAmount(row.getSpecialtyQuarterOneAmountWan(),
                    detailRow.getSpecialtyQuarterOneAmountWan()));
            row.setSpecialtyQuarterTwoAmountWan(addAmount(row.getSpecialtyQuarterTwoAmountWan(),
                    detailRow.getSpecialtyQuarterTwoAmountWan()));
            row.setSpecialtyQuarterThreeAmountWan(addAmount(row.getSpecialtyQuarterThreeAmountWan(),
                    detailRow.getSpecialtyQuarterThreeAmountWan()));
            row.setSpecialtyQuarterFourAmountWan(addAmount(row.getSpecialtyQuarterFourAmountWan(),
                    detailRow.getSpecialtyQuarterFourAmountWan()));
            row.setSpecialtyYearTotalAmountWan(addAmount(row.getSpecialtyYearTotalAmountWan(),
                    detailRow.getSpecialtyYearTotalAmountWan()));

            for (Map.Entry<String, PersonAmountValue> entry : detailRow.getPersonAmountMap().entrySet()) {
                PersonAmountValue totalValue = row.getPersonAmountMap().computeIfAbsent(entry.getKey(),
                        key -> new PersonAmountValue());
                PersonAmountValue currentValue = entry.getValue();
                totalValue.setQuarterOneAmountWan(addAmount(totalValue.getQuarterOneAmountWan(),
                        currentValue == null ? null : currentValue.getQuarterOneAmountWan()));
                totalValue.setQuarterTwoAmountWan(addAmount(totalValue.getQuarterTwoAmountWan(),
                        currentValue == null ? null : currentValue.getQuarterTwoAmountWan()));
                totalValue.setQuarterThreeAmountWan(addAmount(totalValue.getQuarterThreeAmountWan(),
                        currentValue == null ? null : currentValue.getQuarterThreeAmountWan()));
                totalValue.setQuarterFourAmountWan(addAmount(totalValue.getQuarterFourAmountWan(),
                        currentValue == null ? null : currentValue.getQuarterFourAmountWan()));
                totalValue.setYearTotalAmountWan(addAmount(totalValue.getYearTotalAmountWan(),
                        currentValue == null ? null : currentValue.getYearTotalAmountWan()));
            }
        }
        return row;
    }

    private int[] buildSheet1ColumnWidths(List<RoleGroup> roleGroups, List<PersonAmountModule> adjustedPersonModules) {
        List<Integer> widths = new ArrayList<>();
        widths.add(8);
        widths.add(24);
        widths.add(16);
        widths.add(24);
        for (RoleGroup roleGroup : roleGroups) {
            widths.add(16);
            for (int i = 0; i < roleGroup.getModules().size(); i++) {
                widths.add(12);
                widths.add(12);
            }
        }
        for (int i = 0; i < adjustedPersonModules.size(); i++) {
            widths.add(14);
        }
        widths.add(20);
        return widths.stream().mapToInt(Integer::intValue).toArray();
    }

    private int[] buildSheet2ColumnWidths(List<PersonAmountModule> personModules) {
        List<Integer> widths = new ArrayList<>();
        widths.add(8);
        widths.add(24);
        widths.add(16);
        widths.add(24);
        appendQuarterBlockWidths(widths);
        for (int i = 0; i < personModules.size(); i++) {
            appendQuarterBlockWidths(widths);
        }
        return widths.stream().mapToInt(Integer::intValue).toArray();
    }

    private void appendQuarterBlockWidths(List<Integer> widths) {
        widths.add(12);
        widths.add(12);
        widths.add(12);
        widths.add(12);
        widths.add(14);
    }

    private void writeQuarterHeader(Row row, int startCol, CellStyle style) {
        setText(row, startCol, "一季度", style);
        setText(row, startCol + 1, "二季度", style);
        setText(row, startCol + 2, "三季度", style);
        setText(row, startCol + 3, "四季度", style);
        setText(row, startCol + 4, "本年度小计", style);
    }

    private int sheet1LastCol(List<RoleGroup> roleGroups, List<PersonAmountModule> adjustedPersonModules) {
        int totalColumns = SHEET1_FIXED_COL_COUNT + SHEET1_REMARK_COL_COUNT
                + adjustedPersonModules.size() * SHEET1_ADJUSTED_PERSON_WIDTH;
        for (RoleGroup roleGroup : roleGroups) {
            totalColumns += sheet1RoleWidth(roleGroup);
        }
        return totalColumns - 1;
    }

    private int sheet1AdjustedStartCol(List<RoleGroup> roleGroups) {
        int col = SHEET1_FIXED_COL_COUNT;
        for (RoleGroup roleGroup : roleGroups) {
            col += sheet1RoleWidth(roleGroup);
        }
        return col;
    }

    private int sheet1RoleWidth(RoleGroup roleGroup) {
        int personCount = roleGroup == null || roleGroup.getModules() == null ? 1 : roleGroup.getModules().size();
        return SHEET1_ROLE_RATIO_WIDTH + personCount * SHEET1_PERSON_RATIO_WIDTH;
    }

    private int sheet2LastCol(List<PersonAmountModule> personModules) {
        return SHEET2_FIXED_COL_COUNT + SHEET2_SPECIALTY_BLOCK_WIDTH
                + personModules.size() * SHEET2_PERSON_BLOCK_WIDTH - 1;
    }

    private int outputTypeOrder(String outputType) {
        String current = safeText(outputType);
        if (Objects.equals(current, "六大专业考核产值")) {
            return 1;
        }
        if (Objects.equals(current, "专项分包产值")) {
            return 2;
        }
        if (Objects.equals(current, "源头合作分包产值")) {
            return 3;
        }
        if (Objects.equals(current, "内部协作产值")) {
            return 4;
        }
        return 9;
    }

    private int lastDetailIndex(List<SpecialtyPlanningRow> rows) {
        for (int i = rows.size() - 1; i >= 0; i--) {
            if (!Boolean.TRUE.equals(rows.get(i).getTotalRow())) {
                return i;
            }
        }
        return -1;
    }

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

    private String resolveRoleName(List<RolePersonModule> modules, String roleCode) {
        if (modules != null) {
            for (RolePersonModule module : modules) {
                if (module != null && !safeText(module.getRoleName()).isEmpty()) {
                    return safeText(module.getRoleName());
                }
            }
        }
        return OutputSplitBizConstants.getRoleName(roleCode);
    }

    private String textOrBlank(BigDecimal value) {
        return value == null ? "" : 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 String specialtyName;
        private String projectManagerNames;
        private String engineeringPrincipalNames;
        private BigDecimal projectManagerRatio;
        private BigDecimal engineeringPrincipalRatio;
        private Integer year;
        private String centerSignerLabel;
        private String projectSignerLabel;
        private List<RolePersonModule> rolePersonModules;
        private List<PersonAmountModule> personAmountModules;
        private List<SpecialtyPlanningRow> rows;
    }

    @Data
    public static class RolePersonModule {
        private String key;
        private String roleCode;
        private String roleName;
        private String employeeName;
        private Boolean placeholder;
    }

    @Data
    public static class PersonAmountModule {
        private String key;
        private String employeeName;
        private Boolean placeholder;
    }

    @Data
    public static class SpecialtyPlanningRow {
        private Integer serialNo;
        private String projectName;
        private String outputType;
        private String designContent;
        private BigDecimal specialtyRatio;
        private BigDecimal specialtyQuarterOneAmountWan;
        private BigDecimal specialtyQuarterTwoAmountWan;
        private BigDecimal specialtyQuarterThreeAmountWan;
        private BigDecimal specialtyQuarterFourAmountWan;
        private BigDecimal specialtyYearTotalAmountWan;
        private BigDecimal adjustedPersonRatio;
        private String remark;
        private Boolean totalRow;
        private Map<String, BigDecimal> roleRatioMap = new LinkedHashMap<>();
        private Map<String, RolePersonRatioValue> rolePersonValueMap = new LinkedHashMap<>();
        private Map<String, BigDecimal> adjustedPersonRatioMap = new LinkedHashMap<>();
        private Map<String, PersonAmountValue> personAmountMap = new LinkedHashMap<>();
    }

    @Data
    public static class RolePersonRatioValue {
        private BigDecimal workRatio;
        private BigDecimal specialtyRatio;
    }

    @Data
    public static class PersonAmountValue {
        private BigDecimal quarterOneRatio;
        private BigDecimal quarterOneAmountWan;
        private BigDecimal quarterTwoRatio;
        private BigDecimal quarterTwoAmountWan;
        private BigDecimal quarterThreeRatio;
        private BigDecimal quarterThreeAmountWan;
        private BigDecimal quarterFourRatio;
        private BigDecimal quarterFourAmountWan;
        private BigDecimal yearTotalRatio;
        private BigDecimal yearTotalAmountWan;
    }

    @Data
    private static class RoleGroup {
        private String roleCode;
        private String roleName;
        private List<RolePersonModule> modules;
    }
}
