package com.ruoyi.loanpricing.service.impl;

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

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.loanpricing.domain.entity.LoanPricingWorkflow;
import com.ruoyi.loanpricing.domain.entity.ModelCorpOutputFields;
import com.ruoyi.loanpricing.domain.entity.ModelRetailOutputFields;
import com.ruoyi.loanpricing.domain.vo.LoanPricingWorkflowListVO;
import com.ruoyi.loanpricing.domain.vo.LoanPricingWorkflowVO;
import com.ruoyi.loanpricing.mapper.LoanPricingWorkflowMapper;
import com.ruoyi.loanpricing.mapper.ModelCorpOutputFieldsMapper;
import com.ruoyi.loanpricing.mapper.ModelRetailOutputFieldsMapper;
import com.ruoyi.loanpricing.service.LoanPricingSensitiveDisplayService;
import com.ruoyi.loanpricing.service.LoanPricingModelService;
import com.ruoyi.loanpricing.service.SensitiveFieldCryptoService;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Collections;
import java.util.Objects;

@ExtendWith(MockitoExtension.class)
class LoanPricingWorkflowServiceImplTest
{
    @Mock
    private LoanPricingWorkflowMapper loanPricingWorkflowMapper;

    @Mock
    private LoanPricingModelService loanPricingModelService;

    @Mock
    private ModelRetailOutputFieldsMapper modelRetailOutputFieldsMapper;

    @Mock
    private ModelCorpOutputFieldsMapper modelCorpOutputFieldsMapper;

    @Mock
    private SensitiveFieldCryptoService sensitiveFieldCryptoService;

    @Mock
    private LoanPricingSensitiveDisplayService loanPricingSensitiveDisplayService;

    @InjectMocks
    private LoanPricingWorkflowServiceImpl loanPricingWorkflowService;

    @Test
    void shouldEncryptCustNameAndIdNumBeforeInsert()
    {
        LoanPricingWorkflow workflow = new LoanPricingWorkflow();
        workflow.setCustName("张三");
        workflow.setIdNum("110101199001011234");
        workflow.setCustIsn("CUST001");

        when(sensitiveFieldCryptoService.encrypt("张三")).thenReturn("cipher-name");
        when(sensitiveFieldCryptoService.encrypt("110101199001011234")).thenReturn("cipher-id");

        loanPricingWorkflowService.createLoanPricing(workflow);

        verify(loanPricingWorkflowMapper).insert(argThat((LoanPricingWorkflow entity) ->
                Objects.equals("cipher-name", entity.getCustName())
                        && Objects.equals("cipher-id", entity.getIdNum())
                        && Objects.equals("CUST001", entity.getCustIsn())));
    }

    @Test
    void shouldReturnPagedWorkflowListWithCalculateRate()
    {
        LoanPricingWorkflowListVO row = new LoanPricingWorkflowListVO();
        row.setCalculateRate("6.15");

        Page<LoanPricingWorkflowListVO> pageResult = new Page<>(1, 10);
        pageResult.setRecords(java.util.List.of(row));

        when(loanPricingWorkflowMapper.selectWorkflowPageWithRates(any(), any())).thenReturn(pageResult);

        IPage<LoanPricingWorkflowListVO> result = loanPricingWorkflowService.selectLoanPricingPage(new Page<>(1, 10), new LoanPricingWorkflow());

        assertEquals("6.15", result.getRecords().get(0).getCalculateRate());
    }

    @Test
    void shouldMaskCustNameWhenReturningPagedWorkflowList()
    {
        LoanPricingWorkflowListVO row = new LoanPricingWorkflowListVO();
        row.setCustName("cipher-name");

        Page<LoanPricingWorkflowListVO> pageResult = new Page<>(1, 10);
        pageResult.setRecords(Collections.singletonList(row));

        when(loanPricingWorkflowMapper.selectWorkflowPageWithRates(any(), any())).thenReturn(pageResult);
        when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("张三");
        when(loanPricingSensitiveDisplayService.maskCustName("张三")).thenReturn("张*");

        IPage<LoanPricingWorkflowListVO> result = loanPricingWorkflowService.selectLoanPricingPage(new Page<>(1, 10), new LoanPricingWorkflow());

        assertEquals("张*", result.getRecords().get(0).getCustName());
    }

    @Test
    void shouldUseCustIsnInsteadOfCustNameAsQueryCondition()
    {
        LoanPricingWorkflow query = new LoanPricingWorkflow();
        query.setCustIsn("CUST001");
        query.setCustName("张三");

        when(loanPricingWorkflowMapper.selectList(any())).thenReturn(Collections.emptyList());

        loanPricingWorkflowService.selectLoanPricingList(query);

        ArgumentCaptor<LambdaQueryWrapper<LoanPricingWorkflow>> wrapperCaptor = ArgumentCaptor.forClass(LambdaQueryWrapper.class);
        verify(loanPricingWorkflowMapper).selectList(wrapperCaptor.capture());

        LambdaQueryWrapper<LoanPricingWorkflow> wrapper = wrapperCaptor.getValue();
        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), LoanPricingWorkflow.class);
        String sqlSegment = wrapper.getSqlSegment();

        assertTrue(sqlSegment.contains("cust_isn"), sqlSegment);
        assertTrue(!sqlSegment.contains("cust_name"), sqlSegment);
    }

    @Test
    void shouldUseRetailModelOutputFinalCalculateRateForWorkflowDetail()
    {
        LoanPricingWorkflow workflow = new LoanPricingWorkflow();
        workflow.setSerialNum("P20260328001");
        workflow.setCustType("个人");
        workflow.setModelOutputId(11L);
        workflow.setLoanRate("4.35");

        ModelRetailOutputFields retailOutputFields = new ModelRetailOutputFields();
        retailOutputFields.setCalculateRate("6.15");
        retailOutputFields.setFinalCalculateRate("6.05");

        when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
        when(modelRetailOutputFieldsMapper.selectById(11L)).thenReturn(retailOutputFields);

        LoanPricingWorkflowVO result = loanPricingWorkflowService.selectLoanPricingBySerialNum("P20260328001");

        assertEquals("6.05", result.getLoanPricingWorkflow().getLoanRate());
        assertEquals("6.15", result.getModelRetailOutputFields().getCalculateRate());
        assertEquals("6.05", result.getModelRetailOutputFields().getFinalCalculateRate());
    }

    @Test
    void shouldMaskCustNameAndIdNumWhenReturningWorkflowDetail()
    {
        LoanPricingWorkflow workflow = new LoanPricingWorkflow();
        workflow.setSerialNum("P20260328001");
        workflow.setCustType("个人");
        workflow.setCustName("cipher-name");
        workflow.setIdNum("cipher-id");

        when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
        when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("张三");
        when(sensitiveFieldCryptoService.decrypt("cipher-id")).thenReturn("110101199001011234");
        when(loanPricingSensitiveDisplayService.maskCustName("张三")).thenReturn("张*");
        when(loanPricingSensitiveDisplayService.maskIdNum("110101199001011234")).thenReturn("1101********1234");

        LoanPricingWorkflowVO result = loanPricingWorkflowService.selectLoanPricingBySerialNum("P20260328001");

        assertEquals("张*", result.getLoanPricingWorkflow().getCustName());
        assertEquals("1101********1234", result.getLoanPricingWorkflow().getIdNum());
    }

    @Test
    void shouldMaskCustNameAndIdNumInRetailModelOutputBasicInfo()
    {
        LoanPricingWorkflow workflow = new LoanPricingWorkflow();
        workflow.setSerialNum("P20260328001");
        workflow.setCustType("个人");
        workflow.setCustName("cipher-name");
        workflow.setIdNum("cipher-id");
        workflow.setModelOutputId(11L);

        ModelRetailOutputFields retailOutputFields = new ModelRetailOutputFields();
        retailOutputFields.setCustName("张三");
        retailOutputFields.setIdNum("110101199001011234");
        retailOutputFields.setCalculateRate("6.15");

        when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
        when(modelRetailOutputFieldsMapper.selectById(11L)).thenReturn(retailOutputFields);
        when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("张三");
        when(sensitiveFieldCryptoService.decrypt("cipher-id")).thenReturn("110101199001011234");
        when(loanPricingSensitiveDisplayService.maskCustName("张三")).thenReturn("张*");
        when(loanPricingSensitiveDisplayService.maskIdNum("110101199001011234")).thenReturn("1101********1234");

        LoanPricingWorkflowVO result = loanPricingWorkflowService.selectLoanPricingBySerialNum("P20260328001");

        assertEquals("张*", result.getModelRetailOutputFields().getCustName());
        assertEquals("1101********1234", result.getModelRetailOutputFields().getIdNum());
    }

    @Test
    void shouldUseCorporateModelOutputCalculateRateForWorkflowDetail()
    {
        LoanPricingWorkflow workflow = new LoanPricingWorkflow();
        workflow.setSerialNum("C20260328001");
        workflow.setCustType("企业");
        workflow.setModelOutputId(22L);
        workflow.setLoanRate("3.80");

        ModelCorpOutputFields corpOutputFields = new ModelCorpOutputFields();
        corpOutputFields.setCalculateRate("3.932");
        corpOutputFields.setFinalCalculateRate("3.652");

        when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
        when(modelCorpOutputFieldsMapper.selectById(22L)).thenReturn(corpOutputFields);

        LoanPricingWorkflowVO result = loanPricingWorkflowService.selectLoanPricingBySerialNum("C20260328001");

        assertEquals("3.652", result.getLoanPricingWorkflow().getLoanRate());
        assertEquals("3.932", result.getModelCorpOutputFields().getCalculateRate());
        assertEquals("3.652", result.getModelCorpOutputFields().getFinalCalculateRate());
    }

    @Test
    void shouldMaskCustNameAndIdNumInCorporateModelOutputBasicInfo()
    {
        LoanPricingWorkflow workflow = new LoanPricingWorkflow();
        workflow.setSerialNum("C20260328001");
        workflow.setCustType("企业");
        workflow.setCustName("cipher-name");
        workflow.setIdNum("cipher-id");
        workflow.setModelOutputId(22L);

        ModelCorpOutputFields corpOutputFields = new ModelCorpOutputFields();
        corpOutputFields.setCustName("测试科技有限公司");
        corpOutputFields.setIdNum("91110000100000000X");
        corpOutputFields.setCalculateRate("3.932");

        when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
        when(modelCorpOutputFieldsMapper.selectById(22L)).thenReturn(corpOutputFields);
        when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("测试科技有限公司");
        when(sensitiveFieldCryptoService.decrypt("cipher-id")).thenReturn("91110000100000000X");
        when(loanPricingSensitiveDisplayService.maskCustName("测试科技有限公司")).thenReturn("测试****公司");
        when(loanPricingSensitiveDisplayService.maskIdNum("91110000100000000X")).thenReturn("91*************00X");

        LoanPricingWorkflowVO result = loanPricingWorkflowService.selectLoanPricingBySerialNum("C20260328001");

        assertEquals("测试****公司", result.getModelCorpOutputFields().getCustName());
        assertEquals("91*************00X", result.getModelCorpOutputFields().getIdNum());
    }
}
