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.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.eq;
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 java.math.BigDecimal;

import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.trading.domain.BizSupplier;
import com.ruoyi.web.modules.trading.mapper.BizSupplierMapper;
import com.ruoyi.web.modules.trading.support.SupplierCodeGenerator;
import com.ruoyi.web.modules.trading.support.SupplierReferenceChecker;
import com.ruoyi.web.modules.trading.support.SupplierRules;
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("BizSupplierServiceImpl")
class BizSupplierServiceImplTest
{
    private static final String VALID_ID_CARD = "11010519491231002X";

    private static final String VALID_PHONE = "13800138000";

    @Mock
    private BizSupplierMapper mapper;

    @Mock
    private SupplierCodeGenerator codeGenerator;

    @Mock
    private SupplierReferenceChecker referenceChecker;

    @Mock
    private TradePartnerAccountAssigner accountAssigner;

    @InjectMocks
    private BizSupplierServiceImpl service;

    @Test
    @DisplayName("新增分配编号")
    void insertAllocatesCode()
    {
        when(mapper.countBizSupplierByNameExcludeId(any(), isNull())).thenReturn(0);
        when(mapper.countBizSupplierByIdentityNoExcludeId(any(), isNull())).thenReturn(0);
        when(codeGenerator.allocateNextCode()).thenReturn("03");
        when(mapper.insertBizSupplier(any())).thenReturn(1);
        BizSupplier row = sampleRow();
        row.setFeeMethod(SupplierRules.FEE_METHOD_PER_HEAD);
        row.setFeeStandard(new BigDecimal("12"));
        assertEquals(1, service.insertBizSupplier(row));
        verify(mapper).insertBizSupplier(argThat(r -> "03".equals(r.getSupplierCode())));
    }

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

    @Test
    @DisplayName("修改保留账号分配字段")
    void updatePreservesAccountFields()
    {
        BizSupplier db = sampleRow();
        db.setId(3L);
        db.setSupplierCode("03");
        db.setAccountAssigned(TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES);
        db.setSysUserId(200L);
        db.setAssignedLoginName("gys3");
        when(mapper.selectBizSupplierById(3L)).thenReturn(db);
        when(mapper.countBizSupplierByNameExcludeId(any(), eq(3L))).thenReturn(0);
        when(mapper.countBizSupplierByIdentityNoExcludeId(any(), eq(3L))).thenReturn(0);
        when(mapper.updateBizSupplier(any())).thenReturn(1);

        BizSupplier in = sampleRow();
        in.setId(3L);
        in.setFeeMethod(SupplierRules.FEE_METHOD_PER_ACCOUNT);
        assertEquals(1, service.updateBizSupplier(in));

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

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

    @Test
    @DisplayName("存在未结算结算单不可删除")
    void deleteBlocked()
    {
        BizSupplier db = sampleRow();
        db.setId(5L);
        when(mapper.selectBizSupplierByIdAny(5L)).thenReturn(db);
        when(referenceChecker.hasUnsettledSettlement(5L)).thenReturn(true);
        ServiceException ex = assertThrows(ServiceException.class,
                () -> service.deleteBizSupplierByIds(new Long[] { 5L }, "u"));
        assertTrue(ex.getMessage().contains("未结算"));
        verify(mapper, never()).logicDeleteBizSupplierByIds(any(), any());
    }

    @Test
    @DisplayName("分配账号")
    void assignAccount()
    {
        BizSupplier db = sampleRow();
        db.setId(1L);
        when(mapper.selectBizSupplierById(1L)).thenReturn(db);
        SysUser user = new SysUser();
        user.setUserId(100L);
        user.setUserName("zxgys1");
        when(accountAssigner.createAccount(eq("扎西供应商"), eq(VALID_PHONE), eq(1L),
                eq(TradePartnerAccountRules.ROLE_KEY_SUPPLIER), eq("admin"))).thenReturn(user);
        when(mapper.updateBizSupplier(any())).thenReturn(1);
        assertEquals(1, service.assignAccount(1L, "admin"));
        verify(mapper).updateBizSupplier(argThat(r ->
                TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES == r.getAccountAssigned()
                        && Long.valueOf(100L).equals(r.getSysUserId())
                        && "zxgys1".equals(r.getAssignedLoginName())));
    }

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

    @Test
    @DisplayName("删除已分配联动删账号")
    void deleteRemovesLinkedAccount()
    {
        BizSupplier db = sampleRow();
        db.setId(6L);
        db.setAccountAssigned(TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES);
        db.setSysUserId(88L);
        when(mapper.selectBizSupplierByIdAny(6L)).thenReturn(db);
        when(referenceChecker.hasUnsettledSettlement(6L)).thenReturn(false);
        doNothing().when(accountAssigner).deleteLinkedAccount(
                TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES, 88L);
        when(mapper.logicDeleteBizSupplierByIds(any(), eq("u"))).thenReturn(1);
        assertEquals(1, service.deleteBizSupplierByIds(new Long[] { 6L }, "u"));
        verify(accountAssigner).deleteLinkedAccount(
                TradePartnerAccountRules.ACCOUNT_ASSIGNED_YES, 88L);
    }

    @Test
    @DisplayName("详情填充字典名")
    void detailEnrich()
    {
        BizSupplier db = sampleRow();
        db.setId(1L);
        db.setSupplierCode("01");
        db.setVehiclePlatesJson("[\"藏A12345\"]");
        when(mapper.selectBizSupplierById(1L)).thenReturn(db);
        BizSupplier got = service.selectBizSupplierById(1L);
        assertEquals("个体户", got.getSupplierTypeName());
        assertEquals("按户计费", got.getFeeMethodName());
        assertEquals(1, got.getVehiclePlates().size());
    }

    private static BizSupplier sampleRow()
    {
        BizSupplier r = new BizSupplier();
        r.setSupplierName("扎西供应商");
        r.setSupplierType(SupplierRules.TYPE_INDIVIDUAL);
        r.setIdentityNo(VALID_ID_CARD);
        r.setContactPhone(VALID_PHONE);
        r.setFeeMethod(SupplierRules.FEE_METHOD_PER_ACCOUNT);
        return r;
    }
}
