package com.ruoyi.web.modules.trading.service.impl;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.trading.domain.BizDistributor;
import com.ruoyi.web.modules.trading.mapper.BizDistributorMapper;
import com.ruoyi.web.modules.trading.support.DistributorCodeGenerator;
import com.ruoyi.web.modules.trading.support.DistributorReferenceChecker;
import com.ruoyi.web.modules.trading.support.DistributorRules;
import com.ruoyi.web.modules.trading.support.TradePartnerAccountAssigner;
import com.ruoyi.web.modules.trading.support.TradePartnerAccountRules;
import org.junit.jupiter.api.DisplayName;
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;

@ExtendWith(MockitoExtension.class)
@DisplayName("BizDistributorServiceImpl")
class BizDistributorServiceImplTest
{
    private static final String VALID_ID_CARD = "11010519491231002X";

    private static final String VALID_PHONE = "13800138000";

    @Mock
    private BizDistributorMapper mapper;

    @Mock
    private DistributorCodeGenerator codeGenerator;

    @Mock
    private DistributorReferenceChecker referenceChecker;

    @Mock
    private TradePartnerAccountAssigner accountAssigner;

    @InjectMocks
    private BizDistributorServiceImpl service;

    @Test
    @DisplayName("新增分配编号")
    void insertAllocatesCode()
    {
        when(mapper.countBizDistributorByNameExcludeId(any(), isNull())).thenReturn(0);
        when(mapper.countBizDistributorByIdentityNoExcludeId(any(), isNull())).thenReturn(0);
        when(codeGenerator.allocateNextCode()).thenReturn("05");
        when(mapper.insertBizDistributor(any())).thenReturn(1);
        assertEquals(1, service.insertBizDistributor(sampleRow()));
        verify(mapper).insertBizDistributor(argThat(r -> "05".equals(r.getDistributorCode())));
    }

    @Test
    @DisplayName("名称重复")
    void duplicateName()
    {
        when(mapper.countBizDistributorByNameExcludeId(any(), isNull())).thenReturn(1);
        assertThrows(ServiceException.class, () -> service.insertBizDistributor(sampleRow()));
        verify(mapper, never()).insertBizDistributor(any());
    }

    @Test
    @DisplayName("修改保留账号分配字段")
    void updatePreservesAccountFields()
    {
        BizDistributor db = sampleRow();
        db.setId(3L);
        db.setDistributorCode("03");
        db.setAccountAssigned(TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES);
        db.setSysUserId(200L);
        db.setAssignedLoginName("cxs3");
        when(mapper.selectBizDistributorById(3L)).thenReturn(db);
        when(mapper.countBizDistributorByNameExcludeId(any(), eq(3L))).thenReturn(0);
        when(mapper.countBizDistributorByIdentityNoExcludeId(any(), eq(3L))).thenReturn(0);
        when(mapper.updateBizDistributor(any())).thenReturn(1);

        BizDistributor in = sampleRow();
        in.setId(3L);
        assertEquals(1, service.updateBizDistributor(in));

        verify(mapper).updateBizDistributor(argThat(r ->
                TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES == r.getAccountAssigned()
                        && Long.valueOf(200L).equals(r.getSysUserId())
                        && "cxs3".equals(r.getAssignedLoginName())));
    }

    @Test
    @DisplayName("修改禁止改编号")
    void updateCodeImmutable()
    {
        BizDistributor db = sampleRow();
        db.setId(2L);
        db.setDistributorCode("02");
        when(mapper.selectBizDistributorById(2L)).thenReturn(db);
        BizDistributor in = sampleRow();
        in.setId(2L);
        in.setDistributorCode("99");
        assertThrows(ServiceException.class, () -> service.updateBizDistributor(in));
    }

    @Test
    @DisplayName("分配账号")
    void assignAccount()
    {
        BizDistributor db = sampleRow();
        db.setId(3L);
        when(mapper.selectBizDistributorById(3L)).thenReturn(db);
        SysUser user = new SysUser();
        user.setUserId(200L);
        user.setUserName("zxcxs3");
        when(accountAssigner.createAccount(eq("扎西承销商"), eq(VALID_PHONE), eq(3L),
                eq(TradePartnerAccountRules.ROLE_KEY_DISTRIBUTOR), eq("admin"))).thenReturn(user);
        when(mapper.updateBizDistributor(any())).thenReturn(1);
        assertEquals(1, service.assignAccount(3L, "admin"));
        verify(mapper).updateBizDistributor(argThat(r ->
                TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES == r.getAccountAssigned()
                        && Long.valueOf(200L).equals(r.getSysUserId())));
    }

    @Test
    @DisplayName("重复分配拒绝")
    void assignAccountTwice()
    {
        BizDistributor db = sampleRow();
        db.setId(4L);
        db.setAccountAssigned(TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES);
        when(mapper.selectBizDistributorById(4L)).thenReturn(db);
        assertThrows(ServiceException.class, () -> service.assignAccount(4L, "admin"));
        verify(accountAssigner, never()).createAccount(any(), any(), any(), any(), any());
    }

    @Test
    @DisplayName("存在未支付订单不可删除")
    void deleteBlockedByUnpaidOrder()
    {
        BizDistributor db = sampleRow();
        db.setId(5L);
        when(mapper.selectBizDistributorByIdAny(5L)).thenReturn(db);
        when(referenceChecker.hasUnpaidOrder(5L)).thenReturn(true);
        ServiceException ex = assertThrows(ServiceException.class,
                () -> service.deleteBizDistributorByIds(new Long[] {5L}, "admin"));
        assertTrue(ex.getMessage().contains("未支付"));
        verify(mapper, never()).logicDeleteBizDistributorByIds(any(), any());
    }

    @Test
    @DisplayName("无未支付订单可逻辑删除")
    void deleteSuccessWhenNoUnpaidOrder()
    {
        BizDistributor db = sampleRow();
        db.setId(6L);
        when(mapper.selectBizDistributorByIdAny(6L)).thenReturn(db);
        when(referenceChecker.hasUnpaidOrder(6L)).thenReturn(false);
        doNothing().when(accountAssigner).deleteLinkedAccount(any(), any());
        when(mapper.logicDeleteBizDistributorByIds(any(), eq("admin"))).thenReturn(1);
        assertEquals(1, service.deleteBizDistributorByIds(new Long[] {6L}, "admin"));
    }

    @Test
    @DisplayName("详情填充字典名")
    void detailEnrich()
    {
        BizDistributor db = sampleRow();
        db.setId(1L);
        db.setDistributorCode("01");
        db.setSalesDestination(DistributorRules.SALES_DESTINATION_LOCAL_PROVINCE);
        when(mapper.selectBizDistributorById(1L)).thenReturn(db);
        BizDistributor got = service.selectBizDistributorById(1L);
        assertEquals("个体户", got.getDistributorTypeName());
        assertEquals("本省", got.getSalesDestinationName());
    }

    private static BizDistributor sampleRow()
    {
        BizDistributor r = new BizDistributor();
        r.setDistributorName("扎西承销商");
        r.setDistributorType(DistributorRules.TYPE_INDIVIDUAL);
        r.setIdentityNo(VALID_ID_CARD);
        r.setContactPhone(VALID_PHONE);
        r.setSalesDestination(DistributorRules.SALES_DESTINATION_LOCAL_CITY);
        return r;
    }
}
