package com.ruoyi.ccdi.project.service.impl;

import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement;
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
import com.ruoyi.ccdi.project.service.ICcdiProjectHistoryImportService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 历史项目导入服务实现
 *
 * @author ruoyi
 */
@Slf4j
@Service
public class CcdiProjectHistoryImportServiceImpl implements ICcdiProjectHistoryImportService {

    private static final AtomicInteger HISTORY_IMPORT_BATCH_ID = new AtomicInteger(1_000_000);

    @Resource
    private CcdiBankStatementMapper bankStatementMapper;

    @Resource
    private CcdiFileUploadRecordMapper recordMapper;

    @Resource
    private CcdiProjectMapper projectMapper;

    @Resource
    @Qualifier("fileUploadExecutor")
    private Executor fileUploadExecutor;

    @Resource
    private ICcdiBankTagService bankTagService;

    @Override
    public void submitImport(Long targetProjectId, Integer targetLsfxProjectId,
                             CcdiProjectImportHistoryDTO dto, String operator) {
        fileUploadExecutor.execute(() -> executeImport(targetProjectId, targetLsfxProjectId, dto, operator));
    }

    private void executeImport(Long targetProjectId, Integer targetLsfxProjectId,
                               CcdiProjectImportHistoryDTO dto, String operator) {
        List<CcdiFileUploadRecord> sourceRecords = recordMapper.selectSuccessfulRecordsByProjectIds(dto.getSourceProjectIds());
        if (sourceRecords == null || sourceRecords.isEmpty()) {
            log.info("【项目历史导入】无可复制的来源批次: projectId={}, sourceProjectIds={}",
                targetProjectId, dto.getSourceProjectIds());
            return;
        }

        List<CcdiBankStatement> statementsToInsert = new ArrayList<>();
        List<CcdiFileUploadRecord> recordsToInsert = new ArrayList<>();
        Set<String> dedupKeys = new HashSet<>();

        for (CcdiFileUploadRecord sourceRecord : sourceRecords) {
            List<CcdiBankStatement> sourceStatements = bankStatementMapper.selectStatementsForHistoryImport(
                sourceRecord.getProjectId(), sourceRecord.getLogId(), dto.getStartDate(), dto.getEndDate()
            );
            if (sourceStatements == null || sourceStatements.isEmpty()) {
                continue;
            }

            Integer newBatchId = HISTORY_IMPORT_BATCH_ID.incrementAndGet();
            int sizeBefore = statementsToInsert.size();

            for (CcdiBankStatement sourceStatement : sourceStatements) {
                CcdiBankStatement targetStatement = copyStatement(sourceStatement, targetProjectId, targetLsfxProjectId, newBatchId);
                if (dedupKeys.add(buildDedupKey(targetStatement))) {
                    statementsToInsert.add(targetStatement);
                }
            }

            if (statementsToInsert.size() > sizeBefore) {
                recordsToInsert.add(buildHistoryImportRecord(
                    sourceRecord, targetProjectId, targetLsfxProjectId, newBatchId, resolveSourceProjectName(sourceRecord.getProjectId()), operator
                ));
            }
        }

        if (!statementsToInsert.isEmpty()) {
            bankStatementMapper.insertBatch(statementsToInsert);
        }
        if (!recordsToInsert.isEmpty()) {
            recordMapper.insertBatch(recordsToInsert);
        }
        if (!statementsToInsert.isEmpty()) {
            refreshProjectTargetCount(targetProjectId);
            bankTagService.submitAutoRebuild(targetProjectId, TriggerType.AUTO_BATCH_UPLOAD);
        }
    }

    private CcdiBankStatement copyStatement(CcdiBankStatement sourceStatement, Long targetProjectId,
                                            Integer targetLsfxProjectId, Integer newBatchId) {
        CcdiBankStatement targetStatement = new CcdiBankStatement();
        BeanUtils.copyProperties(sourceStatement, targetStatement);
        targetStatement.setBankStatementId(null);
        targetStatement.setProjectId(targetProjectId);
        targetStatement.setGroupId(targetLsfxProjectId);
        targetStatement.setBatchId(newBatchId);
        normalizeDedupFields(targetStatement);
        return targetStatement;
    }

    private CcdiFileUploadRecord buildHistoryImportRecord(CcdiFileUploadRecord sourceRecord, Long targetProjectId,
                                                          Integer targetLsfxProjectId, Integer newBatchId,
                                                          String sourceProjectName, String operator) {
        CcdiFileUploadRecord record = new CcdiFileUploadRecord();
        record.setProjectId(targetProjectId);
        record.setLsfxProjectId(targetLsfxProjectId);
        record.setLogId(newBatchId);
        record.setFileName(sourceRecord.getFileName());
        record.setFileSize(sourceRecord.getFileSize());
        record.setFileStatus("parsed_success");
        record.setEnterpriseNames(sourceRecord.getEnterpriseNames());
        record.setAccountNos(sourceRecord.getAccountNos());
        record.setUploadTime(new Date());
        record.setUploadUser(operator);
        record.setSourceType("HISTORY_IMPORT");
        record.setSourceProjectId(sourceRecord.getProjectId());
        record.setSourceProjectName(sourceProjectName);
        return record;
    }

    private String resolveSourceProjectName(Long sourceProjectId) {
        CcdiProject sourceProject = projectMapper.selectById(sourceProjectId);
        return sourceProject == null ? null : sourceProject.getProjectName();
    }

    private void refreshProjectTargetCount(Long targetProjectId) {
        CcdiProject targetProject = projectMapper.selectById(targetProjectId);
        if (targetProject == null) {
            log.warn("【项目历史导入】刷新目标人数时项目不存在: projectId={}", targetProjectId);
            return;
        }
        int targetCount = bankStatementMapper.countMatchedStaffCountByProjectId(targetProjectId);
        targetProject.setTargetCount(targetCount);
        projectMapper.updateById(targetProject);
    }

    private void normalizeDedupFields(CcdiBankStatement statement) {
        statement.setLeAccountNo(trimToNull(statement.getLeAccountNo()));
    }

    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

    private String buildDedupKey(CcdiBankStatement statement) {
        return String.join("|",
            valueOf(statement.getTrxDate()),
            valueOf(statement.getCurrency()),
            valueOf(statement.getLeAccountNo()),
            valueOf(statement.getLeAccountName()),
            valueOf(statement.getAccountId()),
            valueOf(statement.getCustomerAccountName()),
            valueOf(statement.getCustomerAccountNo()),
            valueOf(statement.getCustomerBank()),
            valueOf(statement.getCustomerReference()),
            valueOf(statement.getCustomerCertNo()),
            valueOf(statement.getCustomerSocialCreditCode()),
            valueOf(statement.getAmountDr()),
            valueOf(statement.getAmountCr()),
            valueOf(statement.getAmountBalance()),
            valueOf(statement.getCashType()),
            valueOf(statement.getUserMemo()),
            valueOf(statement.getBankComments()),
            valueOf(statement.getBankTrxNumber()),
            valueOf(statement.getBank()),
            valueOf(statement.getTrxFlag()),
            valueOf(statement.getTrxType()),
            valueOf(statement.getExceptionType()),
            valueOf(statement.getInternalFlag()),
            valueOf(statement.getCreateDate()),
            valueOf(statement.getPaymentMethod()),
            valueOf(statement.getCretNo())
        );
    }

    private String valueOf(Object value) {
        return Objects.toString(value, "");
    }
}
