package tech.easyflow.datacenter.excel.service.impl;

import org.junit.Assert;
import org.junit.Test;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * {@link DatacenterExcelImportServiceImpl} 的 Excel 表头字段名处理测试。
 */
public class DatacenterExcelImportServiceImplTest {

    /**
     * 验证包含中文和常见标点的表头会转换为安全的物理字段名。
     */
    @Test
    public void shouldNormalizeExcelHeaderToSafeIdentifier() {
        Assert.assertEquals("", DatacenterExcelImportServiceImpl.normalizeIdentifier("金额（万元）"));
        Assert.assertEquals("amount_cny", DatacenterExcelImportServiceImpl.normalizeIdentifier("Amount（CNY）"));
        Assert.assertEquals("model_version", DatacenterExcelImportServiceImpl.normalizeIdentifier("模型 / Model Version"));
    }

    /**
     * 验证非 ASCII 表头、SQL 关键字和重复表头均会生成有效且唯一的字段名。
     */
    @Test
    public void shouldGenerateSafeUniqueExcelFieldNames() {
        Set<String> usedNames = new HashSet<>();

        Assert.assertEquals("col_1", DatacenterExcelImportServiceImpl.normalizeIdentifier("金额（万元）", 0, usedNames));
        Assert.assertEquals("col_select", DatacenterExcelImportServiceImpl.normalizeIdentifier("select", 1, usedNames));
        Assert.assertEquals("col_id", DatacenterExcelImportServiceImpl.normalizeIdentifier("模型ID", 2, usedNames));
        Assert.assertEquals("col_id_1", DatacenterExcelImportServiceImpl.normalizeIdentifier("部门ID", 3, usedNames));
        Assert.assertEquals("amount", DatacenterExcelImportServiceImpl.normalizeIdentifier("Amount", 4, usedNames));
        Assert.assertEquals("amount_1", DatacenterExcelImportServiceImpl.normalizeIdentifier("Amount", 5, usedNames));
        Assert.assertEquals("col_order", DatacenterExcelImportServiceImpl.normalizeIdentifier("Order", 6, usedNames));
        Assert.assertEquals("col_group", DatacenterExcelImportServiceImpl.normalizeIdentifier("Group", 7, usedNames));
    }

    /**
     * 验证超长异常摘要会保留首尾信息并限制在数据库字段长度内。
     */
    @Test
    public void shouldTruncateLongImportErrorSummary() {
        String message = "起始原因:" + "a".repeat(1200) + ":最终原因";

        String summary = DatacenterExcelImportServiceImpl.summarizeImportError(new IllegalArgumentException(message));

        Assert.assertEquals(1000, summary.codePointCount(0, summary.length()));
        Assert.assertTrue(summary.startsWith("起始原因:"));
        Assert.assertTrue(summary.contains("…（错误摘要已截断）…"));
        Assert.assertTrue(summary.endsWith(":最终原因"));
    }

    /**
     * 验证仅允许 xls 和 xlsx 文件名通过服务端扩展名校验。
     */
    @Test
    public void shouldAcceptOnlySupportedWorkbookExtensions() {
        Assert.assertTrue(DatacenterExcelImportServiceImpl.isSupportedWorkbookFileName("预算表.XLS"));
        Assert.assertTrue(DatacenterExcelImportServiceImpl.isSupportedWorkbookFileName("预算表.xlsx"));
        Assert.assertFalse(DatacenterExcelImportServiceImpl.isSupportedWorkbookFileName("预算表.xlsm"));
        Assert.assertFalse(DatacenterExcelImportServiceImpl.isSupportedWorkbookFileName("预算表.csv"));
    }

    /**
     * 验证当前 WorkbookFactory 能解析 xls 和 xlsx 工作簿。
     *
     * @throws IOException 工作簿序列化或读取失败时抛出
     */
    @Test
    public void shouldParseXlsAndXlsxWorkbooks() throws IOException {
        assertWorkbookCanBeParsed(new HSSFWorkbook());
        assertWorkbookCanBeParsed(new XSSFWorkbook());
    }

    /**
     * 验证自定义连接名称优先使用，空名称回退到文件名。
     */
    @Test
    public void shouldResolveImportSourceName() {
        Assert.assertEquals(
                "模型预算",
                DatacenterExcelImportServiceImpl.resolveImportSourceName(" 模型预算 ", "原文件.xlsx")
        );
        Assert.assertEquals(
                "原文件",
                DatacenterExcelImportServiceImpl.resolveImportSourceName(" ", "原文件.xlsx")
        );
    }

    /**
     * 验证连接名称超过数据库字段长度时给出业务错误。
     */
    @Test
    public void shouldRejectTooLongImportSourceName() {
        BusinessException error = Assert.assertThrows(
                BusinessException.class,
                () -> DatacenterExcelImportServiceImpl.resolveImportSourceName("长".repeat(101), "原文件.xlsx")
        );

        Assert.assertEquals("连接名称不能超过 100 个字符", error.getMessage());
    }

    /**
     * 验证空工作簿、无表头和只有表头的工作表均不可导入。
     */
    @Test
    public void shouldRejectWorkbookWithoutHeaderAndData() {
        DataFormatter formatter = new DataFormatter();
        try (Workbook workbook = new XSSFWorkbook()) {
            Assert.assertFalse(DatacenterExcelImportServiceImpl.hasImportableSheet(workbook, formatter));

            var sheet = workbook.createSheet("空表");
            sheet.createRow(0).createCell(0).setCellValue("");
            Assert.assertFalse(DatacenterExcelImportServiceImpl.hasImportableSheet(workbook, formatter));

            sheet.getRow(0).getCell(0).setCellValue("模型ID");
            Assert.assertFalse(DatacenterExcelImportServiceImpl.hasImportableSheet(workbook, formatter));

            sheet.createRow(1).createCell(0).setCellValue("deepseek-v4-pro");
            Assert.assertTrue(DatacenterExcelImportServiceImpl.hasImportableSheet(workbook, formatter));
        } catch (IOException ex) {
            Assert.fail(ex.getMessage());
        }
    }

    /**
     * 验证公式单元格导入计算结果，不暴露公式文本。
     */
    @Test
    public void shouldImportCalculatedFormulaValue() {
        DataFormatter formatter = new DataFormatter();
        try (Workbook workbook = new XSSFWorkbook()) {
            var row = workbook.createSheet("公式").createRow(0);
            row.createCell(0).setCellValue(3);
            var formulaCell = row.createCell(1);
            formulaCell.setCellFormula("A1*2");

            String value = DatacenterExcelImportServiceImpl.formatCellValue(
                    formulaCell,
                    formatter,
                    workbook.getCreationHelper().createFormulaEvaluator()
            );

            Assert.assertEquals("6", value);
        } catch (IOException ex) {
            Assert.fail(ex.getMessage());
        }
    }

    /**
     * 将工作簿序列化后再交给导入使用的解析器读取。
     *
     * @param workbook 待验证的工作簿
     * @throws IOException 工作簿序列化或读取失败时抛出
     */
    private void assertWorkbookCanBeParsed(Workbook workbook) throws IOException {
        workbook.createSheet("Sheet1");
        try (workbook; ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            workbook.write(outputStream);
            try (Workbook parsedWorkbook = WorkbookFactory.create(new ByteArrayInputStream(outputStream.toByteArray()))) {
                Assert.assertEquals(1, parsedWorkbook.getNumberOfSheets());
            }
        }
    }
}
