package com.ruoyi.info.collection.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.utils.IdCardUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.info.collection.domain.CcdiStaffRecruitment;
import com.ruoyi.info.collection.domain.CcdiStaffRecruitmentWork;
import com.ruoyi.info.collection.domain.dto.CcdiStaffRecruitmentAddDTO;
import com.ruoyi.info.collection.domain.excel.CcdiStaffRecruitmentExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffRecruitmentWorkExcel;
import com.ruoyi.info.collection.domain.vo.ImportResult;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.domain.vo.RecruitmentImportFailureVO;
import com.ruoyi.info.collection.enums.AdmitStatus;
import com.ruoyi.info.collection.enums.RecruitType;
import com.ruoyi.info.collection.mapper.CcdiStaffRecruitmentMapper;
import com.ruoyi.info.collection.mapper.CcdiStaffRecruitmentWorkMapper;
import com.ruoyi.info.collection.service.ICcdiStaffRecruitmentImportService;
import com.ruoyi.info.collection.utils.ImportLogUtils;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 招聘信息异步导入Service实现
 *
 * @author ruoyi
 * @date 2026-02-06
 */
@Service
@EnableAsync
public class CcdiStaffRecruitmentImportServiceImpl implements ICcdiStaffRecruitmentImportService {

    private static final Logger log = LoggerFactory.getLogger(CcdiStaffRecruitmentImportServiceImpl.class);

    private static final String MAIN_SHEET_NAME = "招聘信息";
    private static final String WORK_SHEET_NAME = "历史工作经历";
    private static final int EXCEL_DATA_START_ROW = 2;

    @Resource
    private CcdiStaffRecruitmentMapper recruitmentMapper;

    @Resource
    private CcdiStaffRecruitmentWorkMapper recruitmentWorkMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Async
    @Transactional
    public void importRecruitmentAsync(List<CcdiStaffRecruitmentExcel> recruitmentList,
                                       List<CcdiStaffRecruitmentWorkExcel> workList,
                                       String taskId,
                                       String userName) {
        List<CcdiStaffRecruitmentExcel> safeRecruitmentList = recruitmentList == null
            ? Collections.emptyList()
            : recruitmentList;
        List<CcdiStaffRecruitmentWorkExcel> safeWorkList = workList == null
            ? Collections.emptyList()
            : workList;
        int totalCount = safeRecruitmentList.size() + safeWorkList.size();
        long startTime = System.currentTimeMillis();

        ImportLogUtils.logImportStart(log, taskId, "招聘信息双Sheet", totalCount, userName);

        List<RecruitmentImportFailureVO> failures = new ArrayList<>();
        List<MainImportRow> indexedMainRows = buildMainImportRows(safeRecruitmentList);
        List<WorkImportRow> indexedWorkRows = buildWorkImportRows(safeWorkList);

        MainImportResult mainImportResult = importMainSheet(indexedMainRows, failures, userName, taskId);
        int workSuccessCount = importWorkSheet(
            indexedWorkRows,
            mainImportResult.importedRecruitmentMap(),
            failures,
            userName,
            taskId
        );

        if (!failures.isEmpty()) {
            saveFailures(taskId, failures);
        }

        ImportResult result = new ImportResult();
        result.setTotalCount(totalCount);
        result.setSuccessCount(mainImportResult.successCount() + workSuccessCount);
        result.setFailureCount(Math.max(totalCount - result.getSuccessCount(), 0));

        String finalStatus = resolveFinalStatus(result);
        updateImportStatus(taskId, finalStatus, result);

        long duration = System.currentTimeMillis() - startTime;
        ImportLogUtils.logImportComplete(
            log,
            taskId,
            "招聘信息双Sheet",
            totalCount,
            result.getSuccessCount(),
            result.getFailureCount(),
            duration
        );
    }

    @Override
    public ImportStatusVO getImportStatus(String taskId) {
        String key = "import:recruitment:" + taskId;
        Boolean hasKey = redisTemplate.hasKey(key);

        if (Boolean.FALSE.equals(hasKey)) {
            throw new RuntimeException("任务不存在或已过期");
        }

        Map<Object, Object> statusMap = redisTemplate.opsForHash().entries(key);

        ImportStatusVO statusVO = new ImportStatusVO();
        statusVO.setTaskId((String) statusMap.get("taskId"));
        statusVO.setStatus((String) statusMap.get("status"));
        statusVO.setTotalCount((Integer) statusMap.get("totalCount"));
        statusVO.setSuccessCount((Integer) statusMap.get("successCount"));
        statusVO.setFailureCount((Integer) statusMap.get("failureCount"));
        statusVO.setProgress((Integer) statusMap.get("progress"));
        statusVO.setStartTime((Long) statusMap.get("startTime"));
        statusVO.setEndTime((Long) statusMap.get("endTime"));
        statusVO.setMessage((String) statusMap.get("message"));

        return statusVO;
    }

    @Override
    public List<RecruitmentImportFailureVO> getImportFailures(String taskId) {
        String key = "import:recruitment:" + taskId + ":failures";
        Object failuresObj = redisTemplate.opsForValue().get(key);

        if (failuresObj == null) {
            return Collections.emptyList();
        }

        return JSON.parseArray(JSON.toJSONString(failuresObj), RecruitmentImportFailureVO.class);
    }

    private MainImportResult importMainSheet(List<MainImportRow> mainRows,
                                             List<RecruitmentImportFailureVO> failures,
                                             String userName,
                                             String taskId) {
        if (mainRows.isEmpty()) {
            return new MainImportResult(Collections.emptyMap(), 0);
        }

        Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> importedRecruitmentMap = new LinkedHashMap<>();
        int successCount = 0;

        for (int index = 0; index < mainRows.size(); index++) {
            MainImportRow mainRow = mainRows.get(index);
            CcdiStaffRecruitmentExcel excel = mainRow.data();
            try {
                CcdiStaffRecruitmentAddDTO addDTO = new CcdiStaffRecruitmentAddDTO();
                BeanUtils.copyProperties(excel, addDTO);
                addDTO.setRecruitType(normalizeRecruitType(excel.getRecruitType()));

                validateRecruitmentData(addDTO, mainRow.sheetRowNum());

                String recruitId = trim(excel.getRecruitId());
                CcdiStaffRecruitment recruitment = new CcdiStaffRecruitment();
                BeanUtils.copyProperties(excel, recruitment);
                recruitment.setRecruitId(recruitId);
                recruitment.setRecruitType(addDTO.getRecruitType());
                recruitment.setCreatedBy(userName);
                recruitment.setUpdatedBy(userName);
                recruitmentMapper.insert(recruitment);
                successCount++;
                addRecruitment(importedRecruitmentMap, recruitment);

                ImportLogUtils.logProgress(log, taskId, index + 1, mainRows.size(), successCount, failures.size());
            } catch (Exception exception) {
                FailureMeta failureMeta = resolveFailureMeta(exception, List.of(mainRow.sheetRowNum()), MAIN_SHEET_NAME);
                failures.add(buildFailure(excel, failureMeta.sheetName(), failureMeta.sheetRowNum(), exception.getMessage()));
                ImportLogUtils.logValidationError(
                    log,
                    taskId,
                    index + 1,
                    exception.getMessage(),
                    String.format("招聘记录编号=%s, 项目名称=%s, 应聘人员=%s", excel.getRecruitId(), excel.getRecruitName(), excel.getCandName())
                );
            }
        }

        return new MainImportResult(importedRecruitmentMap, successCount);
    }

    private int importWorkSheet(List<WorkImportRow> workRows,
                                Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> importedRecruitmentMap,
                                List<RecruitmentImportFailureVO> failures,
                                String userName,
                                String taskId) {
        if (workRows.isEmpty()) {
            return 0;
        }

        Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> existingRecruitmentMap =
            getExistingRecruitmentMap(workRows, importedRecruitmentMap);
        Map<String, List<WorkImportRow>> groupedRows = groupWorkRows(workRows);
        int successCount = 0;
        int processedGroups = 0;

        for (List<WorkImportRow> recruitWorkRows : groupedRows.values()) {
            processedGroups++;
            WorkImportRow firstRow = recruitWorkRows.get(0);
            String recruitId = trim(firstRow.data().getRecruitId());

            try {
                RecruitmentMatchKey matchKey = buildMatchKey(firstRow.data());
                CcdiStaffRecruitment recruitment = resolveMatchedRecruitment(
                    matchKey,
                    importedRecruitmentMap,
                    existingRecruitmentMap,
                    extractWorkRowNums(recruitWorkRows)
                );
                validateWorkGroup(recruitWorkRows, recruitment);

                if (recruitment != null && hasExistingWorkHistory(recruitment.getId())) {
                    throw buildValidationException(
                        WORK_SHEET_NAME,
                        extractWorkRowNums(recruitWorkRows),
                        String.format("招聘记录编号[%s]已存在历史工作经历，不允许重复导入", recruitId)
                    );
                }

                List<CcdiStaffRecruitmentWork> entities = buildWorkEntities(recruitWorkRows, recruitment, userName);
                entities.forEach(entity -> recruitmentWorkMapper.insert(entity));
                successCount += recruitWorkRows.size();

                ImportLogUtils.logProgress(log, taskId, processedGroups, groupedRows.size(), successCount, failures.size());
            } catch (Exception exception) {
                FailureMeta failureMeta = resolveFailureMeta(exception, extractWorkRowNums(recruitWorkRows), WORK_SHEET_NAME);
                failures.add(buildFailure(firstRow.data(), failureMeta.sheetName(), failureMeta.sheetRowNum(), exception.getMessage()));
                ImportLogUtils.logValidationError(
                    log,
                    taskId,
                    processedGroups,
                    exception.getMessage(),
                    String.format(
                        "招聘记录编号=%s, 候选人=%s, 工作单位=%s",
                        firstRow.data().getRecruitId(),
                        firstRow.data().getCandName(),
                        firstRow.data().getCompanyName()
                    )
                );
            }
        }

        return successCount;
    }

    private Map<String, List<WorkImportRow>> groupWorkRows(List<WorkImportRow> workRows) {
        Map<String, List<WorkImportRow>> groupedRows = new LinkedHashMap<>();
        for (WorkImportRow workRow : workRows) {
            groupedRows.computeIfAbsent(buildWorkGroupKey(workRow), key -> new ArrayList<>()).add(workRow);
        }
        return groupedRows;
    }

    private String buildWorkGroupKey(WorkImportRow workRow) {
        RecruitmentMatchKey key = buildMatchKey(workRow.data());
        if (key.isComplete()) {
            return key.value();
        }
        return "__ROW__" + workRow.sheetRowNum();
    }

    private Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> getExistingRecruitmentMap(
        List<WorkImportRow> workRows,
        Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> importedRecruitmentMap
    ) {
        LinkedHashSet<String> recruitIds = workRows.stream()
            .filter(row -> !importedRecruitmentMap.containsKey(buildMatchKey(row.data())))
            .map(row -> trim(row.data().getRecruitId()))
            .filter(StringUtils::isNotEmpty)
            .collect(Collectors.toCollection(LinkedHashSet::new));
        if (recruitIds.isEmpty()) {
            return Collections.emptyMap();
        }
        List<CcdiStaffRecruitment> recruitments = selectRecruitmentsByRecruitIds(recruitIds);
        Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> result = new LinkedHashMap<>();
        recruitments.forEach(item -> addRecruitment(result, item));
        return result;
    }

    private CcdiStaffRecruitment resolveMatchedRecruitment(
        RecruitmentMatchKey matchKey,
        Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> importedRecruitmentMap,
        Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> existingRecruitmentMap,
        List<Integer> rowNums
    ) {
        List<CcdiStaffRecruitment> matchedRecruitments = new ArrayList<>();
        matchedRecruitments.addAll(importedRecruitmentMap.getOrDefault(matchKey, Collections.emptyList()));
        matchedRecruitments.addAll(existingRecruitmentMap.getOrDefault(matchKey, Collections.emptyList()));
        if (matchedRecruitments.size() > 1) {
            throw buildValidationException(
                WORK_SHEET_NAME,
                rowNums,
                String.format("招聘记录编号[%s]匹配到多条招聘主信息，无法确定历史工作经历归属", matchKey.recruitId())
            );
        }
        return matchedRecruitments.isEmpty() ? null : matchedRecruitments.get(0);
    }

    private List<CcdiStaffRecruitmentWork> buildWorkEntities(List<WorkImportRow> workRows,
                                                             CcdiStaffRecruitment recruitment,
                                                             String userName) {
        List<CcdiStaffRecruitmentWork> entities = new ArrayList<>();
        for (WorkImportRow workRow : workRows) {
            CcdiStaffRecruitmentWork entity = new CcdiStaffRecruitmentWork();
            BeanUtils.copyProperties(workRow.data(), entity);
            entity.setRecruitmentId(recruitment.getId());
            entity.setRecruitId(recruitment.getRecruitId());
            entity.setCreatedBy(userName);
            entity.setUpdatedBy(userName);
            entities.add(entity);
        }
        return entities;
    }

    private boolean hasExistingWorkHistory(Long recruitmentId) {
        LambdaQueryWrapper<CcdiStaffRecruitmentWork> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CcdiStaffRecruitmentWork::getRecruitmentId, recruitmentId);
        return recruitmentWorkMapper.selectCount(wrapper) > 0;
    }

    private void validateRecruitmentData(CcdiStaffRecruitmentAddDTO addDTO, int sheetRowNum) {
        if (StringUtils.isEmpty(addDTO.getRecruitId())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "招聘记录编号不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getRecruitName())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "招聘项目名称不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getPosName())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "职位名称不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getPosCategory())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "职位类别不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getPosDesc())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "职位描述不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getCandName())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "候选人姓名不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getCandEdu())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "学历不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getCandId())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "证件号码不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getCandSchool())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "毕业院校不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getCandMajor())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "专业不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getCandGrad())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "毕业年月不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getAdmitStatus())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "录用情况不能为空");
        }
        if (StringUtils.isEmpty(addDTO.getRecruitType())) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "招聘类型不能为空");
        }

        String idCardError = IdCardUtil.getErrorMessage(addDTO.getCandId());
        if (idCardError != null) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "证件号码" + idCardError);
        }

        if (!addDTO.getCandGrad().matches("^((19|20)\\d{2})(0[1-9]|1[0-2])$")) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "毕业年月格式不正确,应为YYYYMM");
        }

        if (AdmitStatus.getDescByCode(addDTO.getAdmitStatus()) == null) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "录用情况只能填写'录用'、'未录用'或'放弃'");
        }

        if (RecruitType.getDescByCode(addDTO.getRecruitType()) == null) {
            throw buildValidationException(MAIN_SHEET_NAME, List.of(sheetRowNum), "招聘类型只能填写'SOCIAL/社招'或'CAMPUS/校招'");
        }
    }

    private String normalizeRecruitType(String recruitType) {
        String value = trim(recruitType);
        if (StringUtils.isEmpty(value)) {
            return value;
        }
        for (RecruitType type : RecruitType.values()) {
            if (type.getCode().equals(value) || type.getDesc().equals(value)) {
                return type.getCode();
            }
        }
        return value;
    }

    private void validateWorkGroup(List<WorkImportRow> workRows, CcdiStaffRecruitment recruitment) {
        Set<Integer> processedSortOrders = new HashSet<>();
        for (WorkImportRow workRow : workRows) {
            validateRecruitmentWorkData(workRow.data(), recruitment, processedSortOrders, workRow.sheetRowNum());
        }
    }

    private void validateRecruitmentWorkData(CcdiStaffRecruitmentWorkExcel excel,
                                             CcdiStaffRecruitment recruitment,
                                             Set<Integer> processedSortOrders,
                                             int sheetRowNum) {
        if (StringUtils.isEmpty(trim(excel.getRecruitId()))) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "招聘记录编号不能为空");
        }
        if (StringUtils.isEmpty(trim(excel.getCandName()))) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "候选人姓名不能为空");
        }
        if (StringUtils.isEmpty(trim(excel.getRecruitName()))) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "招聘项目名称不能为空");
        }
        if (StringUtils.isEmpty(trim(excel.getPosName()))) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "职位名称不能为空");
        }
        if (excel.getSortOrder() == null || excel.getSortOrder() <= 0) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "排序号不能为空且必须大于0");
        }
        if (!processedSortOrders.add(excel.getSortOrder())) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "同一招聘记录编号下排序号重复");
        }
        if (StringUtils.isEmpty(trim(excel.getCompanyName()))) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "工作单位不能为空");
        }
        if (StringUtils.isEmpty(trim(excel.getPositionName()))) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "岗位名称不能为空");
        }
        if (StringUtils.isEmpty(trim(excel.getJobStartMonth()))) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "入职时间不能为空");
        }
        validateMonth(excel.getJobStartMonth(), "入职时间", sheetRowNum);
        if (StringUtils.isNotEmpty(trim(excel.getJobEndMonth()))) {
            validateMonth(excel.getJobEndMonth(), "离职时间", sheetRowNum);
        }
        if (recruitment == null) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "招聘记录编号不存在，请先维护招聘主信息");
        }
        if (!"SOCIAL".equals(recruitment.getRecruitType())) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "该招聘记录不是社招，不允许导入历史工作经历");
        }
        if (!sameText(excel.getCandName(), recruitment.getCandName())) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "招聘记录编号与候选人姓名不匹配");
        }
        if (!sameText(excel.getRecruitName(), recruitment.getRecruitName())) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "招聘记录编号与招聘项目名称不匹配");
        }
        if (!sameText(excel.getPosName(), recruitment.getPosName())) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), "招聘记录编号与职位名称不匹配");
        }
    }

    private void validateMonth(String value, String fieldName, int sheetRowNum) {
        String month = trim(value);
        if (!month.matches("^((19|20)\\d{2})-(0[1-9]|1[0-2])$")) {
            throw buildValidationException(WORK_SHEET_NAME, List.of(sheetRowNum), fieldName + "格式不正确，应为YYYY-MM");
        }
    }

    private boolean sameText(String first, String second) {
        return Objects.equals(trim(first), trim(second));
    }

    private String trim(String value) {
        return value == null ? null : value.trim();
    }

    private void saveFailures(String taskId, List<RecruitmentImportFailureVO> failures) {
        try {
            String failuresKey = "import:recruitment:" + taskId + ":failures";
            redisTemplate.opsForValue().set(failuresKey, failures, 7, TimeUnit.DAYS);
            ImportLogUtils.logRedisOperation(log, taskId, "保存失败记录", failures.size());
        } catch (Exception exception) {
            ImportLogUtils.logRedisError(log, taskId, "保存失败记录", exception);
        }
    }

    private RecruitmentImportFailureVO buildFailure(CcdiStaffRecruitmentExcel excel,
                                                    String sheetName,
                                                    String sheetRowNum,
                                                    String errorMessage) {
        RecruitmentImportFailureVO failure = new RecruitmentImportFailureVO();
        BeanUtils.copyProperties(excel, failure);
        failure.setSheetName(sheetName);
        failure.setSheetRowNum(sheetRowNum);
        failure.setErrorMessage(errorMessage);
        return failure;
    }

    private RecruitmentImportFailureVO buildFailure(CcdiStaffRecruitmentWorkExcel excel,
                                                    String sheetName,
                                                    String sheetRowNum,
                                                    String errorMessage) {
        RecruitmentImportFailureVO failure = new RecruitmentImportFailureVO();
        BeanUtils.copyProperties(excel, failure);
        failure.setSheetName(sheetName);
        failure.setSheetRowNum(sheetRowNum);
        failure.setErrorMessage(errorMessage);
        return failure;
    }

    private String resolveFinalStatus(ImportResult result) {
        if (result.getFailureCount() == 0) {
            return "SUCCESS";
        }
        if (result.getSuccessCount() == 0) {
            return "FAILED";
        }
        return "PARTIAL_SUCCESS";
    }

    private void updateImportStatus(String taskId, String status, ImportResult result) {
        String key = "import:recruitment:" + taskId;
        Map<String, Object> statusData = new HashMap<>();
        statusData.put("status", status);
        statusData.put("totalCount", result.getTotalCount());
        statusData.put("successCount", result.getSuccessCount());
        statusData.put("failureCount", result.getFailureCount());
        statusData.put("progress", 100);
        statusData.put("endTime", System.currentTimeMillis());

        if ("SUCCESS".equals(status)) {
            statusData.put("message", "全部成功!共导入" + result.getTotalCount() + "条数据");
        } else {
            statusData.put("message", "成功" + result.getSuccessCount() + "条,失败" + result.getFailureCount() + "条");
        }

        redisTemplate.opsForHash().putAll(key, statusData);
    }

    private List<CcdiStaffRecruitment> selectRecruitmentsByRecruitIds(Set<String> recruitIds) {
        if (recruitIds == null || recruitIds.isEmpty()) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<CcdiStaffRecruitment> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(CcdiStaffRecruitment::getRecruitId, recruitIds);
        return recruitmentMapper.selectList(wrapper);
    }

    private void addRecruitment(Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> map,
                                CcdiStaffRecruitment recruitment) {
        map.computeIfAbsent(buildMatchKey(recruitment), key -> new ArrayList<>()).add(recruitment);
    }

    private RecruitmentMatchKey buildMatchKey(CcdiStaffRecruitment recruitment) {
        return new RecruitmentMatchKey(
            trim(recruitment.getRecruitId()),
            trim(recruitment.getCandName()),
            trim(recruitment.getRecruitName()),
            trim(recruitment.getPosName())
        );
    }

    private RecruitmentMatchKey buildMatchKey(CcdiStaffRecruitmentWorkExcel excel) {
        return new RecruitmentMatchKey(
            trim(excel.getRecruitId()),
            trim(excel.getCandName()),
            trim(excel.getRecruitName()),
            trim(excel.getPosName())
        );
    }

    private List<MainImportRow> buildMainImportRows(List<CcdiStaffRecruitmentExcel> recruitmentList) {
        List<MainImportRow> rows = new ArrayList<>();
        for (int i = 0; i < recruitmentList.size(); i++) {
            rows.add(new MainImportRow(recruitmentList.get(i), i + EXCEL_DATA_START_ROW));
        }
        return rows;
    }

    private List<WorkImportRow> buildWorkImportRows(List<CcdiStaffRecruitmentWorkExcel> workList) {
        List<WorkImportRow> rows = new ArrayList<>();
        for (int i = 0; i < workList.size(); i++) {
            rows.add(new WorkImportRow(workList.get(i), i + EXCEL_DATA_START_ROW));
        }
        return rows;
    }

    private List<Integer> extractWorkRowNums(List<WorkImportRow> rows) {
        return rows.stream().map(WorkImportRow::sheetRowNum).toList();
    }

    private FailureMeta resolveFailureMeta(Exception exception, List<Integer> rowNums, String defaultSheetName) {
        if (exception instanceof ImportValidationException validationException) {
            return new FailureMeta(validationException.getSheetName(), validationException.getSheetRowNum());
        }
        return new FailureMeta(defaultSheetName, formatSheetRowNum(rowNums));
    }

    private ImportValidationException buildValidationException(String sheetName, List<Integer> rowNums, String message) {
        return new ImportValidationException(sheetName, formatSheetRowNum(rowNums), message);
    }

    private String formatSheetRowNum(List<Integer> rowNums) {
        if (rowNums == null || rowNums.isEmpty()) {
            return "";
        }
        return rowNums.stream()
            .filter(Objects::nonNull)
            .distinct()
            .sorted()
            .map(String::valueOf)
            .collect(Collectors.joining("、"));
    }

    private record MainImportRow(CcdiStaffRecruitmentExcel data, int sheetRowNum) {}

    private record WorkImportRow(CcdiStaffRecruitmentWorkExcel data, int sheetRowNum) {}

    private record MainImportResult(Map<RecruitmentMatchKey, List<CcdiStaffRecruitment>> importedRecruitmentMap,
                                    int successCount) {}

    private record FailureMeta(String sheetName, String sheetRowNum) {}

    private record RecruitmentMatchKey(String recruitId, String candName, String recruitName, String posName) {

        private boolean isComplete() {
            return StringUtils.isNotEmpty(recruitId)
                && StringUtils.isNotEmpty(candName)
                && StringUtils.isNotEmpty(recruitName)
                && StringUtils.isNotEmpty(posName);
        }

        private String value() {
            return String.join("|", recruitId, candName, recruitName, posName);
        }
    }

    private static class ImportValidationException extends RuntimeException {

        private final String sheetName;
        private final String sheetRowNum;

        private ImportValidationException(String sheetName, String sheetRowNum, String message) {
            super(message);
            this.sheetName = sheetName;
            this.sheetRowNum = sheetRowNum;
        }

        public String getSheetName() {
            return sheetName;
        }

        public String getSheetRowNum() {
            return sheetRowNum;
        }
    }
}
