package com.ruoyi.info.collection.utils;

import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class EasyExcelUtilImportDropdownValidationTest {

    @Test
    void importExcel_shouldPassWhenAllDictDropdownColumnsKeepListValidation() throws Exception {
        byte[] bytes = baseStaffWorkbook(true, true, true, 2);

        List<CcdiBaseStaffExcel> rows = EasyExcelUtil.importExcel(
                new ByteArrayInputStream(bytes),
                CcdiBaseStaffExcel.class,
                "员工信息"
        );

        assertEquals(2, rows.size());
    }

    @Test
    void importExcel_shouldFailWhenPartyMemberDropdownIsMissing() throws Exception {
        byte[] bytes = baseStaffWorkbook(false, true, true, 2);

        ServiceException exception = assertThrows(ServiceException.class, () ->
                EasyExcelUtil.importExcel(new ByteArrayInputStream(bytes), CcdiBaseStaffExcel.class, "员工信息")
        );

        assertTrue(exception.getMessage().contains("是否党员 列缺少下拉框"));
    }

    @Test
    void importExcel_shouldFailWhenStatusDropdownIsMissing() throws Exception {
        byte[] bytes = baseStaffWorkbook(true, false, true, 2);

        ServiceException exception = assertThrows(ServiceException.class, () ->
                EasyExcelUtil.importExcel(new ByteArrayInputStream(bytes), CcdiBaseStaffExcel.class, "员工信息")
        );

        assertEquals("员工信息 Sheet 的 状态 列缺少下拉框，请下载最新导入模板填写后重新导入", exception.getMessage());
    }

    @Test
    void importExcel_shouldReportAllMissingDropdownColumnsInSameSheet() throws Exception {
        byte[] bytes = baseStaffWorkbook(false, false, true, 2);

        ServiceException exception = assertThrows(ServiceException.class, () ->
                EasyExcelUtil.importExcel(new ByteArrayInputStream(bytes), CcdiBaseStaffExcel.class, "员工信息")
        );

        assertEquals("员工信息 Sheet 的 是否党员、状态 列缺少下拉框，请下载最新导入模板填写后重新导入", exception.getMessage());
    }

    @Test
    void importExcel_shouldFailWhenValidationIsNotListType() throws Exception {
        byte[] bytes = baseStaffWorkbook(true, true, false, 2);

        ServiceException exception = assertThrows(ServiceException.class, () ->
                EasyExcelUtil.importExcel(new ByteArrayInputStream(bytes), CcdiBaseStaffExcel.class, "员工信息")
        );

        assertTrue(exception.getMessage().contains("状态 列缺少下拉框"));
    }

    @Test
    void importExcel_shouldFailWhenListValidationDoesNotCoverEveryActualDataRow() throws Exception {
        byte[] bytes = baseStaffWorkbook(true, true, true, 1);

        ServiceException exception = assertThrows(ServiceException.class, () ->
                EasyExcelUtil.importExcel(new ByteArrayInputStream(bytes), CcdiBaseStaffExcel.class, "员工信息")
        );

        assertTrue(exception.getMessage().contains("状态 列缺少下拉框"));
    }

    @Test
    void importExcel_shouldFailWhenSecondSheetDropdownIsMissing() throws Exception {
        byte[] bytes = baseStaffDualSheetWorkbookWithMissingAssetStatusDropdown();

        ServiceException exception = assertThrows(ServiceException.class, () ->
                EasyExcelUtil.importExcel(new ByteArrayInputStream(bytes), CcdiBaseStaffAssetInfoExcel.class, "员工资产信息")
        );

        assertEquals("员工资产信息 Sheet 的 资产状态 列缺少下拉框，请下载最新导入模板填写后重新导入", exception.getMessage());
    }

    @Test
    void importExcel_shouldSkipDropdownStructureValidationWhenClassHasNoDictDropdownFields() throws Exception {
        byte[] bytes = plainWorkbookWithoutDropdown();

        List<PlainExcel> rows = EasyExcelUtil.importExcel(
                new ByteArrayInputStream(bytes),
                PlainExcel.class,
                "普通信息"
        );

        assertEquals(1, rows.size());
    }

    private byte[] baseStaffWorkbook(boolean partyDropdown, boolean statusDropdown, boolean statusAsList,
                                     int statusLastRow) throws Exception {
        try (Workbook workbook = new XSSFWorkbook();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            Sheet sheet = workbook.createSheet("员工信息");
            Row header = sheet.createRow(0);
            String[] headers = {"姓名", "员工ID", "所属部门ID", "身份证号", "电话", "年收入（元/年）",
                    "入职时间", "是否党员", "状态"};
            for (int i = 0; i < headers.length; i++) {
                header.createCell(i).setCellValue(headers[i]);
            }
            createBaseStaffRow(sheet, 1, "张三", 9020001L, "33010619850202101X", "0", "1");
            createBaseStaffRow(sheet, 2, "李四", 9020002L, "330106198603031022", "1", "1");

            if (partyDropdown) {
                addListValidation(sheet, 7, 1, 2, "0", "1");
            }
            if (statusDropdown) {
                if (statusAsList) {
                    addListValidation(sheet, 8, 1, statusLastRow, "0", "1");
                } else {
                    addIntegerValidation(sheet, 8, 1, 2);
                }
            }

            workbook.write(outputStream);
            return outputStream.toByteArray();
        }
    }

    private byte[] baseStaffDualSheetWorkbookWithMissingAssetStatusDropdown() throws Exception {
        try (Workbook workbook = new XSSFWorkbook();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            Sheet staffSheet = workbook.createSheet("员工信息");
            Row staffHeader = staffSheet.createRow(0);
            String[] staffHeaders = {"姓名", "员工ID", "所属部门ID", "身份证号", "电话", "年收入（元/年）",
                    "入职时间", "是否党员", "状态"};
            for (int i = 0; i < staffHeaders.length; i++) {
                staffHeader.createCell(i).setCellValue(staffHeaders[i]);
            }
            createBaseStaffRow(staffSheet, 1, "张三", 9020001L, "33010619850202101X", "0", "1");
            addListValidation(staffSheet, 7, 1, 1, "0", "1");
            addListValidation(staffSheet, 8, 1, 1, "0", "1");

            Sheet assetSheet = workbook.createSheet("员工资产信息");
            Row assetHeader = assetSheet.createRow(0);
            String[] assetHeaders = {"员工身份证号*", "资产大类*", "资产小类*", "资产名称*", "产权占比",
                    "购买/评估日期", "资产原值", "当前估值*", "估值截止日期", "资产状态*", "备注"};
            for (int i = 0; i < assetHeaders.length; i++) {
                assetHeader.createCell(i).setCellValue(assetHeaders[i]);
            }
            Row assetRow = assetSheet.createRow(1);
            assetRow.createCell(0).setCellValue("33010619850202101X");
            assetRow.createCell(1).setCellValue("房产");
            assetRow.createCell(2).setCellValue("住宅");
            assetRow.createCell(3).setCellValue("测试住宅");
            assetRow.createCell(7).setCellValue(1000000D);
            assetRow.createCell(9).setCellValue("正常");

            workbook.write(outputStream);
            return outputStream.toByteArray();
        }
    }

    private void createBaseStaffRow(Sheet sheet, int rowIndex, String name, long staffId, String idCard,
                                    String partyMember, String status) {
        Row row = sheet.createRow(rowIndex);
        row.createCell(0).setCellValue(name);
        row.createCell(1).setCellValue(staffId);
        row.createCell(2).setCellValue(103L);
        row.createCell(3, CellType.STRING).setCellValue(idCard);
        row.createCell(4, CellType.STRING).setCellValue("13370000001");
        row.createCell(5).setCellValue(new BigDecimal("180000").doubleValue());
        row.createCell(6).setCellValue("2026-04-30");
        row.createCell(7, CellType.STRING).setCellValue(partyMember);
        row.createCell(8, CellType.STRING).setCellValue(status);
    }

    private void addListValidation(Sheet sheet, int columnIndex, int firstRow, int lastRow, String... options) {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        DataValidationConstraint constraint = helper.createExplicitListConstraint(options);
        DataValidation validation = helper.createValidation(
                constraint,
                new CellRangeAddressList(firstRow, lastRow, columnIndex, columnIndex)
        );
        sheet.addValidationData(validation);
    }

    private void addIntegerValidation(Sheet sheet, int columnIndex, int firstRow, int lastRow) {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        DataValidationConstraint constraint = helper.createIntegerConstraint(
                DataValidationConstraint.OperatorType.BETWEEN,
                "0",
                "1"
        );
        DataValidation validation = helper.createValidation(
                constraint,
                new CellRangeAddressList(firstRow, lastRow, columnIndex, columnIndex)
        );
        sheet.addValidationData(validation);
    }

    private byte[] plainWorkbookWithoutDropdown() throws Exception {
        try (Workbook workbook = new XSSFWorkbook();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            Sheet sheet = workbook.createSheet("普通信息");
            Row header = sheet.createRow(0);
            header.createCell(0).setCellValue("名称");
            Row row = sheet.createRow(1);
            row.createCell(0).setCellValue("张三");
            workbook.write(outputStream);
            return outputStream.toByteArray();
        }
    }

    public static class PlainExcel {
        @ExcelProperty(value = "名称", index = 0)
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
