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

import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
import com.ruoyi.ccdi.project.domain.vo.BankTagRuleExecutionConfig;
import com.ruoyi.ccdi.project.domain.vo.BankTagStatementHitVO;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagAnalysisMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagRuleMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import java.util.List;
import java.util.concurrent.Executor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiBankTagServiceRiskCountRefreshTest {

    @InjectMocks
    private CcdiBankTagServiceImpl service;

    @Mock
    private CcdiBankTagRuleMapper ruleMapper;

    @Mock
    private CcdiBankTagResultMapper resultMapper;

    @Mock
    private CcdiBankTagTaskMapper taskMapper;

    @Mock
    private CcdiBankTagAnalysisMapper analysisMapper;

    @Mock
    private BankTagRuleConfigResolver configResolver;

    @Mock
    private ICcdiProjectService projectService;

    @Mock
    private ICcdiProjectOverviewService projectOverviewService;

    @Mock
    private ProjectBankTagRebuildCoordinator coordinator;

    @Test
    void shouldRefreshOverviewEmployeeResultsAfterTagRebuildSuccess() {
        ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);

        CcdiBankTagRule rule = buildRule();
        BankTagRuleExecutionConfig config = buildConfig(rule);
        BankTagStatementHitVO hit = buildHit();

        doAnswer(invocation -> {
            CcdiBankTagTask task = invocation.getArgument(0);
            task.setId(101L);
            return 1;
        }).when(taskMapper).insertTask(any(CcdiBankTagTask.class));
        when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule));
        when(configResolver.resolve(40L, rule)).thenReturn(config);
        when(analysisMapper.selectHouseOrCarExpenseStatements(40L)).thenReturn(List.of(hit));

        service.rebuildProject(40L, null, "tester", TriggerType.MANUAL);

        InOrder inOrder = inOrder(projectService, resultMapper, projectOverviewService, taskMapper);
        inOrder.verify(projectService).updateProjectStatus(40L, "3", "tester");
        inOrder.verify(resultMapper).deleteByProjectAndModel(40L, null);
        inOrder.verify(resultMapper).insertBatch(anyList());
        inOrder.verify(projectOverviewService).refreshOverviewEmployeeResults(40L, "tester");
        inOrder.verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus())));
        inOrder.verify(projectService).updateProjectStatus(40L, "1", "tester");
    }

    @Test
    void shouldFailTaskWhenOverviewEmployeeResultRefreshFails() {
        ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);

        CcdiBankTagRule rule = buildRule();
        BankTagRuleExecutionConfig config = buildConfig(rule);

        doAnswer(invocation -> {
            CcdiBankTagTask task = invocation.getArgument(0);
            task.setId(102L);
            return 1;
        }).when(taskMapper).insertTask(any(CcdiBankTagTask.class));
        when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule));
        when(configResolver.resolve(40L, rule)).thenReturn(config);
        when(analysisMapper.selectHouseOrCarExpenseStatements(40L)).thenReturn(List.of(buildHit()));
        doThrow(new RuntimeException("refresh failed"))
            .when(projectOverviewService).refreshOverviewEmployeeResults(40L, "tester");

        assertThrows(RuntimeException.class,
            () -> service.rebuildProject(40L, null, "tester", TriggerType.MANUAL));

        verify(taskMapper).updateTask(argThat(task -> "FAILED".equals(task.getStatus())
            && "refresh failed".equals(task.getErrorMessage())));
        verify(projectService).updateProjectStatus(40L, "4", "tester");
    }

    private CcdiBankTagRule buildRule() {
        CcdiBankTagRule rule = new CcdiBankTagRule();
        rule.setModelCode("LARGE_TRANSACTION");
        rule.setModelName("大额交易");
        rule.setRuleCode("HOUSE_OR_CAR_EXPENSE");
        rule.setRuleName("房车消费支出交易");
        rule.setResultType("STATEMENT");
        return rule;
    }

    private BankTagRuleExecutionConfig buildConfig(CcdiBankTagRule rule) {
        BankTagRuleExecutionConfig config = new BankTagRuleExecutionConfig();
        config.setProjectId(40L);
        config.setRuleMeta(rule);
        return config;
    }

    private BankTagStatementHitVO buildHit() {
        BankTagStatementHitVO hit = new BankTagStatementHitVO();
        hit.setBankStatementId(10L);
        hit.setGroupId(40);
        hit.setLogId(40001);
        hit.setReasonDetail("命中房车消费支出");
        return hit;
    }
}
