package com.ruoyi.info.collection.utils;

import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.info.collection.domain.excel.CcdiAccountInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import com.ruoyi.info.collection.domain.excel.CcdiCustEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.excel.CcdiCustFmyRelationExcel;
import com.ruoyi.info.collection.domain.excel.CcdiEnterpriseBaseInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEntityExcel;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryPersonExcel;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionExcel;
import com.ruoyi.info.collection.domain.excel.CcdiPurchaseTransactionSupplierExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffRecruitmentExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffRecruitmentWorkExcel;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataValidation;
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.CellRangeAddress;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.mock.web.MockHttpServletResponse;

import java.io.ByteArrayInputStream;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mockStatic;

class EasyExcelUtilTemplateTest {

    @Test
    void importTemplateWithDictDropdown_shouldAddAssetStatusDropdownToAssetTemplate() throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();

        try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
            mocked.when(() -> DictUtils.getDictCache("ccdi_asset_status"))
                    .thenReturn(List.of(
                            buildDictData("正常"),
                            buildDictData("冻结"),
                            buildDictData("处置中"),
                            buildDictData("报废")
                    ));

            EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiAssetInfoExcel.class, "亲属资产信息");
        }

        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
            Sheet sheet = workbook.getSheetAt(0);
            assertTrue(hasValidationOnColumn(sheet, 9), "资产状态列应包含下拉校验");
        }
    }

    @Test
    void importTemplateWithDictDropdown_shouldFormatCertificateColumnsAsText() throws Exception {
        MockHttpServletResponse assetResponse = new MockHttpServletResponse();
        MockHttpServletResponse familyResponse = new MockHttpServletResponse();

        try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
            mocked.when(() -> DictUtils.getDictCache("ccdi_asset_status"))
                    .thenReturn(List.of(buildDictData("正常")));
            mocked.when(() -> DictUtils.getDictCache("ccdi_relation_type"))
                    .thenReturn(List.of(buildDictData("配偶")));
            mocked.when(() -> DictUtils.getDictCache("ccdi_indiv_gender"))
                    .thenReturn(List.of(buildDictData("男")));
            mocked.when(() -> DictUtils.getDictCache("ccdi_certificate_type"))
                    .thenReturn(List.of(buildDictData("居民身份证")));

            EasyExcelUtil.importTemplateWithDictDropdown(assetResponse, CcdiAssetInfoExcel.class, "亲属资产信息");
            EasyExcelUtil.importTemplateWithDictDropdown(familyResponse, CcdiStaffFmyRelationExcel.class, "员工亲属关系信息");
        }

        try (Workbook assetWorkbook = WorkbookFactory.create(new ByteArrayInputStream(assetResponse.getContentAsByteArray()));
             Workbook familyWorkbook = WorkbookFactory.create(new ByteArrayInputStream(familyResponse.getContentAsByteArray()))) {
            assertTextColumn(assetWorkbook.getSheetAt(0), 0);
            assertTextColumn(familyWorkbook.getSheetAt(0), 6);
        }
    }

    @Test
    void importTemplateWithDictDropdown_shouldAddPartyMemberDropdownToBaseStaffTemplate() throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();

        try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
            mocked.when(() -> DictUtils.getDictCache("ccdi_employee_status"))
                    .thenReturn(List.of(
                            buildDictData("在职", "0"),
                            buildDictData("离职", "1")
                    ));
            mocked.when(() -> DictUtils.getDictCache("ccdi_yes_no_flag"))
                    .thenReturn(List.of(
                            buildDictData("是", "1"),
                            buildDictData("否", "0")
                    ));

            EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiBaseStaffExcel.class, "员工信息");
        }

        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
            Sheet sheet = workbook.getSheetAt(0);
            assertTrue(hasValidationOnColumn(sheet, 7), "是否党员列应包含下拉校验");
            assertHeaderValue(sheet, 0, "姓名*");
            assertHeaderValue(sheet, 1, "员工ID*");
            assertHeaderValue(sheet, 2, "所属部门ID*");
            assertHeaderValue(sheet, 3, "身份证号*");
            assertHeaderValue(sheet, 4, "电话*");
            assertHeaderValue(sheet, 7, "是否党员*");
            assertHeaderValue(sheet, 8, "状态*");
            assertTextColumn(sheet, 3);
            assertTextColumn(sheet, 4);
        }
    }

    @Test
    void importTemplateWithDictDropdown_shouldKeepBaseStaffDualSheetColumnWidths() throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();

        try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
            mocked.when(() -> DictUtils.getDictCache("ccdi_employee_status"))
                    .thenReturn(List.of(
                            buildDictData("在职", "0"),
                            buildDictData("离职", "1")
                    ));
            mocked.when(() -> DictUtils.getDictCache("ccdi_yes_no_flag"))
                    .thenReturn(List.of(
                            buildDictData("是", "1"),
                            buildDictData("否", "0")
                    ));
            mocked.when(() -> DictUtils.getDictCache("ccdi_asset_status"))
                    .thenReturn(List.of(
                            buildDictData("正常"),
                            buildDictData("冻结"),
                            buildDictData("处置中")
                    ));

            EasyExcelUtil.importTemplateWithDictDropdown(
                    response,
                    CcdiBaseStaffExcel.class,
                    "员工信息",
                    CcdiBaseStaffAssetInfoExcel.class,
                    "员工资产信息",
                    "员工信息维护导入模板"
            );
        }

        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
            Sheet staffSheet = workbook.getSheet("员工信息");
            Sheet assetSheet = workbook.getSheet("员工资产信息");
            assertNotNull(staffSheet);
            assertNotNull(assetSheet);

            assertColumnWidthsAtLeast(staffSheet, new int[] {16, 18, 20, 24, 18, 20, 18, 16, 14});
            assertColumnWidthsAtLeast(assetSheet, new int[] {24, 18, 18, 24, 14, 20, 18, 18, 20, 16, 32});
            assertHeaderValue(staffSheet, 0, "姓名*");
            assertHeaderValue(staffSheet, 8, "状态*");
            assertHeaderValue(assetSheet, 0, "员工身份证号*");
            assertHeaderValue(assetSheet, 1, "资产大类*");
            assertHeaderValue(assetSheet, 10, "备注");
        }
    }

    @Test
    void importTemplateWithDictDropdown_shouldCreateRecruitmentDualSheetTemplate() throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();

        try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
            mocked.when(() -> DictUtils.getDictCache("ccdi_admit_status"))
                .thenReturn(List.of(
                    buildDictData("录用"),
                    buildDictData("未录用"),
                    buildDictData("放弃")
                ));
            mocked.when(() -> DictUtils.getDictCache("ccdi_recruit_type"))
                .thenReturn(List.of(
                    buildDictData("社招", "SOCIAL"),
                    buildDictData("校招", "CAMPUS")
                ));

            EasyExcelUtil.importTemplateWithDictDropdown(
                response,
                CcdiStaffRecruitmentExcel.class,
                "招聘信息",
                CcdiStaffRecruitmentWorkExcel.class,
                "历史工作经历",
                "招聘信息管理导入模板"
            );
        }

        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
            assertEquals(2, workbook.getNumberOfSheets(), "招聘导入模板应输出双Sheet");
            assertEquals("招聘信息", workbook.getSheetAt(0).getSheetName());
            assertEquals("历史工作经历", workbook.getSheetAt(1).getSheetName());
            assertTrue(hasValidationOnColumn(workbook.getSheetAt(0), 5), "录用情况列应包含下拉校验");
            assertTrue(hasValidationOnColumn(workbook.getSheetAt(0), 7), "招聘类型列应包含下拉校验");
        }
    }

    @Test
    void importTemplateWithDictDropdown_shouldUseUpdatedEnterpriseHeaders() throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();

        try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
            mocked.when(() -> DictUtils.getDictCache("ccdi_entity_type"))
                    .thenReturn(List.of(buildDictData("有限责任公司")));
            mocked.when(() -> DictUtils.getDictCache("ccdi_enterprise_nature"))
                    .thenReturn(List.of(buildDictData("民营企业")));
            mocked.when(() -> DictUtils.getDictCache("ccdi_certificate_type"))
                    .thenReturn(List.of(buildDictData("居民身份证")));

            EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiEnterpriseBaseInfoExcel.class, "实体库管理");
        }

        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
            Row headerRow = workbook.getSheetAt(0).getRow(0);
            assertEquals("经营状态", headerRow.getCell(16).getStringCellValue());
            assertEquals("风险等级*", headerRow.getCell(17).getStringCellValue());
            assertEquals("企业来源*", headerRow.getCell(18).getStringCellValue());
            assertEquals(19, headerRow.getLastCellNum());
        }
    }

    @Test
    void infoImportTemplates_shouldFormatIdentifierAndContactColumnsAsText() throws Exception {
        try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
            mockCommonDicts(mocked);

            assertPlainTemplateTextColumns(CcdiAccountInfoExcel.class, "账户库管理", 1, 3, 7);
            assertSingleTemplateTextColumns(CcdiAssetInfoExcel.class, "亲属资产信息", 0);
            assertSingleTemplateTextColumns(CcdiBaseStaffAssetInfoExcel.class, "员工资产信息", 0);
            assertDualTemplateTextColumns(
                    CcdiBaseStaffExcel.class,
                    "员工信息",
                    new int[] {3, 4},
                    CcdiBaseStaffAssetInfoExcel.class,
                    "员工资产信息",
                    new int[] {0},
                    "员工信息维护导入模板"
            );
            assertSingleTemplateTextColumns(CcdiCustEnterpriseRelationExcel.class, "信贷客户实体关联信息", 0, 1);
            assertSingleTemplateTextColumns(CcdiCustFmyRelationExcel.class, "信贷客户家庭关系", 0, 6, 7, 8);
            assertSingleTemplateTextColumns(CcdiEnterpriseBaseInfoExcel.class, "实体库管理", 0, 10);
            assertSingleTemplateTextColumns(CcdiIntermediaryPersonExcel.class, "个人中介信息", 5, 6, 10, 12);
            assertSingleTemplateTextColumns(CcdiIntermediaryEntityExcel.class, "实体中介信息", 1, 10);
            assertSingleTemplateTextColumns(CcdiIntermediaryEnterpriseRelationExcel.class, "中介实体关联关系信息", 0, 1);
            assertDualTemplateTextColumns(
                    CcdiPurchaseTransactionExcel.class,
                    "招投标主信息",
                    new int[] {0, 21, 24},
                    CcdiPurchaseTransactionSupplierExcel.class,
                    "供应商信息",
                    new int[] {0, 2, 4, 5},
                    "招投标信息维护导入模板"
            );
            assertSingleTemplateTextColumns(CcdiStaffEnterpriseRelationExcel.class, "员工亲属实体关联", 0, 1);
            assertSingleTemplateTextColumns(CcdiStaffFmyRelationExcel.class, "员工亲属关系信息", 0, 6, 7, 8);
            assertDualTemplateTextColumns(
                    CcdiStaffRecruitmentExcel.class,
                    "招聘信息",
                    new int[] {0, 9, 14, 16},
                    CcdiStaffRecruitmentWorkExcel.class,
                    "历史工作经历",
                    new int[] {0},
                    "招聘信息管理导入模板"
            );
        }
    }

    private void assertTextColumn(Sheet sheet, int columnIndex) {
        CellStyle style = sheet.getColumnStyle(columnIndex);
        assertNotNull(style, "文本列应设置默认样式");
        assertEquals("@", style.getDataFormatString(), "证件号列应使用文本格式");
    }

    private void assertHeaderValue(Sheet sheet, int columnIndex, String expectedValue) {
        assertEquals(expectedValue, sheet.getRow(0).getCell(columnIndex).getStringCellValue());
    }

    private void assertTextColumns(Sheet sheet, int... columnIndexes) {
        for (int columnIndex : columnIndexes) {
            assertTextColumn(sheet, columnIndex);
        }
    }

    private void assertPlainTemplateTextColumns(Class<?> clazz, String sheetName, int... columnIndexes) throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();
        EasyExcelUtil.importTemplateExcel(response, clazz, sheetName);

        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
            assertTextColumns(workbook.getSheetAt(0), columnIndexes);
        }
    }

    private void assertSingleTemplateTextColumns(Class<?> clazz, String sheetName, int... columnIndexes) throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();
        EasyExcelUtil.importTemplateWithDictDropdown(response, clazz, sheetName);

        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
            assertTextColumns(workbook.getSheetAt(0), columnIndexes);
        }
    }

    private <T1, T2> void assertDualTemplateTextColumns(Class<T1> firstClazz,
                                                        String firstSheetName,
                                                        int[] firstColumnIndexes,
                                                        Class<T2> secondClazz,
                                                        String secondSheetName,
                                                        int[] secondColumnIndexes,
                                                        String fileName) throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();
        EasyExcelUtil.importTemplateWithDictDropdown(
                response,
                firstClazz,
                firstSheetName,
                secondClazz,
                secondSheetName,
                fileName
        );

        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
            assertTextColumns(workbook.getSheet(firstSheetName), firstColumnIndexes);
            assertTextColumns(workbook.getSheet(secondSheetName), secondColumnIndexes);
        }
    }

    private void assertColumnWidthsAtLeast(Sheet sheet, int[] expectedWidths) {
        for (int columnIndex = 0; columnIndex < expectedWidths.length; columnIndex++) {
            int currentColumnIndex = columnIndex;
            int expectedWidth = expectedWidths[currentColumnIndex];
            int expected = expectedWidth * 256;
            assertTrue(sheet.getColumnWidth(currentColumnIndex) >= expected,
                    () -> sheet.getSheetName() + "第" + (currentColumnIndex + 1) + "列宽度应不小于" + expectedWidth);
        }
    }

    private boolean hasValidationOnColumn(Sheet sheet, int columnIndex) {
        for (DataValidation validation : sheet.getDataValidations()) {
            for (CellRangeAddress address : validation.getRegions().getCellRangeAddresses()) {
                if (address.getFirstColumn() <= columnIndex && address.getLastColumn() >= columnIndex) {
                    return true;
                }
            }
        }
        return false;
    }

    private void mockCommonDicts(MockedStatic<DictUtils> mocked) {
        mocked.when(() -> DictUtils.getDictCache("ccdi_asset_status"))
                .thenReturn(List.of(buildDictData("正常")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_employee_status"))
                .thenReturn(List.of(buildDictData("在职", "1")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_yes_no_flag"))
                .thenReturn(List.of(buildDictData("是", "1")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_relation_type"))
                .thenReturn(List.of(buildDictData("配偶")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_indiv_gender"))
                .thenReturn(List.of(buildDictData("男")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_certificate_type"))
                .thenReturn(List.of(buildDictData("居民身份证")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_entity_type"))
                .thenReturn(List.of(buildDictData("有限责任公司")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_enterprise_nature"))
                .thenReturn(List.of(buildDictData("民营企业")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_person_type"))
                .thenReturn(List.of(buildDictData("中介")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_person_sub_type"))
                .thenReturn(List.of(buildDictData("本人")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_admit_status"))
                .thenReturn(List.of(buildDictData("录用")));
        mocked.when(() -> DictUtils.getDictCache("ccdi_recruit_type"))
                .thenReturn(List.of(buildDictData("社招", "SOCIAL")));
    }

    private SysDictData buildDictData(String label) {
        return buildDictData(label, label);
    }

    private SysDictData buildDictData(String label, String value) {
        SysDictData dictData = new SysDictData();
        dictData.setDictLabel(label);
        dictData.setDictValue(value);
        return dictData;
    }
}
