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.StringUtils;
import com.ruoyi.info.collection.domain.CcdiEnterpriseBaseInfo;
import com.ruoyi.info.collection.domain.excel.CcdiEnterpriseBaseInfoExcel;
import com.ruoyi.info.collection.domain.vo.EnterpriseBaseInfoImportFailureVO;
import com.ruoyi.info.collection.domain.vo.ImportResult;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.enums.DataSource;
import com.ruoyi.info.collection.enums.EnterpriseRiskLevel;
import com.ruoyi.info.collection.enums.EnterpriseSource;
import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper;
import com.ruoyi.info.collection.service.ICcdiEnterpriseBaseInfoImportService;
import jakarta.annotation.Resource;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
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;

/**
 * 实体库管理导入 Service 实现
 *
 * @author ruoyi
 * @date 2026-04-17
 */
@Service
@EnableAsync
public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseBaseInfoImportService {

    @Resource
    private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Async
    public void importEnterpriseBaseInfoAsync(List<CcdiEnterpriseBaseInfoExcel> excelList, String taskId, String userName) {
        List<CcdiEnterpriseBaseInfo> successRecords = new ArrayList<>();
        List<EnterpriseBaseInfoImportFailureVO> failures = new ArrayList<>();
        Set<String> existingCreditCodes = getExistingCreditCodes(excelList);
        Set<String> processedCreditCodes = new HashSet<>();

        for (CcdiEnterpriseBaseInfoExcel excel : excelList) {
            try {
                CcdiEnterpriseBaseInfo entity = validateAndBuildEntity(excel, existingCreditCodes, processedCreditCodes, userName);
                successRecords.add(entity);
                processedCreditCodes.add(entity.getSocialCreditCode());
            } catch (Exception e) {
                EnterpriseBaseInfoImportFailureVO failureVO = new EnterpriseBaseInfoImportFailureVO();
                BeanUtils.copyProperties(excel, failureVO);
                failureVO.setErrorMessage(e.getMessage());
                failures.add(failureVO);
            }
        }

        if (!successRecords.isEmpty()) {
            saveBatch(successRecords, 500);
        }

        if (!failures.isEmpty()) {
            redisTemplate.opsForValue().set(buildFailuresKey(taskId), failures, 7, TimeUnit.DAYS);
        }

        ImportResult result = new ImportResult();
        result.setTotalCount(excelList.size());
        result.setSuccessCount(successRecords.size());
        result.setFailureCount(failures.size());
        updateImportStatus(taskId, failures.isEmpty() ? "SUCCESS" : "PARTIAL_SUCCESS", result);
    }

    @Override
    public ImportStatusVO getImportStatus(String taskId) {
        String key = buildStatusKey(taskId);
        Boolean exists = redisTemplate.hasKey(key);
        if (Boolean.FALSE.equals(exists)) {
            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<EnterpriseBaseInfoImportFailureVO> getImportFailures(String taskId) {
        Object failuresObj = redisTemplate.opsForValue().get(buildFailuresKey(taskId));
        if (failuresObj == null) {
            return Collections.emptyList();
        }
        return JSON.parseArray(JSON.toJSONString(failuresObj), EnterpriseBaseInfoImportFailureVO.class);
    }

    @Override
    public CcdiEnterpriseBaseInfo validateAndBuildEntity(CcdiEnterpriseBaseInfoExcel excel,
                                                         Set<String> existingCreditCodes,
                                                         Set<String> processedCreditCodes,
                                                         String userName) {
        if (excel == null) {
            throw new RuntimeException("导入数据不能为空");
        }
        if (StringUtils.isEmpty(excel.getEnterpriseName())) {
            throw new RuntimeException("企业名称不能为空");
        }
        if (StringUtils.isEmpty(excel.getSocialCreditCode())) {
            throw new RuntimeException("统一社会信用代码不能为空");
        }
        if (!excel.getSocialCreditCode().matches("^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$")) {
            throw new RuntimeException("统一社会信用代码格式不正确");
        }
        String entSource = EnterpriseSource.resolveCode(StringUtils.trim(excel.getEntSource()));
        if (entSource == null) {
            throw new RuntimeException("企业来源不在允许范围内");
        }
        String riskLevel = EnterpriseRiskLevel.resolveCode(StringUtils.trim(excel.getRiskLevel()));
        if (riskLevel == null) {
            if (EnterpriseSource.INTERMEDIARY.getCode().equals(entSource) && StringUtils.isEmpty(excel.getRiskLevel())) {
                riskLevel = "1";
            } else {
                throw new RuntimeException("风险等级不在允许范围内");
            }
        }

        if (existingCreditCodes.contains(excel.getSocialCreditCode())) {
            throw new RuntimeException(String.format("统一社会信用代码[%s]已存在,请勿重复导入", excel.getSocialCreditCode()));
        }
        if (processedCreditCodes.contains(excel.getSocialCreditCode())) {
            throw new RuntimeException(String.format("统一社会信用代码[%s]在导入文件中重复,已跳过此条记录", excel.getSocialCreditCode()));
        }

        CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo();
        BeanUtils.copyProperties(excel, entity);
        entity.setRiskLevel(riskLevel);
        entity.setEntSource(entSource);
        entity.setDataSource(DataSource.IMPORT.getCode());
        entity.setStatus(trimToNull(excel.getStatus()));
        entity.setCreatedBy(userName);
        entity.setUpdatedBy(userName);
        return entity;
    }

    private Set<String> getExistingCreditCodes(List<CcdiEnterpriseBaseInfoExcel> excelList) {
        List<String> creditCodes = excelList.stream()
                .map(CcdiEnterpriseBaseInfoExcel::getSocialCreditCode)
                .filter(StringUtils::isNotEmpty)
                .collect(Collectors.toList());
        if (creditCodes.isEmpty()) {
            return Collections.emptySet();
        }
        LambdaQueryWrapper<CcdiEnterpriseBaseInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(CcdiEnterpriseBaseInfo::getSocialCreditCode, creditCodes);
        return enterpriseBaseInfoMapper.selectList(wrapper).stream()
                .map(CcdiEnterpriseBaseInfo::getSocialCreditCode)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
    }

    private int saveBatch(List<CcdiEnterpriseBaseInfo> list, int batchSize) {
        int total = 0;
        for (int i = 0; i < list.size(); i += batchSize) {
            int end = Math.min(i + batchSize, list.size());
            total += enterpriseBaseInfoMapper.insertBatch(list.subList(i, end));
        }
        return total;
    }

    private void updateImportStatus(String taskId, String status, ImportResult result) {
        Map<String, Object> statusData = new HashMap<>();
        statusData.put("status", status);
        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(buildStatusKey(taskId), statusData);
    }

    private String buildStatusKey(String taskId) {
        return "import:enterpriseBaseInfo:" + taskId;
    }

    private String buildFailuresKey(String taskId) {
        return "import:enterpriseBaseInfo:" + taskId + ":failures";
    }

    private String trimToNull(String value) {
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        return value.trim();
    }
}
