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 org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiProjectHistoryImportServiceImplTest {

    @InjectMocks
    private CcdiProjectHistoryImportServiceImpl service;

    @Mock
    private CcdiBankStatementMapper bankStatementMapper;

    @Mock
    private CcdiFileUploadRecordMapper recordMapper;

    @Mock
    private CcdiProjectMapper projectMapper;

    @Mock
    private Executor fileUploadExecutor;

    @Mock
    private ICcdiBankTagService bankTagService;

    @Test
    void shouldFilterStatementsByTrxDateAndDeduplicateAcrossSourceProjects() {
        CcdiProjectImportHistoryDTO dto = buildImportDto();
        when(recordMapper.selectSuccessfulRecordsByProjectIds(dto.getSourceProjectIds()))
            .thenReturn(List.of(buildSourceRecord(11L, 101, "批次A"), buildSourceRecord(12L, 202, "批次B")));

        when(bankStatementMapper.selectStatementsForHistoryImport(11L, 101, "2026-01-01", "2026-01-31"))
            .thenReturn(List.of(buildStatement("2026-01-10", "6222", "100.00", "备注A")));
        when(bankStatementMapper.selectStatementsForHistoryImport(12L, 202, "2026-01-01", "2026-01-31"))
            .thenReturn(List.of(
                buildStatement("2026-01-10", "6222", "100.00", "备注A"),
                buildStatement("2026-01-11", "6333", "200.00", "备注B")
            ));
        doAnswer(invocation -> {
            Runnable runnable = invocation.getArgument(0);
            runnable.run();
            return null;
        }).when(fileUploadExecutor).execute(any(Runnable.class));

        AtomicReference<List<CcdiBankStatement>> insertedStatements = new AtomicReference<>();
        doAnswer(invocation -> {
            insertedStatements.set(List.copyOf(invocation.getArgument(0)));
            return insertedStatements.get().size();
        }).when(bankStatementMapper).insertBatch(anyList());

        when(projectMapper.selectById(11L)).thenReturn(buildProject(11L, "历史项目A"));
        when(projectMapper.selectById(12L)).thenReturn(buildProject(12L, "历史项目B"));

        service.submitImport(90L, 3001, dto, "tester");

        assertEquals(2, insertedStatements.get().size());
        assertTrue(insertedStatements.get().stream().allMatch(item -> Long.valueOf(90L).equals(item.getProjectId())));
        assertTrue(insertedStatements.get().stream().allMatch(item -> Integer.valueOf(3001).equals(item.getGroupId())));
        verify(bankStatementMapper).selectStatementsForHistoryImport(11L, 101, "2026-01-01", "2026-01-31");
        verify(bankStatementMapper).selectStatementsForHistoryImport(12L, 202, "2026-01-01", "2026-01-31");
    }

    @Test
    void shouldGenerateNewBatchIdsAndHistoryImportFileRecordsOnlyForSuccessfulSourceBatches() {
        CcdiProjectImportHistoryDTO dto = buildImportDto();
        CcdiFileUploadRecord sourceRecord = buildSourceRecord(11L, 101, "批次A");
        when(recordMapper.selectSuccessfulRecordsByProjectIds(dto.getSourceProjectIds()))
            .thenReturn(List.of(sourceRecord, buildSourceRecord(12L, 202, "批次B")));

        when(bankStatementMapper.selectStatementsForHistoryImport(11L, 101, "2026-01-01", "2026-01-31"))
            .thenReturn(List.of(buildStatement("2026-01-12", "6444", "300.00", "备注C")));
        when(bankStatementMapper.selectStatementsForHistoryImport(12L, 202, "2026-01-01", "2026-01-31"))
            .thenReturn(List.of());
        doAnswer(invocation -> {
            Runnable runnable = invocation.getArgument(0);
            runnable.run();
            return null;
        }).when(fileUploadExecutor).execute(any(Runnable.class));

        AtomicReference<List<CcdiBankStatement>> insertedStatements = new AtomicReference<>();
        doAnswer(invocation -> {
            insertedStatements.set(List.copyOf(invocation.getArgument(0)));
            return insertedStatements.get().size();
        }).when(bankStatementMapper).insertBatch(anyList());

        AtomicReference<List<CcdiFileUploadRecord>> insertedRecords = new AtomicReference<>();
        doAnswer(invocation -> {
            insertedRecords.set(List.copyOf(invocation.getArgument(0)));
            return insertedRecords.get().size();
        }).when(recordMapper).insertBatch(anyList());

        when(projectMapper.selectById(11L)).thenReturn(buildProject(11L, "历史项目A"));

        service.submitImport(90L, 3001, dto, "tester");

        assertEquals(1, insertedStatements.get().size());
        assertNotEquals(101, insertedStatements.get().get(0).getBatchId());
        assertEquals(1, insertedRecords.get().size());
        assertEquals("HISTORY_IMPORT", insertedRecords.get().get(0).getSourceType());
        assertEquals(11L, insertedRecords.get().get(0).getSourceProjectId());
        assertEquals("历史项目A", insertedRecords.get().get(0).getSourceProjectName());
        assertEquals("parsed_success", insertedRecords.get().get(0).getFileStatus());
        assertNotEquals(sourceRecord.getLogId(), insertedRecords.get().get(0).getLogId());
    }

    @Test
    void shouldRefreshTargetCountAndSubmitAutoRebuildAfterImport() {
        CcdiProjectImportHistoryDTO dto = buildImportDto();
        when(recordMapper.selectSuccessfulRecordsByProjectIds(dto.getSourceProjectIds()))
            .thenReturn(List.of(buildSourceRecord(11L, 101, "批次A")));
        when(bankStatementMapper.selectStatementsForHistoryImport(11L, 101, "2026-01-01", "2026-01-31"))
            .thenReturn(List.of(buildStatement("2026-01-12", "6444", "300.00", "备注C")));
        doAnswer(invocation -> {
            Runnable runnable = invocation.getArgument(0);
            runnable.run();
            return null;
        }).when(fileUploadExecutor).execute(any(Runnable.class));
        when(projectMapper.selectById(11L)).thenReturn(buildProject(11L, "历史项目A"));
        when(projectMapper.selectById(90L)).thenReturn(buildProject(90L, "新项目"));
        when(bankStatementMapper.countMatchedStaffCountByProjectId(90L)).thenReturn(3);

        service.submitImport(90L, 3001, dto, "tester");

        verify(projectMapper).updateById(org.mockito.ArgumentMatchers.<CcdiProject>argThat(project ->
            Long.valueOf(90L).equals(project.getProjectId()) && Integer.valueOf(3).equals(project.getTargetCount())
        ));
        verify(bankTagService).submitAutoRebuild(90L, TriggerType.AUTO_BATCH_UPLOAD);
    }

    private CcdiProjectImportHistoryDTO buildImportDto() {
        CcdiProjectImportHistoryDTO dto = new CcdiProjectImportHistoryDTO();
        dto.setProjectName("新项目");
        dto.setDescription("从历史复制");
        dto.setSourceProjectIds(List.of(11L, 12L));
        dto.setStartDate("2026-01-01");
        dto.setEndDate("2026-01-31");
        return dto;
    }

    private CcdiFileUploadRecord buildSourceRecord(Long sourceProjectId, Integer logId, String fileName) {
        CcdiFileUploadRecord record = new CcdiFileUploadRecord();
        record.setProjectId(sourceProjectId);
        record.setLogId(logId);
        record.setFileName(fileName);
        record.setFileStatus("parsed_success");
        return record;
    }

    private CcdiProject buildProject(Long projectId, String projectName) {
        CcdiProject project = new CcdiProject();
        project.setProjectId(projectId);
        project.setProjectName(projectName);
        return project;
    }

    private CcdiBankStatement buildStatement(String trxDate, String accountNo, String amountCr, String memo) {
        CcdiBankStatement statement = new CcdiBankStatement();
        statement.setTrxDate(trxDate);
        statement.setLeAccountNo(accountNo);
        statement.setAmountCr(new BigDecimal(amountCr));
        statement.setUserMemo(memo);
        statement.setCustomerAccountName("对手方");
        statement.setCustomerAccountNo("7000");
        statement.setBatchSequence(1);
        return statement;
    }
}
