package com.ruoyi.web.modules.finance.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
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 com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.finance.constant.FinanceConstants;
import com.ruoyi.web.modules.finance.domain.BizShopPayAccount;
import com.ruoyi.web.modules.finance.domain.BizShopWallet;
import com.ruoyi.web.modules.finance.domain.BizShopWithdraw;
import com.ruoyi.web.modules.finance.dto.SellerWithdrawSubmitDTO;
import com.ruoyi.web.modules.finance.dto.WithdrawAuditDTO;
import com.ruoyi.web.modules.finance.mapper.BizShopFundLogMapper;
import com.ruoyi.web.modules.finance.mapper.BizShopPayAccountMapper;
import com.ruoyi.web.modules.finance.mapper.BizShopWalletMapper;
import com.ruoyi.web.modules.finance.mapper.BizShopWithdrawMapper;
import com.ruoyi.web.modules.finance.service.impl.ShopFundServiceImpl;
import com.ruoyi.web.modules.finance.vo.FundLogListRowVO;
import com.ruoyi.web.modules.finance.vo.PlatformFundOverviewSummaryVO;
import com.ruoyi.web.modules.finance.vo.PlatformShopFundRankRowVO;
import com.ruoyi.web.modules.finance.dto.PlatformFundLogQuery;
import com.ruoyi.web.modules.finance.dto.PlatformShopFundRankQuery;
import com.ruoyi.web.modules.finance.dto.PlatformWithdrawSummaryDetailQuery;
import com.ruoyi.web.modules.finance.vo.PlatformWithdrawSummaryDetailRowVO;
import com.ruoyi.web.modules.finance.vo.PlatformWithdrawSummaryRowVO;
import com.ruoyi.web.modules.finance.support.FinanceSupport;
import com.ruoyi.web.modules.order.mapper.BizOrderMapper;
import com.ruoyi.web.modules.store.domain.BizShop;
import com.ruoyi.web.modules.store.mapper.BizShopMapper;

@ExtendWith(MockitoExtension.class)
class ShopFundServiceImplTest
{
    private static final Long SHOP_ID = 101L;

    @Mock
    private BizShopWalletMapper walletMapper;

    @Mock
    private BizShopFundLogMapper fundLogMapper;

    @Mock
    private BizShopWithdrawMapper withdrawMapper;

    @Mock
    private BizShopPayAccountMapper payAccountMapper;

    @Mock
    private BizOrderMapper orderMapper;

    @Mock
    private BizShopMapper shopMapper;

    @Mock
    private FinanceSupport financeSupport;

    @InjectMocks
    private ShopFundServiceImpl shopFundService;

    @Test
    void submitWithdraw_exceedBalance_throws()
    {
        SellerWithdrawSubmitDTO dto = new SellerWithdrawSubmitDTO();
        dto.setAccountId(1L);
        dto.setWithdrawAmount(new BigDecimal("100.00"));

        BizShopPayAccount account = new BizShopPayAccount();
        account.setAccountId(1L);
        account.setAccountType(FinanceConstants.ACCOUNT_TYPE_BANK);
        account.setRealName("张三");
        account.setAccountNo("6222021234567890");
        when(payAccountMapper.selectByIdAndShop(1L, SHOP_ID)).thenReturn(account);

        BizShopWallet wallet = new BizShopWallet();
        wallet.setShopId(SHOP_ID);
        wallet.setAvailableBalance(new BigDecimal("50.00"));
        wallet.setFrozenAmount(BigDecimal.ZERO);
        when(walletMapper.selectByShopIdForUpdate(SHOP_ID)).thenReturn(wallet);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> shopFundService.submitWithdraw(SHOP_ID, dto, "seller"));
        assertEquals(FinanceConstants.MSG_AMOUNT_EXCEED, ex.getMessage());
        verify(withdrawMapper, never()).insert(any());
    }

    @Test
    void submitWithdraw_success()
    {
        SellerWithdrawSubmitDTO dto = new SellerWithdrawSubmitDTO();
        dto.setAccountId(1L);
        dto.setWithdrawAmount(new BigDecimal("100.00"));

        BizShopPayAccount account = new BizShopPayAccount();
        account.setAccountId(1L);
        account.setAccountType(FinanceConstants.ACCOUNT_TYPE_BANK);
        account.setRealName("张三");
        account.setAccountNo("6222021234567890");
        when(payAccountMapper.selectByIdAndShop(1L, SHOP_ID)).thenReturn(account);

        BizShopWallet wallet = new BizShopWallet();
        wallet.setShopId(SHOP_ID);
        wallet.setAvailableBalance(new BigDecimal("200.00"));
        wallet.setFrozenAmount(BigDecimal.ZERO);
        when(walletMapper.selectByShopIdForUpdate(SHOP_ID)).thenReturn(wallet);
        when(financeSupport.nextWithdrawNo()).thenReturn("WD20260526000001");
        when(financeSupport.maskAccountNo(FinanceConstants.ACCOUNT_TYPE_BANK, "6222021234567890"))
                .thenReturn("**** **** **** 7890");

        BizShop shop = new BizShop();
        shop.setShopName("测试店铺");
        when(shopMapper.selectById(SHOP_ID)).thenReturn(shop);
        when(withdrawMapper.insert(any(BizShopWithdraw.class))).thenAnswer(invocation -> {
            BizShopWithdraw w = invocation.getArgument(0);
            w.setWithdrawId(1001L);
            return 1;
        });

        assertEquals("WD20260526000001",
                shopFundService.submitWithdraw(SHOP_ID, dto, "seller").get("withdrawNo"));
        verify(walletMapper).updateBalance(eq(SHOP_ID), eq(new BigDecimal("100.00")), eq(new BigDecimal("100.00")),
                any(Date.class));
        verify(fundLogMapper).insert(any());
    }

    @Test
    void rejectWithdraw_notPending_throws()
    {
        BizShopWithdraw withdraw = new BizShopWithdraw();
        withdraw.setWithdrawId(1L);
        withdraw.setWithdrawStatus(FinanceConstants.WITHDRAW_STATUS_COMPLETED);
        when(withdrawMapper.selectById(1L)).thenReturn(withdraw);

        WithdrawAuditDTO dto = new WithdrawAuditDTO();
        dto.setWithdrawId(1L);
        dto.setProcessRemark("驳回原因");

        ServiceException ex = assertThrows(ServiceException.class,
                () -> shopFundService.rejectWithdraw(dto, "admin"));
        assertEquals(FinanceConstants.MSG_CANNOT_AUDIT, ex.getMessage());
    }

    @Test
    void listSellerWithdraws_enrichesText()
    {
        when(withdrawMapper.selectSellerList(eq(SHOP_ID), any())).thenReturn(Collections.emptyList());
        assertEquals(0, shopFundService.listSellerWithdraws(SHOP_ID, null).size());
    }

    @Test
    void getPlatformOverviewSummary_returnsTotals()
    {
        when(walletMapper.sumAvailableBalance()).thenReturn(new BigDecimal("1000.50"));
        when(walletMapper.sumFrozenAmount()).thenReturn(new BigDecimal("200.00"));
        when(orderMapper.sumPlatformPendingSettle()).thenReturn(new BigDecimal("300.25"));

        PlatformFundOverviewSummaryVO summary = shopFundService.getPlatformOverviewSummary();
        assertEquals(new BigDecimal("1000.50"), summary.getTotalBalance());
        assertEquals(new BigDecimal("200.00"), summary.getFrozenTotal());
        assertEquals(new BigDecimal("300.25"), summary.getPendingSettleTotal());
    }

    @Test
    void listPlatformShopRanks_enrichesStatus()
    {
        PlatformShopFundRankRowVO row = new PlatformShopFundRankRowVO();
        row.setShopStatus("0");
        row.setDelFlag("0");
        row.setAvailableBalance(new BigDecimal("10"));
        row.setFrozenAmount(new BigDecimal("1"));
        row.setPendingSettleAmount(new BigDecimal("2"));
        when(walletMapper.selectPlatformShopRankList(any())).thenReturn(Collections.singletonList(row));
        when(financeSupport.toShopStatusText("0", "0")).thenReturn("开业");

        java.util.List<PlatformShopFundRankRowVO> rows = shopFundService.listPlatformShopRanks(new PlatformShopFundRankQuery());
        assertEquals(1, rows.size());
        assertEquals("开业", rows.get(0).getShopStatusText());
        assertEquals(Boolean.FALSE, rows.get(0).getDeleted());
    }

    @Test
    void listPlatformFundLogs_enrichesReasonText()
    {
        FundLogListRowVO row = new FundLogListRowVO();
        row.setChangeReason(FinanceConstants.REASON_ORDER_SETTLE);
        when(fundLogMapper.selectPlatformList(any())).thenReturn(Collections.singletonList(row));
        when(financeSupport.toChangeReasonText(FinanceConstants.REASON_ORDER_SETTLE)).thenReturn("订单收支");

        java.util.List<FundLogListRowVO> logRows = shopFundService.listPlatformFundLogs(new PlatformFundLogQuery());
        assertEquals("订单收支", logRows.get(0).getChangeReasonText());
    }

    @Test
    void listPlatformWithdrawSummary_enrichesStatus()
    {
        PlatformWithdrawSummaryRowVO row = new PlatformWithdrawSummaryRowVO();
        row.setShopStatus("0");
        row.setDelFlag("0");
        row.setWithdrawnAmount(new BigDecimal("100"));
        row.setPendingAuditAmount(new BigDecimal("50"));
        when(withdrawMapper.selectPlatformSummaryList(any())).thenReturn(Collections.singletonList(row));
        when(financeSupport.toShopStatusText("0", "0")).thenReturn("开业");

        java.util.List<PlatformWithdrawSummaryRowVO> rows = shopFundService
                .listPlatformWithdrawSummary(new com.ruoyi.web.modules.finance.dto.PlatformWithdrawSummaryQuery());
        assertEquals(1, rows.size());
        assertEquals("开业", rows.get(0).getShopStatusText());
        assertEquals(new BigDecimal("100.00"), rows.get(0).getWithdrawnAmount());
    }

    @Test
    void listPlatformWithdrawSummaryDetails_requiresShopId()
    {
        ServiceException ex = assertThrows(ServiceException.class,
                () -> shopFundService.listPlatformWithdrawSummaryDetails(new PlatformWithdrawSummaryDetailQuery()));
        assertEquals(FinanceConstants.MSG_SHOP_ID_REQUIRED, ex.getMessage());
    }

    @Test
    void listPlatformWithdrawSummaryDetails_enrichesAccount()
    {
        PlatformWithdrawSummaryDetailQuery query = new PlatformWithdrawSummaryDetailQuery();
        query.setShopId(SHOP_ID);
        PlatformWithdrawSummaryDetailRowVO row = new PlatformWithdrawSummaryDetailRowVO();
        row.setAccountType(FinanceConstants.ACCOUNT_TYPE_BANK);
        row.setAccountRealName("张三");
        row.setAccountNoMask("****7890");
        row.setWithdrawStatus(FinanceConstants.WITHDRAW_STATUS_PENDING);
        row.setWithdrawAmount(new BigDecimal("88"));
        when(withdrawMapper.selectSummaryDetailList(eq(SHOP_ID), any())).thenReturn(Collections.singletonList(row));
        when(financeSupport.toAccountTypeText(FinanceConstants.ACCOUNT_TYPE_BANK)).thenReturn("银行卡");
        when(financeSupport.toWithdrawStatusText(FinanceConstants.WITHDRAW_STATUS_PENDING)).thenReturn("待审核");
        when(financeSupport.buildAccountSummary(FinanceConstants.ACCOUNT_TYPE_BANK, "张三", "****7890"))
                .thenReturn("银行卡 ****7890 张三");

        java.util.List<PlatformWithdrawSummaryDetailRowVO> rows = shopFundService
                .listPlatformWithdrawSummaryDetails(query);
        assertEquals("待审核", rows.get(0).getWithdrawStatusText());
        assertEquals("银行卡 ****7890 张三", rows.get(0).getAccountSummary());
    }

    @Test
    void seedContactBankAccountFromEntry_insertsBankCard()
    {
        when(payAccountMapper.countByShopIdAndAccountNo(SHOP_ID, "6222021234567890")).thenReturn(0);

        shopFundService.seedContactBankAccountFromEntry(SHOP_ID, "张三", "6222021234567890", "system");

        verify(payAccountMapper).insert(any(BizShopPayAccount.class));
    }

    @Test
    void seedContactBankAccountFromEntry_skipsDuplicate()
    {
        when(payAccountMapper.countByShopIdAndAccountNo(SHOP_ID, "6222021234567890")).thenReturn(1);

        shopFundService.seedContactBankAccountFromEntry(SHOP_ID, "张三", "6222021234567890", "system");

        verify(payAccountMapper, never()).insert(any());
    }

    @Test
    void seedContactBankAccountFromEntry_skipsBlank()
    {
        shopFundService.seedContactBankAccountFromEntry(SHOP_ID, "", "6222021234567890", "system");
        verify(payAccountMapper, never()).insert(any());
    }
}
